@vibetasks/cli 0.6.9 → 0.6.11

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 +1653 -153
  2. package/package.json +2 -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 Command30 } 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";
@@ -416,6 +416,7 @@ import { writeFile, readFile, mkdir } from "fs/promises";
416
416
  import { join } from "path";
417
417
  import { homedir } from "os";
418
418
  import { randomUUID } from "crypto";
419
+ import { AuthManager as AuthManager2 } from "@vibetasks/core";
419
420
  var ADJECTIVES = [
420
421
  "swift",
421
422
  "calm",
@@ -455,8 +456,102 @@ 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"
@@ -510,6 +605,8 @@ var SessionManager = class {
510
605
  store.sessions.push(session);
511
606
  store.currentSession = session.id;
512
607
  await this.save();
608
+ await this.syncToServer(session).catch(() => {
609
+ });
513
610
  return session;
514
611
  }
515
612
  /**
@@ -532,23 +629,22 @@ var SessionManager = class {
532
629
  }
533
630
  /**
534
631
  * Log an action in the current session
632
+ * Syncs to server in real-time (non-blocking)
535
633
  */
536
634
  async logAction(action) {
537
635
  const store = await this.load();
538
- const session = store.sessions.find((s) => s.id === store.currentSession);
636
+ let session = store.sessions.find((s) => s.id === store.currentSession);
637
+ const actionWithTimestamp = {
638
+ ...action,
639
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
640
+ };
539
641
  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
- });
642
+ session = await this.startSession();
550
643
  }
644
+ session.actions.push(actionWithTimestamp);
551
645
  await this.save();
646
+ this.syncActionToServer(session.id, actionWithTimestamp).catch(() => {
647
+ });
552
648
  }
553
649
  /**
554
650
  * End the current session
@@ -561,6 +657,8 @@ var SessionManager = class {
561
657
  session.summary = summary;
562
658
  store.currentSession = void 0;
563
659
  await this.save();
660
+ this.syncToServer(session).catch(() => {
661
+ });
564
662
  }
565
663
  return session || null;
566
664
  }
@@ -622,6 +720,58 @@ var SessionManager = class {
622
720
  }
623
721
  return lines.filter(Boolean).join("\n");
624
722
  }
723
+ /**
724
+ * Generate a comprehensive handoff for the next AI
725
+ * Includes: vibing tasks with context, git status, files changed, next steps
726
+ */
727
+ async generateHandoff() {
728
+ const { execSync: execSync2 } = await import("child_process");
729
+ const lines = [];
730
+ lines.push("# AI Handoff");
731
+ lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
732
+ lines.push("");
733
+ const currentSession = await this.getCurrentSession();
734
+ if (currentSession) {
735
+ lines.push(`## Current Session: ${currentSession.id}`);
736
+ lines.push(`Project: ${currentSession.project || "unknown"}`);
737
+ lines.push(`Started: ${new Date(currentSession.startedAt).toLocaleString()}`);
738
+ lines.push(`Actions: ${currentSession.actions.length}`);
739
+ lines.push("");
740
+ }
741
+ try {
742
+ const gitStatus = execSync2("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
743
+ if (gitStatus) {
744
+ const changedFiles = gitStatus.split("\n").slice(0, 20);
745
+ lines.push("## Uncommitted Changes");
746
+ changedFiles.forEach((f) => lines.push(`- ${f}`));
747
+ if (gitStatus.split("\n").length > 20) {
748
+ lines.push(`- ... and ${gitStatus.split("\n").length - 20} more files`);
749
+ }
750
+ lines.push("");
751
+ }
752
+ } catch {
753
+ }
754
+ try {
755
+ const recentCommits = execSync2("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
756
+ if (recentCommits) {
757
+ lines.push("## Recent Commits");
758
+ recentCommits.split("\n").forEach((c) => lines.push(`- ${c}`));
759
+ lines.push("");
760
+ }
761
+ } catch {
762
+ }
763
+ lines.push("---");
764
+ lines.push("## For Next AI");
765
+ lines.push("");
766
+ lines.push("1. Check vibing tasks: `vibetasks list --status vibing --short-ids`");
767
+ lines.push('2. Read context_notes on vibing tasks for "where I left off"');
768
+ lines.push("3. Check git status for uncommitted work");
769
+ lines.push("");
770
+ if (currentSession) {
771
+ lines.push(`To continue this session's work: "Continue from session ${currentSession.id}"`);
772
+ }
773
+ return lines.join("\n");
774
+ }
625
775
  };
626
776
  var sessionManager = null;
627
777
  function getSessionManager() {
@@ -631,11 +781,84 @@ function getSessionManager() {
631
781
  return sessionManager;
632
782
  }
633
783
 
784
+ // src/utils/short-ids.ts
785
+ import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
786
+ import { join as join2 } from "path";
787
+ import { homedir as homedir2 } from "os";
788
+ var ShortIdManager = class {
789
+ storePath;
790
+ maxIds = 99;
791
+ constructor() {
792
+ this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
793
+ }
794
+ /**
795
+ * Save mappings from a list of tasks with their short_ids
796
+ * Uses the stable short_id from the database
797
+ */
798
+ async saveMappings(tasks) {
799
+ const mappings = tasks.filter((t) => t.short_id).slice(0, this.maxIds).map((task, index) => ({
800
+ shortId: task.short_id,
801
+ // Use actual short_id from database
802
+ fullId: task.id,
803
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
804
+ }));
805
+ const store = {
806
+ mappings,
807
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
808
+ };
809
+ const dir = join2(homedir2(), ".vibetasks");
810
+ await mkdir2(dir, { recursive: true });
811
+ await writeFile2(this.storePath, JSON.stringify(store, null, 2));
812
+ }
813
+ /**
814
+ * Get the full UUID for a short ID
815
+ */
816
+ async getFullId(shortId) {
817
+ try {
818
+ const content = await readFile2(this.storePath, "utf-8");
819
+ const store = JSON.parse(content);
820
+ const mapping = store.mappings.find((m) => m.shortId === shortId);
821
+ return mapping?.fullId || null;
822
+ } catch {
823
+ return null;
824
+ }
825
+ }
826
+ /**
827
+ * Get the short ID for a full UUID (if it exists in current mappings)
828
+ */
829
+ async getShortId(fullId) {
830
+ try {
831
+ const content = await readFile2(this.storePath, "utf-8");
832
+ const store = JSON.parse(content);
833
+ const mapping = store.mappings.find((m) => m.fullId === fullId);
834
+ return mapping?.shortId || null;
835
+ } catch {
836
+ return null;
837
+ }
838
+ }
839
+ /**
840
+ * Resolve an ID that could be either short (1-99) or full UUID
841
+ */
842
+ async resolveId(idOrShortId) {
843
+ const shortId = parseInt(idOrShortId, 10);
844
+ if (!isNaN(shortId) && shortId >= 1 && shortId <= this.maxIds) {
845
+ const fullId = await this.getFullId(shortId);
846
+ if (fullId) {
847
+ return fullId;
848
+ }
849
+ throw new Error(
850
+ `Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
851
+ );
852
+ }
853
+ return idOrShortId;
854
+ }
855
+ };
856
+
634
857
  // 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) => {
858
+ var addCommand = new Command2("add").description("Add a new task").argument("<title>", "Task title").option("-n, --notes <notes>", "Task notes (markdown supported)").option("-d, --due <date>", 'Due date (YYYY-MM-DD, "today", "tomorrow", "+3d")').option("-p, --priority <level>", "Priority: low, medium, high", "none").option("-t, --tags <tags...>", "Tags (space-separated)").option("-s, --subtasks <subtasks...>", "Subtasks (space-separated, use quotes for multi-word)").option("--project <name>", "Override auto-detected project").option("-e, --energy <level>", "Energy required: low, medium, high").option("-a, --acceptance <criteria...>", 'Acceptance criteria - "DONE WHEN" conditions (use quotes for multi-word)').option("-f, --files <files...>", "Relevant files for this task").option("--non-goals <goals...>", "Explicit non-goals / out of scope items").action(async (title, options) => {
636
859
  const spinner = ora2("Creating task...").start();
637
860
  try {
638
- const authManager = new AuthManager2();
861
+ const authManager = new AuthManager3();
639
862
  const taskOps = await TaskOperations.fromAuthManager(authManager);
640
863
  let projectTag;
641
864
  if (options.project) {
@@ -675,14 +898,21 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
675
898
  title: subtaskTitle,
676
899
  done: false
677
900
  })) || [];
901
+ const acceptanceCriteria = options.acceptance?.map((text) => ({
902
+ id: randomUUID2(),
903
+ text,
904
+ done: false
905
+ })) || [];
906
+ const relevantFiles = options.files || [];
907
+ const nonGoals = options.nonGoals || [];
678
908
  let sessionId;
679
909
  let sessionHistory = [];
680
- if (createdBy === "ai") {
681
- const sessionManager2 = getSessionManager();
682
- const session = await sessionManager2.getOrCreateSession(projectTag);
683
- sessionId = session.id;
910
+ const sessionManager2 = getSessionManager();
911
+ const currentSession = await sessionManager2.getCurrentSession();
912
+ if (currentSession) {
913
+ sessionId = currentSession.id;
684
914
  sessionHistory = [{
685
- session_id: session.id,
915
+ session_id: currentSession.id,
686
916
  action: "created",
687
917
  at: (/* @__PURE__ */ new Date()).toISOString()
688
918
  }];
@@ -703,8 +933,12 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
703
933
  subtasks_json: subtasksJson,
704
934
  session_id: sessionId,
705
935
  // Current AI session
706
- session_history: sessionHistory
936
+ session_history: sessionHistory,
707
937
  // Full history of sessions
938
+ // New AI-assisted planning fields
939
+ acceptance_criteria: acceptanceCriteria.length > 0 ? acceptanceCriteria : void 0,
940
+ relevant_files: relevantFiles.length > 0 ? relevantFiles : void 0,
941
+ non_goals: nonGoals.length > 0 ? nonGoals : void 0
708
942
  });
709
943
  if (options.tags && options.tags.length > 0) {
710
944
  const tagIds = [];
@@ -715,8 +949,13 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
715
949
  await taskOps.linkTaskTags(task.id, tagIds);
716
950
  }
717
951
  spinner.succeed(chalk3.green("Task created!"));
718
- if (createdBy === "ai") {
719
- const sessionManager2 = getSessionManager();
952
+ try {
953
+ const shortIdManager = new ShortIdManager();
954
+ const allTasks = await taskOps.getTasks("all");
955
+ await shortIdManager.saveMappings(allTasks);
956
+ } catch (e) {
957
+ }
958
+ if (currentSession) {
720
959
  await sessionManager2.logAction({
721
960
  type: "task_created",
722
961
  description: `Created task: "${title}"`,
@@ -761,6 +1000,18 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
761
1000
  console.log(chalk3.gray(` ${i + 1}. ${st.title}`));
762
1001
  });
763
1002
  }
1003
+ if (acceptanceCriteria.length > 0) {
1004
+ console.log(chalk3.cyan(`Done when:`));
1005
+ acceptanceCriteria.forEach((ac, i) => {
1006
+ console.log(chalk3.gray(` - ${ac.text}`));
1007
+ });
1008
+ }
1009
+ if (relevantFiles.length > 0) {
1010
+ console.log(chalk3.yellow(`Files: ${relevantFiles.join(", ")}`));
1011
+ }
1012
+ if (nonGoals.length > 0) {
1013
+ console.log(chalk3.gray(`Not in scope: ${nonGoals.join(", ")}`));
1014
+ }
764
1015
  console.log();
765
1016
  process.exit(0);
766
1017
  } catch (error) {
@@ -784,82 +1035,7 @@ function isRunningInClaudeCode() {
784
1035
  import { Command as Command3 } from "commander";
785
1036
  import chalk4 from "chalk";
786
1037
  import Table from "cli-table3";
787
- import { AuthManager as AuthManager3, TaskOperations as TaskOperations2 } from "@vibetasks/core";
788
-
789
- // src/utils/short-ids.ts
790
- import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
791
- import { join as join2 } from "path";
792
- import { homedir as homedir2 } from "os";
793
- var ShortIdManager = class {
794
- storePath;
795
- maxIds = 99;
796
- constructor() {
797
- this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
798
- }
799
- /**
800
- * Save mappings from a list of tasks with their short_ids
801
- * Uses the stable short_id from the database
802
- */
803
- async saveMappings(tasks) {
804
- const mappings = tasks.filter((t) => t.short_id).slice(0, this.maxIds).map((task, index) => ({
805
- shortId: task.short_id,
806
- // Use actual short_id from database
807
- fullId: task.id,
808
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
809
- }));
810
- const store = {
811
- mappings,
812
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
813
- };
814
- const dir = join2(homedir2(), ".vibetasks");
815
- await mkdir2(dir, { recursive: true });
816
- await writeFile2(this.storePath, JSON.stringify(store, null, 2));
817
- }
818
- /**
819
- * Get the full UUID for a short ID
820
- */
821
- async getFullId(shortId) {
822
- try {
823
- const content = await readFile2(this.storePath, "utf-8");
824
- const store = JSON.parse(content);
825
- const mapping = store.mappings.find((m) => m.shortId === shortId);
826
- return mapping?.fullId || null;
827
- } catch {
828
- return null;
829
- }
830
- }
831
- /**
832
- * Get the short ID for a full UUID (if it exists in current mappings)
833
- */
834
- async getShortId(fullId) {
835
- try {
836
- const content = await readFile2(this.storePath, "utf-8");
837
- const store = JSON.parse(content);
838
- const mapping = store.mappings.find((m) => m.fullId === fullId);
839
- return mapping?.shortId || null;
840
- } catch {
841
- return null;
842
- }
843
- }
844
- /**
845
- * Resolve an ID that could be either short (1-99) or full UUID
846
- */
847
- async resolveId(idOrShortId) {
848
- const shortId = parseInt(idOrShortId, 10);
849
- if (!isNaN(shortId) && shortId >= 1 && shortId <= this.maxIds) {
850
- const fullId = await this.getFullId(shortId);
851
- if (fullId) {
852
- return fullId;
853
- }
854
- throw new Error(
855
- `Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
856
- );
857
- }
858
- return idOrShortId;
859
- }
860
- };
861
-
862
- // src/commands/list.ts
1038
+ import { AuthManager as AuthManager4, TaskOperations as TaskOperations2 } from "@vibetasks/core";
863
1039
  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) => {
864
1040
  try {
865
1041
  const validFilters = ["all", "today", "upcoming", "completed"];
@@ -868,7 +1044,7 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
868
1044
  console.error(chalk4.gray(`Valid filters: ${validFilters.join(", ")}`));
869
1045
  process.exit(1);
870
1046
  }
871
- const authManager = new AuthManager3();
1047
+ const authManager = new AuthManager4();
872
1048
  const taskOps = await TaskOperations2.fromAuthManager(authManager);
873
1049
  let tasks = await taskOps.getTasks(filter);
874
1050
  if (options.project) {
@@ -1031,11 +1207,11 @@ Error: ${error.message}
1031
1207
  import { Command as Command4 } from "commander";
1032
1208
  import ora3 from "ora";
1033
1209
  import chalk5 from "chalk";
1034
- import { AuthManager as AuthManager4, TaskOperations as TaskOperations3 } from "@vibetasks/core";
1210
+ import { AuthManager as AuthManager5, TaskOperations as TaskOperations3 } from "@vibetasks/core";
1035
1211
  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) => {
1036
1212
  const spinner = ora3("Completing task...").start();
1037
1213
  try {
1038
- const authManager = new AuthManager4();
1214
+ const authManager = new AuthManager5();
1039
1215
  const taskOps = await TaskOperations3.fromAuthManager(authManager);
1040
1216
  const shortIdManager = new ShortIdManager();
1041
1217
  let taskId = await shortIdManager.resolveId(id);
@@ -1108,11 +1284,11 @@ import { Command as Command5 } from "commander";
1108
1284
  import chalk6 from "chalk";
1109
1285
  import ora4 from "ora";
1110
1286
  import inquirer from "inquirer";
1111
- import { AuthManager as AuthManager5, TaskOperations as TaskOperations4 } from "@vibetasks/core";
1287
+ import { AuthManager as AuthManager6, TaskOperations as TaskOperations4 } from "@vibetasks/core";
1112
1288
  var vibingCommand = new Command5("vibing").alias("start").alias("v").description("Start working on a task (move to vibing status)").argument("[task-id]", "Task ID or short # (1-99) to start vibing on").option("-p, --pick", "Pick from todo tasks interactively").action(async (taskIdInput, options) => {
1113
1289
  const spinner = ora4();
1114
1290
  try {
1115
- const authManager = new AuthManager5();
1291
+ const authManager = new AuthManager6();
1116
1292
  const taskOps = await TaskOperations4.fromAuthManager(authManager);
1117
1293
  const shortIdManager = new ShortIdManager();
1118
1294
  let taskId;
@@ -1190,10 +1366,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
1190
1366
  import { Command as Command6 } from "commander";
1191
1367
  import chalk7 from "chalk";
1192
1368
  import Table2 from "cli-table3";
1193
- import { AuthManager as AuthManager6, TaskOperations as TaskOperations5 } from "@vibetasks/core";
1369
+ import { AuthManager as AuthManager7, TaskOperations as TaskOperations5 } from "@vibetasks/core";
1194
1370
  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) => {
1195
1371
  try {
1196
- const authManager = new AuthManager6();
1372
+ const authManager = new AuthManager7();
1197
1373
  const taskOps = await TaskOperations5.fromAuthManager(authManager);
1198
1374
  const limit = parseInt(options.limit, 10);
1199
1375
  const tasks = await taskOps.searchTasks(query, limit);
@@ -1250,7 +1426,7 @@ Error: ${error.message}
1250
1426
  import { Command as Command7 } from "commander";
1251
1427
  import ora5 from "ora";
1252
1428
  import chalk8 from "chalk";
1253
- import { AuthManager as AuthManager7, TaskOperations as TaskOperations6 } from "@vibetasks/core";
1429
+ import { AuthManager as AuthManager8, TaskOperations as TaskOperations6 } from "@vibetasks/core";
1254
1430
  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) => {
1255
1431
  if (!options.title && !options.notes && !options.due && !options.priority && !options.status && !options.project && !options.energy && !options.context) {
1256
1432
  console.error(chalk8.red("\nError: No updates specified"));
@@ -1259,7 +1435,7 @@ var updateCommand = new Command7("update").description("Update a task").argument
1259
1435
  }
1260
1436
  const spinner = ora5("Updating task...").start();
1261
1437
  try {
1262
- const authManager = new AuthManager7();
1438
+ const authManager = new AuthManager8();
1263
1439
  const taskOps = await TaskOperations6.fromAuthManager(authManager);
1264
1440
  let taskId = id;
1265
1441
  if (id.length < 32) {
@@ -1326,10 +1502,10 @@ import { Command as Command8 } from "commander";
1326
1502
  import ora6 from "ora";
1327
1503
  import chalk9 from "chalk";
1328
1504
  import inquirer2 from "inquirer";
1329
- import { AuthManager as AuthManager8, TaskOperations as TaskOperations7 } from "@vibetasks/core";
1505
+ import { AuthManager as AuthManager9, TaskOperations as TaskOperations7 } from "@vibetasks/core";
1330
1506
  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) => {
1331
1507
  try {
1332
- const authManager = new AuthManager8();
1508
+ const authManager = new AuthManager9();
1333
1509
  const taskOps = await TaskOperations7.fromAuthManager(authManager);
1334
1510
  let taskId = id;
1335
1511
  let taskTitle = "";
@@ -1385,10 +1561,10 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
1385
1561
  // src/commands/config.ts
1386
1562
  import { Command as Command9 } from "commander";
1387
1563
  import chalk10 from "chalk";
1388
- import { AuthManager as AuthManager9 } from "@vibetasks/core";
1564
+ import { AuthManager as AuthManager10 } from "@vibetasks/core";
1389
1565
  var configCommand = new Command9("config").description("View or set configuration").argument("[key]", "Configuration key").argument("[value]", "Configuration value").action(async (key, value) => {
1390
1566
  try {
1391
- const authManager = new AuthManager9();
1567
+ const authManager = new AuthManager10();
1392
1568
  if (!key) {
1393
1569
  const configManager = authManager["configManager"];
1394
1570
  const config = await configManager.getConfig();
@@ -1622,12 +1798,12 @@ import { Command as Command11 } from "commander";
1622
1798
  import inquirer3 from "inquirer";
1623
1799
  import ora8 from "ora";
1624
1800
  import chalk12 from "chalk";
1625
- import { AuthManager as AuthManager10 } from "@vibetasks/core";
1801
+ import { AuthManager as AuthManager11 } from "@vibetasks/core";
1626
1802
  import { detectProject as detectProject2 } from "@vibetasks/shared/utils/project-detector";
1627
1803
  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) => {
1628
1804
  const spinner = ora8("Detecting project...").start();
1629
1805
  try {
1630
- const authManager = new AuthManager10();
1806
+ const authManager = new AuthManager11();
1631
1807
  const cwd = process.cwd();
1632
1808
  let project;
1633
1809
  if (options.name) {
@@ -1692,7 +1868,7 @@ import os from "os";
1692
1868
  import { createServer as createServer2 } from "http";
1693
1869
  import { exec as exec2 } from "child_process";
1694
1870
  import { promisify as promisify2 } from "util";
1695
- import { AuthManager as AuthManager11, TaskOperations as TaskOperations8, createSupabaseClient } from "@vibetasks/core";
1871
+ import { AuthManager as AuthManager12, TaskOperations as TaskOperations8, createSupabaseClient } from "@vibetasks/core";
1696
1872
  import { detectProject as detectProject3 } from "@vibetasks/shared/utils/project-detector";
1697
1873
  var execAsync2 = promisify2(exec2);
1698
1874
  var SUPABASE_URL = "https://ihmayqzxqyednchbezya.supabase.co";
@@ -1757,7 +1933,7 @@ function showWelcome() {
1757
1933
  }
1758
1934
  async function checkExistingAuth() {
1759
1935
  try {
1760
- const authManager = new AuthManager11();
1936
+ const authManager = new AuthManager12();
1761
1937
  const token = await authManager.getAccessToken();
1762
1938
  if (!token) return { authenticated: false };
1763
1939
  const supabaseUrl = await authManager.getConfig("supabase_url") || SUPABASE_URL;
@@ -1801,7 +1977,7 @@ async function runBrowserAuth() {
1801
1977
  return;
1802
1978
  }
1803
1979
  try {
1804
- const authManager = new AuthManager11();
1980
+ const authManager = new AuthManager12();
1805
1981
  await authManager.setAccessToken(accessToken);
1806
1982
  await authManager.setRefreshToken(refreshToken);
1807
1983
  if (supabaseUrl) await authManager.setConfig("supabase_url", supabaseUrl);
@@ -1894,7 +2070,7 @@ async function stepAuthentication() {
1894
2070
  ]);
1895
2071
  const spinner = ora9("Authenticating...").start();
1896
2072
  try {
1897
- const authManager = new AuthManager11();
2073
+ const authManager = new AuthManager12();
1898
2074
  await authManager.setConfig("supabase_url", SUPABASE_URL);
1899
2075
  await authManager.setConfig("supabase_key", SUPABASE_ANON_KEY);
1900
2076
  const supabase = createSupabaseClient({
@@ -2043,7 +2219,7 @@ async function stepProjectInit() {
2043
2219
  return { success: true, skipped: true };
2044
2220
  }
2045
2221
  try {
2046
- const authManager = new AuthManager11();
2222
+ const authManager = new AuthManager12();
2047
2223
  await authManager.setConfig(`project_${process.cwd()}`, project.name);
2048
2224
  console.log(chalk13.green("\n \u2713") + chalk13.white(` Project "${project.name}" configured`));
2049
2225
  return { success: true, projectName: project.name };
@@ -2060,7 +2236,7 @@ async function stepVerify() {
2060
2236
  };
2061
2237
  const supabaseSpinner = ora9("Testing Supabase connection...").start();
2062
2238
  try {
2063
- const authManager = new AuthManager11();
2239
+ const authManager = new AuthManager12();
2064
2240
  const taskOps = await TaskOperations8.fromAuthManager(authManager);
2065
2241
  const tasks = await taskOps.getTasks("all");
2066
2242
  result.supabaseConnected = true;
@@ -2233,7 +2409,7 @@ async function runNonInteractiveSetup() {
2233
2409
  if (authResult.success) {
2234
2410
  console.log(chalk13.gray("\nVerifying connection..."));
2235
2411
  try {
2236
- const authManager = new AuthManager11();
2412
+ const authManager = new AuthManager12();
2237
2413
  const taskOps = await TaskOperations8.fromAuthManager(authManager);
2238
2414
  const tasks = await taskOps.getTasks("all");
2239
2415
  console.log(chalk13.green("\u2713") + ` Connected (${tasks.length} tasks)`);
@@ -2304,7 +2480,7 @@ import { Command as Command13 } from "commander";
2304
2480
  import chalk14 from "chalk";
2305
2481
  import ora10 from "ora";
2306
2482
  import inquirer5 from "inquirer";
2307
- import { AuthManager as AuthManager12, TaskOperations as TaskOperations9 } from "@vibetasks/core";
2483
+ import { AuthManager as AuthManager13, TaskOperations as TaskOperations9 } from "@vibetasks/core";
2308
2484
  import { detectProject as detectProject4 } from "@vibetasks/shared/utils/project-detector";
2309
2485
  import { generateErrorTaskTitle } from "@vibetasks/shared";
2310
2486
  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) => {
@@ -2318,7 +2494,7 @@ var watchCommand = new Command13("watch").description("Monitor clipboard for err
2318
2494
  let authManager;
2319
2495
  let taskOps;
2320
2496
  try {
2321
- authManager = new AuthManager12();
2497
+ authManager = new AuthManager13();
2322
2498
  taskOps = await TaskOperations9.fromAuthManager(authManager);
2323
2499
  } catch (error) {
2324
2500
  if (error.message.includes("Not authenticated")) {
@@ -2528,11 +2704,11 @@ import { Command as Command14 } from "commander";
2528
2704
  import chalk15 from "chalk";
2529
2705
  import ora11 from "ora";
2530
2706
  import inquirer6 from "inquirer";
2531
- import { AuthManager as AuthManager13, TaskOperations as TaskOperations10 } from "@vibetasks/core";
2707
+ import { AuthManager as AuthManager14, TaskOperations as TaskOperations10 } from "@vibetasks/core";
2532
2708
  var archiveCommand = new Command14("archive").description("Archive a task or manage archived tasks").argument("[task-id]", "Task ID to archive (full or first 8 characters)").option("-l, --list", "List all archived tasks").option("-u, --unarchive <id>", "Unarchive a task by ID").option("--pick", "Pick from completed tasks to archive").action(async (taskId, options) => {
2533
2709
  const spinner = ora11();
2534
2710
  try {
2535
- const authManager = new AuthManager13();
2711
+ const authManager = new AuthManager14();
2536
2712
  const taskOps = await TaskOperations10.fromAuthManager(authManager);
2537
2713
  if (options.list) {
2538
2714
  spinner.start("Fetching archived tasks...");
@@ -2647,7 +2823,7 @@ import ora12 from "ora";
2647
2823
  import { spawn } from "child_process";
2648
2824
  import path3 from "path";
2649
2825
  import { fileURLToPath } from "url";
2650
- import { AuthManager as AuthManager14, TaskOperations as TaskOperations11 } from "@vibetasks/core";
2826
+ import { AuthManager as AuthManager15, TaskOperations as TaskOperations11 } from "@vibetasks/core";
2651
2827
  import { generateErrorTaskTitle as generateErrorTaskTitle2 } from "@vibetasks/shared";
2652
2828
  var __filename2 = fileURLToPath(import.meta.url);
2653
2829
  var __dirname2 = path3.dirname(__filename2);
@@ -2971,7 +3147,7 @@ async function showNotification(title, message, type = "info", duration) {
2971
3147
  async function createTaskFromError2(error) {
2972
3148
  const spinner = ora12("Creating task from error...").start();
2973
3149
  try {
2974
- const authManager = new AuthManager14();
3150
+ const authManager = new AuthManager15();
2975
3151
  const taskOps = await TaskOperations11.fromAuthManager(authManager);
2976
3152
  const title = generateErrorTaskTitle2(error);
2977
3153
  const notes = formatErrorForNotes(error);
@@ -3034,11 +3210,11 @@ function getTagColor2(category) {
3034
3210
  // src/commands/next.ts
3035
3211
  import { Command as Command16 } from "commander";
3036
3212
  import chalk17 from "chalk";
3037
- import { AuthManager as AuthManager15, TaskOperations as TaskOperations12 } from "@vibetasks/core";
3213
+ import { AuthManager as AuthManager16, TaskOperations as TaskOperations12 } from "@vibetasks/core";
3038
3214
  var PRIORITY_ORDER = { high: 0, medium: 1, low: 2, none: 3 };
3039
3215
  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) => {
3040
3216
  try {
3041
- const authManager = new AuthManager15();
3217
+ const authManager = new AuthManager16();
3042
3218
  const taskOps = await TaskOperations12.fromAuthManager(authManager);
3043
3219
  let tasks = await taskOps.getTasks("all");
3044
3220
  tasks = tasks.filter((t) => t.status !== "done" && t.status !== "archived" && !t.completed);
@@ -3199,7 +3375,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3199
3375
  }
3200
3376
  })
3201
3377
  ).addCommand(
3202
- new Command17("end").description("End the current session").option("-s, --summary <text>", "Add a summary of what was done").action(async (options) => {
3378
+ 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) => {
3203
3379
  const sessionManager2 = getSessionManager();
3204
3380
  const session = await sessionManager2.endSession(options.summary);
3205
3381
  if (!session) {
@@ -3207,18 +3383,59 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3207
3383
  return;
3208
3384
  }
3209
3385
  console.log(chalk18.green("\u2713 Session ended:"), chalk18.bold(session.id));
3210
- console.log(chalk18.gray("Duration:"), formatDuration(session.startedAt, session.endedAt));
3211
- console.log(chalk18.gray("Actions:"), session.actions.length);
3212
- if (options.summary) {
3213
- console.log(chalk18.gray("Summary:"), options.summary);
3214
- }
3215
- })
3216
- ).addCommand(
3217
- new Command17("list").description("List recent sessions").option("-n, --limit <number>", "Number of sessions to show", "10").action(async (options) => {
3218
- const sessionManager2 = getSessionManager();
3219
- const sessions = await sessionManager2.getRecentSessions(parseInt(options.limit));
3220
- if (sessions.length === 0) {
3221
- console.log(chalk18.yellow("No sessions found"));
3386
+ console.log();
3387
+ if (options.stats !== false) {
3388
+ const duration = formatDuration(session.startedAt, session.endedAt);
3389
+ const tasksCreated = session.actions.filter((a) => a.type === "task_created").length;
3390
+ const tasksCompleted = session.actions.filter((a) => a.type === "task_completed").length;
3391
+ const tasksUpdated = session.actions.filter((a) => a.type === "task_updated").length;
3392
+ const filesModified = session.actions.filter((a) => a.type === "file_modified").length;
3393
+ const notes = session.actions.filter((a) => a.type === "note").length;
3394
+ console.log(chalk18.cyan("\u{1F4CA} Session Statistics"));
3395
+ console.log(chalk18.gray("\u2500".repeat(40)));
3396
+ console.log(chalk18.white(` Duration: ${chalk18.bold(duration)}`));
3397
+ console.log(chalk18.white(` Actions logged: ${chalk18.bold(session.actions.length.toString())}`));
3398
+ console.log();
3399
+ if (tasksCreated > 0 || tasksCompleted > 0 || tasksUpdated > 0) {
3400
+ console.log(chalk18.cyan(" Tasks:"));
3401
+ if (tasksCreated > 0) {
3402
+ console.log(chalk18.green(` \u2795 Created: ${tasksCreated}`));
3403
+ }
3404
+ if (tasksCompleted > 0) {
3405
+ console.log(chalk18.green(` \u2713 Completed: ${tasksCompleted}`));
3406
+ }
3407
+ if (tasksUpdated > 0) {
3408
+ console.log(chalk18.blue(` \u{1F4DD} Updated: ${tasksUpdated}`));
3409
+ }
3410
+ }
3411
+ if (filesModified > 0) {
3412
+ console.log(chalk18.yellow(` \u{1F4C4} Files modified: ${filesModified}`));
3413
+ }
3414
+ if (notes > 0) {
3415
+ console.log(chalk18.gray(` \u{1F4AC} Notes logged: ${notes}`));
3416
+ }
3417
+ console.log(chalk18.gray("\u2500".repeat(40)));
3418
+ console.log();
3419
+ }
3420
+ if (options.summary) {
3421
+ console.log(chalk18.cyan("Summary:"), options.summary);
3422
+ console.log();
3423
+ }
3424
+ const totalTasks = session.actions.filter(
3425
+ (a) => a.type === "task_created" || a.type === "task_completed"
3426
+ ).length;
3427
+ if (totalTasks === 0) {
3428
+ console.log(chalk18.yellow("\u{1F4A1} Tip: Use `vibetasks add` and `vibetasks done` to track work"));
3429
+ } else if (totalTasks >= 5) {
3430
+ console.log(chalk18.green("\u{1F525} Productive session!"));
3431
+ }
3432
+ })
3433
+ ).addCommand(
3434
+ new Command17("list").description("List recent sessions").option("-n, --limit <number>", "Number of sessions to show", "10").action(async (options) => {
3435
+ const sessionManager2 = getSessionManager();
3436
+ const sessions = await sessionManager2.getRecentSessions(parseInt(options.limit));
3437
+ if (sessions.length === 0) {
3438
+ console.log(chalk18.yellow("No sessions found"));
3222
3439
  return;
3223
3440
  }
3224
3441
  console.log(chalk18.cyan("Recent Sessions:"));
@@ -3236,7 +3453,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3236
3453
  }
3237
3454
  })
3238
3455
  ).addCommand(
3239
- 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) => {
3456
+ new Command17("show").description("Show session details").argument("<session-id>", "Session ID (partial match supported)").action(async (sessionId) => {
3240
3457
  const sessionManager2 = getSessionManager();
3241
3458
  const handoff = await sessionManager2.getSessionHandoff(sessionId);
3242
3459
  console.log(handoff);
@@ -3250,6 +3467,23 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
3250
3467
  });
3251
3468
  console.log(chalk18.green("\u2713 Logged to session"));
3252
3469
  })
3470
+ ).addCommand(
3471
+ new Command17("sync").description("Sync current session and all actions to the server").action(async () => {
3472
+ const sessionManager2 = getSessionManager();
3473
+ const session = await sessionManager2.getCurrentSession();
3474
+ if (!session) {
3475
+ console.log(chalk18.yellow("No active session to sync"));
3476
+ return;
3477
+ }
3478
+ console.log(chalk18.cyan("Syncing session:"), chalk18.bold(session.id));
3479
+ console.log(chalk18.gray(`Actions to sync: ${session.actions.length}`));
3480
+ const success = await sessionManager2.syncToServer(session);
3481
+ if (success) {
3482
+ console.log(chalk18.green("\u2713 Session synced to server"));
3483
+ } else {
3484
+ console.log(chalk18.yellow("\u26A0 Sync failed (not logged in or server unavailable)"));
3485
+ }
3486
+ })
3253
3487
  );
3254
3488
  function formatDuration(start, end) {
3255
3489
  const ms = new Date(end).getTime() - new Date(start).getTime();
@@ -3265,7 +3499,7 @@ function formatDuration(start, end) {
3265
3499
  import { Command as Command18 } from "commander";
3266
3500
  import ora13 from "ora";
3267
3501
  import chalk19 from "chalk";
3268
- import { AuthManager as AuthManager16, TaskOperations as TaskOperations13 } from "@vibetasks/core";
3502
+ import { AuthManager as AuthManager17, TaskOperations as TaskOperations13 } from "@vibetasks/core";
3269
3503
  function parseIndices(args) {
3270
3504
  const indices = /* @__PURE__ */ new Set();
3271
3505
  for (const arg of args) {
@@ -3307,7 +3541,7 @@ var subtaskCommand = new Command18("subtask").description("Manage subtasks withi
3307
3541
  subtaskCommand.command("list").description("List subtasks for a task").argument("<task-id>", "Task ID, short # (1-99), or first 8 characters").action(async (taskIdArg) => {
3308
3542
  const spinner = ora13("Loading subtasks...").start();
3309
3543
  try {
3310
- const authManager = new AuthManager16();
3544
+ const authManager = new AuthManager17();
3311
3545
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3312
3546
  const shortIdManager = new ShortIdManager();
3313
3547
  let taskId = await shortIdManager.resolveId(taskIdArg);
@@ -3355,7 +3589,7 @@ Error: ${error.message}
3355
3589
  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) => {
3356
3590
  const spinner = ora13("Updating subtasks...").start();
3357
3591
  try {
3358
- const authManager = new AuthManager16();
3592
+ const authManager = new AuthManager17();
3359
3593
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3360
3594
  const shortIdManager = new ShortIdManager();
3361
3595
  let taskId = await shortIdManager.resolveId(taskIdArg);
@@ -3431,7 +3665,7 @@ Error: ${error.message}
3431
3665
  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) => {
3432
3666
  const spinner = ora13("Updating subtasks...").start();
3433
3667
  try {
3434
- const authManager = new AuthManager16();
3668
+ const authManager = new AuthManager17();
3435
3669
  const taskOps = await TaskOperations13.fromAuthManager(authManager);
3436
3670
  const shortIdManager = new ShortIdManager();
3437
3671
  let taskId = await shortIdManager.resolveId(taskIdArg);
@@ -3505,7 +3739,7 @@ Error: ${error.message}
3505
3739
  import { Command as Command19 } from "commander";
3506
3740
  import chalk20 from "chalk";
3507
3741
  import ora14 from "ora";
3508
- import { AuthManager as AuthManager17, TaskOperations as TaskOperations14 } from "@vibetasks/core";
3742
+ import { AuthManager as AuthManager18, TaskOperations as TaskOperations14 } from "@vibetasks/core";
3509
3743
  import {
3510
3744
  parseError,
3511
3745
  generateErrorTaskTitle as generateErrorTaskTitle3,
@@ -3603,7 +3837,7 @@ var errorCommand = new Command19("error").description("Capture an error and crea
3603
3837
  process.exit(1);
3604
3838
  }
3605
3839
  spinner.start("Creating error task...");
3606
- const authManager = new AuthManager17();
3840
+ const authManager = new AuthManager18();
3607
3841
  const taskOps = await TaskOperations14.fromAuthManager(authManager);
3608
3842
  const errorContext = parseError(errorText, "terminal");
3609
3843
  const title = errorContext ? generateErrorTaskTitle3(errorContext) : `Error: ${errorText.split("\n")[0].slice(0, 80)}`;
@@ -3643,7 +3877,7 @@ ${errorText}
3643
3877
  import { Command as Command20 } from "commander";
3644
3878
  import chalk21 from "chalk";
3645
3879
  import Table3 from "cli-table3";
3646
- import { AuthManager as AuthManager18, TaskOperations as TaskOperations15 } from "@vibetasks/core";
3880
+ import { AuthManager as AuthManager19, TaskOperations as TaskOperations15 } from "@vibetasks/core";
3647
3881
  var sourceDisplay = {
3648
3882
  sentry: { label: "Sentry", color: chalk21.red },
3649
3883
  ci: { label: "CI", color: chalk21.yellow },
@@ -3670,7 +3904,7 @@ function formatSource(sourceType) {
3670
3904
  }
3671
3905
  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) => {
3672
3906
  try {
3673
- const authManager = new AuthManager18();
3907
+ const authManager = new AuthManager19();
3674
3908
  const taskOps = await TaskOperations15.fromAuthManager(authManager);
3675
3909
  let sourceTypes;
3676
3910
  if (options.source) {
@@ -3779,7 +4013,7 @@ Error: ${error.message}
3779
4013
  });
3780
4014
  var inboxStatsCommand = new Command20("stats").description("Show inbox statistics").action(async () => {
3781
4015
  try {
3782
- const authManager = new AuthManager18();
4016
+ const authManager = new AuthManager19();
3783
4017
  const taskOps = await TaskOperations15.fromAuthManager(authManager);
3784
4018
  const stats = await taskOps.getInboxStats();
3785
4019
  console.log("\n" + chalk21.bold("Inbox Statistics") + "\n");
@@ -3835,10 +4069,1267 @@ Error: ${error.message}
3835
4069
  });
3836
4070
  inboxCommand.addCommand(inboxStatsCommand);
3837
4071
 
4072
+ // src/commands/handoff.ts
4073
+ import { Command as Command21 } from "commander";
4074
+ import chalk22 from "chalk";
4075
+ import { AuthManager as AuthManager20, TaskOperations as TaskOperations16 } from "@vibetasks/core";
4076
+ var handoffCommand = new Command21("handoff").description("Generate comprehensive handoff for the next AI (vibing tasks, context notes, git status)").argument("[session-id]", "Session ID (shows that session only)").option("--full", "Include full action history from session").action(async (sessionId, options) => {
4077
+ const sessionManager2 = getSessionManager();
4078
+ if (sessionId) {
4079
+ const handoff = await sessionManager2.getSessionHandoff(sessionId);
4080
+ console.log(handoff);
4081
+ return;
4082
+ }
4083
+ console.log(chalk22.cyan("# AI Handoff"));
4084
+ console.log(chalk22.gray(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`));
4085
+ console.log();
4086
+ const currentSession = await sessionManager2.getCurrentSession();
4087
+ if (currentSession) {
4088
+ console.log(chalk22.yellow("## Session"));
4089
+ console.log(`ID: ${chalk22.bold(currentSession.id)}`);
4090
+ console.log(`Project: ${currentSession.project || "unknown"}`);
4091
+ console.log(`Started: ${new Date(currentSession.startedAt).toLocaleString()}`);
4092
+ console.log(`Actions: ${currentSession.actions.length}`);
4093
+ if (currentSession.summary) {
4094
+ console.log(`Summary: ${currentSession.summary}`);
4095
+ }
4096
+ console.log();
4097
+ }
4098
+ try {
4099
+ const authManager = new AuthManager20();
4100
+ const taskOps = await TaskOperations16.fromAuthManager(authManager);
4101
+ const allTasks = await taskOps.getTasks("all");
4102
+ const vibingTasks = allTasks.filter((t) => t.status === "vibing");
4103
+ if (vibingTasks.length > 0) {
4104
+ console.log(chalk22.yellow("## Vibing Tasks (Active Work)"));
4105
+ console.log();
4106
+ for (const task of vibingTasks) {
4107
+ const shortId = task.short_id || task.id.slice(0, 8);
4108
+ console.log(chalk22.green(`### #${shortId}: ${task.title}`));
4109
+ if (task.project_tag) {
4110
+ console.log(chalk22.gray(`Project: ${task.project_tag}`));
4111
+ }
4112
+ const subtasks = task.subtasks_json || task.subtasks || [];
4113
+ if (subtasks.length > 0) {
4114
+ const done = subtasks.filter((s) => s.done).length;
4115
+ console.log(`Progress: ${done}/${subtasks.length} subtasks`);
4116
+ subtasks.forEach((s, i) => {
4117
+ const status = s.done ? chalk22.green("\u2713") : chalk22.gray("\u25CB");
4118
+ const title = s.done ? chalk22.gray(s.title) : s.title;
4119
+ console.log(` ${status} ${i + 1}. ${title}`);
4120
+ });
4121
+ }
4122
+ if (task.context_notes) {
4123
+ console.log();
4124
+ console.log(chalk22.cyan("Where I left off:"));
4125
+ console.log(task.context_notes);
4126
+ } else {
4127
+ console.log();
4128
+ console.log(chalk22.yellow("\u26A0 No context notes - update with:"));
4129
+ console.log(chalk22.gray(` vibetasks update ${shortId} --context "..."`));
4130
+ }
4131
+ console.log();
4132
+ }
4133
+ } else {
4134
+ console.log(chalk22.yellow("## No Vibing Tasks"));
4135
+ console.log(chalk22.gray("All clear - ready for new work."));
4136
+ console.log();
4137
+ }
4138
+ } catch (error) {
4139
+ console.log(chalk22.gray("(Could not fetch tasks - may need to login)"));
4140
+ console.log();
4141
+ }
4142
+ try {
4143
+ const { execSync: execSync2 } = await import("child_process");
4144
+ const gitStatus = execSync2("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
4145
+ if (gitStatus) {
4146
+ const files = gitStatus.split("\n");
4147
+ console.log(chalk22.yellow(`## Uncommitted Changes (${files.length} files)`));
4148
+ files.slice(0, 15).forEach((f) => console.log(chalk22.gray(` ${f}`)));
4149
+ if (files.length > 15) {
4150
+ console.log(chalk22.gray(` ... and ${files.length - 15} more`));
4151
+ }
4152
+ console.log();
4153
+ }
4154
+ const commits = execSync2("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
4155
+ if (commits) {
4156
+ console.log(chalk22.yellow("## Recent Commits"));
4157
+ commits.split("\n").forEach((c) => console.log(chalk22.gray(` ${c}`)));
4158
+ console.log();
4159
+ }
4160
+ } catch {
4161
+ }
4162
+ console.log(chalk22.cyan("\u2500".repeat(60)));
4163
+ console.log(chalk22.yellow("## For Next AI"));
4164
+ console.log();
4165
+ console.log("1. Run: `vibetasks session current || vibetasks session start`");
4166
+ console.log("2. Run: `vibetasks list --status vibing --short-ids`");
4167
+ console.log('3. Read context_notes above for "where I left off"');
4168
+ console.log("4. Continue the vibing task or ask user what to do");
4169
+ if (currentSession) {
4170
+ console.log();
4171
+ console.log(chalk22.gray(`To reference this session: "Continue from session ${currentSession.id}"`));
4172
+ }
4173
+ });
4174
+
4175
+ // src/commands/parse-prd.ts
4176
+ import { Command as Command22 } from "commander";
4177
+ import ora15 from "ora";
4178
+ import chalk23 from "chalk";
4179
+ import { readFile as readFile3 } from "fs/promises";
4180
+ import { existsSync } from "fs";
4181
+ import { AuthManager as AuthManager21, TaskOperations as TaskOperations17 } from "@vibetasks/core";
4182
+ import { detectProject as detectProject6 } from "@vibetasks/shared/utils/project-detector";
4183
+ import { randomUUID as randomUUID3 } from "crypto";
4184
+ var parsePrdCommand = new Command22("parse-prd").description("Parse a PRD/requirements document and create tasks").argument("<file>", "Path to PRD/requirements file (markdown)").option("-t, --title <title>", "Override task title (auto-generated if not provided)").option("-p, --priority <level>", "Priority: low, medium, high", "high").option("--project <name>", "Override auto-detected project").option("--dry-run", "Show what would be created without creating").option("-v, --verbose", "Show detailed parsing output").action(async (filePath, options) => {
4185
+ const spinner = ora15("Parsing PRD...").start();
4186
+ try {
4187
+ if (!existsSync(filePath)) {
4188
+ throw new Error(`File not found: ${filePath}`);
4189
+ }
4190
+ const content = await readFile3(filePath, "utf-8");
4191
+ spinner.text = "Analyzing document structure...";
4192
+ const parsed = parsePrdContent(content, options.verbose);
4193
+ if (options.verbose) {
4194
+ spinner.info("Parsed structure:");
4195
+ console.log(chalk23.gray(` Title: ${parsed.title}`));
4196
+ console.log(chalk23.gray(` Summary: ${parsed.summary?.substring(0, 100)}...`));
4197
+ console.log(chalk23.gray(` Sections: ${parsed.sections.length}`));
4198
+ console.log(chalk23.gray(` Tasks: ${parsed.tasks.length}`));
4199
+ spinner.start("Continuing...");
4200
+ }
4201
+ const finalTitle = options.title || parsed.title || "Implement PRD";
4202
+ const subtasksJson = parsed.tasks.map((task2, i) => ({
4203
+ id: randomUUID3(),
4204
+ title: task2,
4205
+ done: false
4206
+ }));
4207
+ const acceptanceCriteria = parsed.acceptanceCriteria || [];
4208
+ let notes = "";
4209
+ if (parsed.summary) {
4210
+ notes += `## Summary
4211
+ ${parsed.summary}
4212
+
4213
+ `;
4214
+ }
4215
+ if (acceptanceCriteria.length > 0) {
4216
+ notes += `DONE WHEN:
4217
+ ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}
4218
+
4219
+ `;
4220
+ }
4221
+ if (parsed.nonGoals && parsed.nonGoals.length > 0) {
4222
+ notes += `## Out of Scope
4223
+ ${parsed.nonGoals.map((n) => `- ${n}`).join("\n")}
4224
+ `;
4225
+ }
4226
+ if (options.dryRun) {
4227
+ spinner.succeed(chalk23.yellow("Dry run - no task created"));
4228
+ console.log();
4229
+ console.log(chalk23.white.bold(`Task: ${finalTitle}`));
4230
+ console.log(chalk23.gray(`Priority: ${options.priority}`));
4231
+ console.log();
4232
+ if (subtasksJson.length > 0) {
4233
+ console.log(chalk23.cyan("Subtasks:"));
4234
+ subtasksJson.forEach((st, i) => {
4235
+ console.log(chalk23.gray(` ${i + 1}. ${st.title}`));
4236
+ });
4237
+ console.log();
4238
+ }
4239
+ if (acceptanceCriteria.length > 0) {
4240
+ console.log(chalk23.green("Acceptance Criteria:"));
4241
+ acceptanceCriteria.forEach((c, i) => {
4242
+ console.log(chalk23.gray(` \u2713 ${c}`));
4243
+ });
4244
+ console.log();
4245
+ }
4246
+ if (parsed.nonGoals && parsed.nonGoals.length > 0) {
4247
+ console.log(chalk23.red("Non-Goals (out of scope):"));
4248
+ parsed.nonGoals.forEach((n) => {
4249
+ console.log(chalk23.gray(` \u2717 ${n}`));
4250
+ });
4251
+ }
4252
+ process.exit(0);
4253
+ return;
4254
+ }
4255
+ spinner.text = "Creating task...";
4256
+ const authManager = new AuthManager21();
4257
+ const taskOps = await TaskOperations17.fromAuthManager(authManager);
4258
+ let projectTag;
4259
+ if (options.project) {
4260
+ projectTag = options.project;
4261
+ } else {
4262
+ try {
4263
+ const detected = await detectProject6(process.cwd());
4264
+ projectTag = detected.fullName || detected.name;
4265
+ } catch {
4266
+ }
4267
+ }
4268
+ let sessionId;
4269
+ let sessionHistory = [];
4270
+ const sessionManager2 = getSessionManager();
4271
+ const currentSession = await sessionManager2.getCurrentSession();
4272
+ if (currentSession) {
4273
+ sessionId = currentSession.id;
4274
+ sessionHistory = [{
4275
+ session_id: currentSession.id,
4276
+ action: "created",
4277
+ at: (/* @__PURE__ */ new Date()).toISOString()
4278
+ }];
4279
+ }
4280
+ const task = await taskOps.createTask({
4281
+ title: finalTitle,
4282
+ notes,
4283
+ notes_format: "markdown",
4284
+ priority: options.priority,
4285
+ project_tag: projectTag,
4286
+ created_by: "ai",
4287
+ status: "todo",
4288
+ subtasks_json: subtasksJson,
4289
+ session_id: sessionId,
4290
+ session_history: sessionHistory,
4291
+ is_plan: true
4292
+ // Mark as a plan task
4293
+ });
4294
+ try {
4295
+ const shortIdManager = new ShortIdManager();
4296
+ const allTasks = await taskOps.getTasks("all");
4297
+ await shortIdManager.saveMappings(allTasks);
4298
+ } catch {
4299
+ }
4300
+ if (currentSession) {
4301
+ await sessionManager2.logAction({
4302
+ type: "task_created",
4303
+ description: `Created task from PRD: "${finalTitle}"`,
4304
+ taskId: task.id,
4305
+ taskTitle: finalTitle
4306
+ });
4307
+ }
4308
+ spinner.succeed(chalk23.green("Task created from PRD!"));
4309
+ console.log();
4310
+ console.log(chalk23.white.bold(finalTitle));
4311
+ if (projectTag) {
4312
+ console.log(chalk23.gray(`\u{1F4C1} Project: ${chalk23.white(projectTag)}`));
4313
+ }
4314
+ console.log(chalk23.gray(`ID: ${task.id.substring(0, 8)}...`));
4315
+ console.log();
4316
+ if (subtasksJson.length > 0) {
4317
+ console.log(chalk23.cyan(`\u{1F4CB} ${subtasksJson.length} subtasks created:`));
4318
+ subtasksJson.slice(0, 10).forEach((st, i) => {
4319
+ console.log(chalk23.gray(` ${i + 1}. ${st.title}`));
4320
+ });
4321
+ if (subtasksJson.length > 10) {
4322
+ console.log(chalk23.gray(` ... and ${subtasksJson.length - 10} more`));
4323
+ }
4324
+ console.log();
4325
+ }
4326
+ if (acceptanceCriteria.length > 0) {
4327
+ console.log(chalk23.green(`\u2713 ${acceptanceCriteria.length} acceptance criteria defined`));
4328
+ }
4329
+ console.log();
4330
+ console.log(chalk23.gray("Start working with:"));
4331
+ console.log(chalk23.white(` vibetasks vibing ${task.short_id || task.id.substring(0, 8)}`));
4332
+ console.log();
4333
+ process.exit(0);
4334
+ } catch (error) {
4335
+ spinner.fail(chalk23.red("Failed to parse PRD"));
4336
+ if (error.message.includes("Not authenticated")) {
4337
+ console.error(chalk23.yellow("\n\u26A0\uFE0F You need to login first"));
4338
+ console.error(chalk23.gray("Run: vibetasks login\n"));
4339
+ } else {
4340
+ console.error(chalk23.red(`
4341
+ Error: ${error.message}
4342
+ `));
4343
+ }
4344
+ process.exit(1);
4345
+ }
4346
+ });
4347
+ function parsePrdContent(content, verbose = false) {
4348
+ const lines = content.split("\n");
4349
+ const result = {
4350
+ title: "",
4351
+ sections: [],
4352
+ tasks: [],
4353
+ acceptanceCriteria: [],
4354
+ nonGoals: []
4355
+ };
4356
+ const h1Match = content.match(/^#\s+(.+)$/m);
4357
+ if (h1Match) {
4358
+ result.title = h1Match[1].trim();
4359
+ }
4360
+ const summaryMatch = content.match(/^#\s+.+\n\n([\s\S]*?)(?=\n##|$)/);
4361
+ if (summaryMatch) {
4362
+ result.summary = summaryMatch[1].trim().split("\n\n")[0];
4363
+ }
4364
+ const sectionPattern = /^##\s+(.+)$/gm;
4365
+ let match;
4366
+ while ((match = sectionPattern.exec(content)) !== null) {
4367
+ result.sections.push(match[1].trim());
4368
+ }
4369
+ const taskPatterns = [
4370
+ /^[-*]\s+\[[ x]\]\s+(.+)$/gm,
4371
+ // Checkbox items
4372
+ /^[-*]\s+(.+)$/gm,
4373
+ // Regular bullet points under task-like headers
4374
+ /^\d+\.\s+(.+)$/gm
4375
+ // Numbered items
4376
+ ];
4377
+ const taskSectionPatterns = [
4378
+ /##\s*(?:tasks?|implementation|steps?|work breakdown|subtasks?)\s*\n([\s\S]*?)(?=\n##|$)/gi,
4379
+ /##\s*(?:requirements?|deliverables?|milestones?)\s*\n([\s\S]*?)(?=\n##|$)/gi
4380
+ ];
4381
+ for (const pattern of taskSectionPatterns) {
4382
+ let sectionMatch;
4383
+ while ((sectionMatch = pattern.exec(content)) !== null) {
4384
+ const sectionContent = sectionMatch[1];
4385
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4386
+ let bulletMatch;
4387
+ while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
4388
+ const task = bulletMatch[1].trim();
4389
+ const cleanTask = task.replace(/^\[[ x]\]\s*/, "").trim();
4390
+ if (cleanTask && !result.tasks.includes(cleanTask)) {
4391
+ result.tasks.push(cleanTask);
4392
+ }
4393
+ }
4394
+ const numberedPattern = /^\d+\.\s+(.+)$/gm;
4395
+ while ((bulletMatch = numberedPattern.exec(sectionContent)) !== null) {
4396
+ const task = bulletMatch[1].trim();
4397
+ if (task && !result.tasks.includes(task)) {
4398
+ result.tasks.push(task);
4399
+ }
4400
+ }
4401
+ }
4402
+ }
4403
+ if (result.tasks.length === 0) {
4404
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4405
+ while ((match = bulletPattern.exec(content)) !== null) {
4406
+ const task = match[1].trim();
4407
+ if (!task.toLowerCase().includes("done when") && !task.toLowerCase().includes("success") && !task.toLowerCase().includes("criterion")) {
4408
+ result.tasks.push(task);
4409
+ }
4410
+ }
4411
+ }
4412
+ const criteriaPatterns = [
4413
+ /##\s*(?:goals?|success criteria|acceptance criteria|done when|definition of done)\s*\n([\s\S]*?)(?=\n##|$)/gi
4414
+ ];
4415
+ for (const pattern of criteriaPatterns) {
4416
+ let criteriaMatch;
4417
+ while ((criteriaMatch = pattern.exec(content)) !== null) {
4418
+ const sectionContent = criteriaMatch[1];
4419
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4420
+ let bulletMatch;
4421
+ while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
4422
+ const criterion = bulletMatch[1].trim();
4423
+ if (criterion && !result.acceptanceCriteria.includes(criterion)) {
4424
+ result.acceptanceCriteria.push(criterion);
4425
+ }
4426
+ }
4427
+ }
4428
+ }
4429
+ const nonGoalPatterns = [
4430
+ /##\s*(?:non-goals?|out of scope|not in scope|exclusions?)\s*\n([\s\S]*?)(?=\n##|$)/gi
4431
+ ];
4432
+ for (const pattern of nonGoalPatterns) {
4433
+ let nonGoalMatch;
4434
+ while ((nonGoalMatch = pattern.exec(content)) !== null) {
4435
+ const sectionContent = nonGoalMatch[1];
4436
+ const bulletPattern = /^[-*]\s+(.+)$/gm;
4437
+ let bulletMatch;
4438
+ while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
4439
+ const nonGoal = bulletMatch[1].trim();
4440
+ if (nonGoal && !result.nonGoals.includes(nonGoal)) {
4441
+ result.nonGoals.push(nonGoal);
4442
+ }
4443
+ }
4444
+ }
4445
+ }
4446
+ if (result.tasks.length > 20) {
4447
+ result.tasks = result.tasks.slice(0, 20);
4448
+ }
4449
+ return result;
4450
+ }
4451
+
4452
+ // src/commands/checkpoint.ts
4453
+ import { Command as Command23 } from "commander";
4454
+ import chalk24 from "chalk";
4455
+ import { AuthManager as AuthManager22, TaskOperations as TaskOperations18 } from "@vibetasks/core";
4456
+ import { execSync } from "child_process";
4457
+ import * as readline from "readline";
4458
+ var checkpointCommand = new Command23("checkpoint").alias("cp").description("Quick context save for current work (interactive)").argument("[task-id]", "Task ID (defaults to your vibing task)").option("-q, --quick", "Quick mode: just git diff + timestamp, no prompts").option("-m, --message <msg>", "Context message (skip prompt)").option("--no-git", "Skip git diff attachment").action(async (taskId, options) => {
4459
+ try {
4460
+ const authManager = new AuthManager22();
4461
+ const taskOps = await TaskOperations18.fromAuthManager(authManager);
4462
+ const sessionManager2 = getSessionManager();
4463
+ let task;
4464
+ if (taskId) {
4465
+ const allTasks = await taskOps.getTasks("all");
4466
+ task = allTasks.find(
4467
+ (t) => t.id.startsWith(taskId) || t.short_id?.toString() === taskId
4468
+ );
4469
+ if (!task) {
4470
+ console.error(chalk24.red(`Task not found: ${taskId}`));
4471
+ process.exit(1);
4472
+ }
4473
+ } else {
4474
+ const currentSession2 = await sessionManager2.getCurrentSession();
4475
+ const allTasks = await taskOps.getTasks("all");
4476
+ const vibingTasks = allTasks.filter((t) => t.status === "vibing");
4477
+ if (vibingTasks.length === 0) {
4478
+ console.error(chalk24.yellow("No vibing tasks found"));
4479
+ console.error(chalk24.gray("Start one with: vibetasks vibing <task-id>"));
4480
+ process.exit(1);
4481
+ }
4482
+ if (vibingTasks.length === 1) {
4483
+ task = vibingTasks[0];
4484
+ } else {
4485
+ const sessionTask = currentSession2 ? vibingTasks.find((t) => t.session_id === currentSession2.fullId) : null;
4486
+ if (sessionTask) {
4487
+ task = sessionTask;
4488
+ } else {
4489
+ console.log(chalk24.yellow("Multiple vibing tasks found:"));
4490
+ vibingTasks.forEach((t, i) => {
4491
+ console.log(chalk24.gray(` ${i + 1}. [${t.short_id || t.id.slice(0, 8)}] ${t.title}`));
4492
+ });
4493
+ console.log(chalk24.gray("\nSpecify task: vibetasks checkpoint <task-id>"));
4494
+ process.exit(1);
4495
+ }
4496
+ }
4497
+ }
4498
+ console.log(chalk24.cyan("Checkpointing:"), chalk24.bold(task.title));
4499
+ console.log(chalk24.gray(`ID: ${task.short_id || task.id.slice(0, 8)}`));
4500
+ console.log();
4501
+ let contextParts = [];
4502
+ let gitDiff = "";
4503
+ if (options.git !== false) {
4504
+ try {
4505
+ gitDiff = getGitContext();
4506
+ if (gitDiff) {
4507
+ contextParts.push(`## Git Changes
4508
+ \`\`\`
4509
+ ${gitDiff}
4510
+ \`\`\``);
4511
+ console.log(chalk24.green("\u2713"), chalk24.gray("Captured git changes"));
4512
+ }
4513
+ } catch {
4514
+ }
4515
+ }
4516
+ if (options.quick) {
4517
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
4518
+ contextParts.unshift(`Checkpoint at ${timestamp}`);
4519
+ const contextNotes2 = contextParts.join("\n\n");
4520
+ await taskOps.updateTask(task.id, { context_notes: contextNotes2 });
4521
+ console.log(chalk24.green("\u2713 Quick checkpoint saved"));
4522
+ process.exit(0);
4523
+ }
4524
+ if (options.message) {
4525
+ contextParts.unshift(`## Progress
4526
+ ${options.message}`);
4527
+ const contextNotes2 = contextParts.join("\n\n");
4528
+ await taskOps.updateTask(task.id, { context_notes: contextNotes2 });
4529
+ console.log(chalk24.green("\u2713 Checkpoint saved"));
4530
+ process.exit(0);
4531
+ }
4532
+ const rl = readline.createInterface({
4533
+ input: process.stdin,
4534
+ output: process.stdout
4535
+ });
4536
+ const prompt = (question) => {
4537
+ return new Promise((resolve) => {
4538
+ rl.question(chalk24.cyan(question), (answer) => {
4539
+ resolve(answer.trim());
4540
+ });
4541
+ });
4542
+ };
4543
+ console.log(chalk24.gray("Press Enter to skip any question\n"));
4544
+ const completed = await prompt("What did you just complete? ");
4545
+ if (completed) {
4546
+ contextParts.unshift(`## Completed
4547
+ ${completed}`);
4548
+ }
4549
+ const nextStep = await prompt("What's the next step? ");
4550
+ if (nextStep) {
4551
+ contextParts.push(`## Next
4552
+ ${nextStep}`);
4553
+ }
4554
+ const blockers = await prompt("Any blockers? (or press Enter) ");
4555
+ if (blockers) {
4556
+ contextParts.push(`## Blockers
4557
+ ${blockers}`);
4558
+ }
4559
+ rl.close();
4560
+ if (contextParts.length === 0) {
4561
+ console.log(chalk24.yellow("Nothing to save (all fields empty)"));
4562
+ process.exit(0);
4563
+ }
4564
+ const contextNotes = contextParts.join("\n\n");
4565
+ await taskOps.updateTask(task.id, { context_notes: contextNotes });
4566
+ const currentSession = await sessionManager2.getCurrentSession();
4567
+ if (currentSession) {
4568
+ await sessionManager2.logAction({
4569
+ type: "task_updated",
4570
+ description: `Checkpoint: ${task.title}`,
4571
+ taskId: task.id,
4572
+ taskTitle: task.title
4573
+ });
4574
+ }
4575
+ console.log();
4576
+ console.log(chalk24.green("\u2713 Checkpoint saved"));
4577
+ console.log(chalk24.gray("Context will be available for the next AI session"));
4578
+ process.exit(0);
4579
+ } catch (error) {
4580
+ if (error.message.includes("Not authenticated")) {
4581
+ console.error(chalk24.yellow("\n\u26A0\uFE0F You need to login first"));
4582
+ console.error(chalk24.gray("Run: vibetasks login\n"));
4583
+ } else {
4584
+ console.error(chalk24.red(`
4585
+ Error: ${error.message}
4586
+ `));
4587
+ }
4588
+ process.exit(1);
4589
+ }
4590
+ });
4591
+ function getGitContext() {
4592
+ const parts = [];
4593
+ try {
4594
+ const branch = execSync("git branch --show-current", { encoding: "utf-8" }).trim();
4595
+ parts.push(`Branch: ${branch}`);
4596
+ const status = execSync("git status --porcelain", { encoding: "utf-8" }).trim();
4597
+ if (status) {
4598
+ const lines = status.split("\n").slice(0, 20);
4599
+ parts.push(`
4600
+ Changed files:
4601
+ ${lines.join("\n")}`);
4602
+ if (status.split("\n").length > 20) {
4603
+ parts.push(`... and ${status.split("\n").length - 20} more`);
4604
+ }
4605
+ }
4606
+ const log = execSync("git log --oneline -3", { encoding: "utf-8" }).trim();
4607
+ if (log) {
4608
+ parts.push(`
4609
+ Recent commits:
4610
+ ${log}`);
4611
+ }
4612
+ } catch {
4613
+ return "";
4614
+ }
4615
+ return parts.join("\n");
4616
+ }
4617
+
4618
+ // src/commands/log.ts
4619
+ import { Command as Command24 } from "commander";
4620
+ import chalk25 from "chalk";
4621
+ import { AuthManager as AuthManager23, TaskOperations as TaskOperations19 } from "@vibetasks/core";
4622
+ var logCommand = new Command24("log").description("Show change history for a task (like git log)").argument("[task]", "Task ID or short number").option("-n, --limit <number>", "Maximum number of changes to show", "20").option("--oneline", "Compact one-line format").option("--since <date>", "Show changes after date (YYYY-MM-DD)").option("--until <date>", "Show changes before date (YYYY-MM-DD)").option("--type <types>", "Filter by change type (comma-separated)").option("--grep <query>", "Search change messages (ignores task arg)").option("--session <id>", "Show changes from specific session").action(async (taskArg, options) => {
4623
+ try {
4624
+ const authManager = new AuthManager23();
4625
+ const taskOps = await TaskOperations19.fromAuthManager(authManager);
4626
+ if (options.grep) {
4627
+ const changes2 = await taskOps.searchChanges(options.grep, parseInt(options.limit, 10));
4628
+ if (changes2.length === 0) {
4629
+ console.log(chalk25.gray(`
4630
+ No changes found matching: "${options.grep}"
4631
+ `));
4632
+ process.exit(0);
4633
+ }
4634
+ console.log(chalk25.gray(`
4635
+ Found ${changes2.length} change(s) matching "${options.grep}":
4636
+ `));
4637
+ for (const change of changes2) {
4638
+ printChange(change, options.oneline);
4639
+ }
4640
+ process.exit(0);
4641
+ }
4642
+ if (!taskArg) {
4643
+ console.error(chalk25.red("Error: Task ID or number required"));
4644
+ console.error(chalk25.gray('Usage: vibetasks log <task-id> or vibetasks log --grep "query"'));
4645
+ process.exit(1);
4646
+ }
4647
+ let taskId = taskArg;
4648
+ if (/^\d+$/.test(taskArg)) {
4649
+ const shortIdManager = new ShortIdManager();
4650
+ const resolved = await shortIdManager.resolveId(taskArg);
4651
+ if (resolved) {
4652
+ taskId = resolved;
4653
+ } else {
4654
+ console.error(chalk25.red(`Task #${taskArg} not found`));
4655
+ console.error(chalk25.gray("Run: vibetasks list --short-ids to see available tasks"));
4656
+ process.exit(1);
4657
+ }
4658
+ }
4659
+ const task = await taskOps.getTask(taskId);
4660
+ const queryOptions = {
4661
+ limit: parseInt(options.limit, 10)
4662
+ };
4663
+ if (options.since) {
4664
+ queryOptions.since = new Date(options.since).toISOString();
4665
+ }
4666
+ if (options.until) {
4667
+ queryOptions.until = new Date(options.until).toISOString();
4668
+ }
4669
+ if (options.type) {
4670
+ queryOptions.changeTypes = options.type.split(",").map((t) => t.trim());
4671
+ }
4672
+ const changes = await taskOps.getTaskChanges(taskId, queryOptions);
4673
+ if (changes.length === 0) {
4674
+ console.log(chalk25.gray(`
4675
+ No change history found for task: ${task.title}
4676
+ `));
4677
+ process.exit(0);
4678
+ }
4679
+ console.log();
4680
+ console.log(chalk25.cyan.bold(`History for: ${task.title}`));
4681
+ console.log(chalk25.gray(`Task ID: ${taskId}`));
4682
+ console.log(chalk25.gray(`Status: ${task.status} | Changes: ${changes.length}`));
4683
+ console.log();
4684
+ for (const change of changes) {
4685
+ printChange(change, options.oneline, task.title);
4686
+ }
4687
+ console.log();
4688
+ process.exit(0);
4689
+ } catch (error) {
4690
+ if (error.message.includes("Not authenticated")) {
4691
+ console.error(chalk25.yellow("\nYou need to login first"));
4692
+ console.error(chalk25.gray("Run: vibetasks login\n"));
4693
+ } else {
4694
+ console.error(chalk25.red(`
4695
+ Error: ${error.message}
4696
+ `));
4697
+ }
4698
+ process.exit(1);
4699
+ }
4700
+ });
4701
+ function printChange(change, oneline = false, taskTitle) {
4702
+ const changeId = change.id.substring(0, 8);
4703
+ const date = new Date(change.created_at);
4704
+ const dateStr = date.toLocaleDateString();
4705
+ const timeStr = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
4706
+ const typeColors = {
4707
+ created: chalk25.green,
4708
+ status_changed: chalk25.cyan,
4709
+ title_changed: chalk25.yellow,
4710
+ context_updated: chalk25.blue,
4711
+ priority_changed: chalk25.magenta,
4712
+ subtask_done: chalk25.green,
4713
+ subtask_undone: chalk25.red,
4714
+ subtask_added: chalk25.cyan,
4715
+ archived: chalk25.gray,
4716
+ restored: chalk25.green,
4717
+ revert: chalk25.red,
4718
+ handoff: chalk25.magenta
4719
+ };
4720
+ const colorFn = typeColors[change.change_type] || chalk25.white;
4721
+ const actorIcon = change.actor_type === "ai" ? "\u{1F916}" : change.actor_type === "human" ? "\u{1F464}" : "\u2699\uFE0F";
4722
+ if (oneline) {
4723
+ const message = change.message || change.change_type;
4724
+ console.log(
4725
+ `${chalk25.yellow(changeId)} ${chalk25.gray(dateStr)} ${colorFn(change.change_type.padEnd(16))} ${actorIcon} ${message}`
4726
+ );
4727
+ } else {
4728
+ console.log(chalk25.yellow(`change ${change.id}`));
4729
+ console.log(`Actor: ${actorIcon} ${change.actor_type}`);
4730
+ console.log(`Date: ${dateStr} ${timeStr}`);
4731
+ if (change.session_id) {
4732
+ console.log(chalk25.gray(`Session: ${change.session_id.substring(0, 8)}`));
4733
+ }
4734
+ console.log();
4735
+ console.log(` ${colorFn(change.change_type)}: ${change.message || ""}`);
4736
+ if (change.old_value !== void 0 || change.new_value !== void 0) {
4737
+ if (change.field_changed === "status") {
4738
+ console.log(chalk25.gray(` ${chalk25.red(`- ${change.old_value}`)} ${chalk25.green(`+ ${change.new_value}`)}`));
4739
+ } else if (change.field_changed === "context_notes") {
4740
+ const oldVal = truncate(String(change.old_value || ""), 60);
4741
+ const newVal = truncate(String(change.new_value || ""), 60);
4742
+ if (oldVal) console.log(chalk25.red(` - ${oldVal}`));
4743
+ if (newVal) console.log(chalk25.green(` + ${newVal}`));
4744
+ } else if (change.field_changed === "subtasks") {
4745
+ const newSub = change.new_value;
4746
+ if (newSub?.title) {
4747
+ console.log(chalk25.gray(` Subtask: ${newSub.title}`));
4748
+ }
4749
+ } else if (change.old_value !== void 0 && change.new_value !== void 0) {
4750
+ console.log(chalk25.red(` - ${change.old_value}`));
4751
+ console.log(chalk25.green(` + ${change.new_value}`));
4752
+ }
4753
+ }
4754
+ console.log();
4755
+ }
4756
+ }
4757
+ function truncate(str, maxLength) {
4758
+ if (!str) return "";
4759
+ if (str.length <= maxLength) return str;
4760
+ return str.substring(0, maxLength - 3) + "...";
4761
+ }
4762
+
4763
+ // src/commands/show.ts
4764
+ import { Command as Command25 } from "commander";
4765
+ import chalk26 from "chalk";
4766
+ import { AuthManager as AuthManager24, TaskOperations as TaskOperations20 } from "@vibetasks/core";
4767
+ 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) => {
4768
+ try {
4769
+ const authManager = new AuthManager24();
4770
+ const taskOps = await TaskOperations20.fromAuthManager(authManager);
4771
+ const change = await taskOps.getChange(changeId);
4772
+ const task = await taskOps.getTask(change.task_id);
4773
+ const date = new Date(change.created_at);
4774
+ const dateStr = date.toLocaleDateString();
4775
+ const timeStr = date.toLocaleTimeString();
4776
+ const actorIcon = change.actor_type === "ai" ? "\u{1F916}" : change.actor_type === "human" ? "\u{1F464}" : "\u2699\uFE0F";
4777
+ console.log();
4778
+ console.log(chalk26.yellow(`change ${change.id}`));
4779
+ console.log(`Task: ${chalk26.cyan(task.title)}`);
4780
+ console.log(` ${chalk26.gray(change.task_id)}`);
4781
+ console.log(`Actor: ${actorIcon} ${change.actor_type}`);
4782
+ console.log(`Date: ${dateStr} ${timeStr}`);
4783
+ if (change.session_id) {
4784
+ console.log(`Session: ${chalk26.gray(change.session_id)}`);
4785
+ }
4786
+ console.log();
4787
+ console.log(chalk26.bold(`${change.change_type}`));
4788
+ if (change.message) {
4789
+ console.log(` ${change.message}`);
4790
+ }
4791
+ console.log();
4792
+ if (change.field_changed) {
4793
+ console.log(chalk26.gray(`Field: ${change.field_changed}`));
4794
+ console.log();
4795
+ if (change.old_value !== void 0) {
4796
+ const oldVal = formatValue(change.old_value);
4797
+ console.log(chalk26.red(`- ${oldVal}`));
4798
+ }
4799
+ if (change.new_value !== void 0) {
4800
+ const newVal = formatValue(change.new_value);
4801
+ console.log(chalk26.green(`+ ${newVal}`));
4802
+ }
4803
+ }
4804
+ if (change.reverted_change_id) {
4805
+ console.log();
4806
+ console.log(chalk26.gray(`Reverted change: ${change.reverted_change_id}`));
4807
+ }
4808
+ console.log();
4809
+ process.exit(0);
4810
+ } catch (error) {
4811
+ if (error.message.includes("Not authenticated")) {
4812
+ console.error(chalk26.yellow("\nYou need to login first"));
4813
+ console.error(chalk26.gray("Run: vibetasks login\n"));
4814
+ } else if (error.code === "PGRST116") {
4815
+ console.error(chalk26.red(`
4816
+ Change not found: ${changeId}
4817
+ `));
4818
+ } else {
4819
+ console.error(chalk26.red(`
4820
+ Error: ${error.message}
4821
+ `));
4822
+ }
4823
+ process.exit(1);
4824
+ }
4825
+ });
4826
+ function formatValue(value) {
4827
+ if (value === null || value === void 0) {
4828
+ return "(empty)";
4829
+ }
4830
+ if (typeof value === "object") {
4831
+ return JSON.stringify(value, null, 2);
4832
+ }
4833
+ return String(value);
4834
+ }
4835
+
4836
+ // src/commands/pause.ts
4837
+ import { Command as Command26 } from "commander";
4838
+ import chalk27 from "chalk";
4839
+ import ora16 from "ora";
4840
+ import { AuthManager as AuthManager25, TaskOperations as TaskOperations21 } from "@vibetasks/core";
4841
+ 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) => {
4842
+ const spinner = ora16();
4843
+ try {
4844
+ const authManager = new AuthManager25();
4845
+ const taskOps = await TaskOperations21.fromAuthManager(authManager);
4846
+ if (options.list) {
4847
+ spinner.start("Fetching paused tasks...");
4848
+ const allTasks = await taskOps.getTasks("all");
4849
+ const pausedTasks = allTasks.filter((t) => t.status === "paused");
4850
+ spinner.stop();
4851
+ if (pausedTasks.length === 0) {
4852
+ console.log(chalk27.yellow("\nNo paused tasks found.\n"));
4853
+ console.log(chalk27.gray('Pause a task with: vibetasks pause <task-id> -m "reason"\n'));
4854
+ process.exit(0);
4855
+ }
4856
+ console.log(chalk27.white("\nPaused Tasks:\n"));
4857
+ pausedTasks.forEach((task2, index) => {
4858
+ console.log(
4859
+ chalk27.gray(`${index + 1}. `) + chalk27.yellow("\u23F8 ") + chalk27.white(task2.title) + chalk27.gray(` [${task2.id.slice(0, 8)}]`)
4860
+ );
4861
+ if (task2.context_notes) {
4862
+ const lastLine = task2.context_notes.split("\n").pop();
4863
+ if (lastLine) {
4864
+ console.log(chalk27.gray(` \u2514\u2500 ${lastLine}`));
4865
+ }
4866
+ }
4867
+ });
4868
+ console.log(chalk27.gray(`
4869
+ Total: ${pausedTasks.length} paused task(s)
4870
+ `));
4871
+ console.log(chalk27.gray("Resume with: vibetasks resume <task-id>\n"));
4872
+ process.exit(0);
4873
+ }
4874
+ if (!taskArg) {
4875
+ console.log(chalk27.yellow("\nUsage:"));
4876
+ console.log(chalk27.gray(" vibetasks pause <task-id> # Pause a specific task"));
4877
+ console.log(chalk27.gray(" vibetasks pause --list # List paused tasks\n"));
4878
+ process.exit(1);
4879
+ }
4880
+ let taskId = taskArg;
4881
+ if (/^\d+$/.test(taskArg)) {
4882
+ const shortIdManager = new ShortIdManager();
4883
+ const resolved = await shortIdManager.resolveId(taskArg);
4884
+ if (resolved) {
4885
+ taskId = resolved;
4886
+ } else {
4887
+ console.error(chalk27.red(`Task #${taskArg} not found`));
4888
+ console.error(chalk27.gray("Run: vibetasks list --short-ids to see available tasks"));
4889
+ process.exit(1);
4890
+ }
4891
+ } else if (taskArg.length < 32) {
4892
+ const allTasks = await taskOps.getTasks("all");
4893
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskArg));
4894
+ if (!matchingTask) {
4895
+ console.error(chalk27.red(`Task not found: ${taskArg}`));
4896
+ process.exit(1);
4897
+ }
4898
+ taskId = matchingTask.id;
4899
+ }
4900
+ const task = await taskOps.getTask(taskId);
4901
+ if (task.status === "paused") {
4902
+ console.log(chalk27.yellow(`
4903
+ Task is already paused: ${task.title}
4904
+ `));
4905
+ console.log(chalk27.gray("Resume with: vibetasks resume " + taskId.slice(0, 8) + "\n"));
4906
+ process.exit(0);
4907
+ }
4908
+ if (task.status === "done" || task.status === "archived") {
4909
+ console.error(chalk27.red(`
4910
+ Cannot pause a ${task.status} task.
4911
+ `));
4912
+ process.exit(1);
4913
+ }
4914
+ spinner.start("Pausing task...");
4915
+ const pausedTask = await taskOps.pauseTask(taskId, options.message);
4916
+ spinner.succeed(chalk27.green("Task paused!"));
4917
+ console.log();
4918
+ console.log(chalk27.yellow("\u23F8 ") + chalk27.white(pausedTask.title));
4919
+ if (options.message) {
4920
+ console.log(chalk27.gray(` Reason: ${options.message}`));
4921
+ }
4922
+ console.log();
4923
+ console.log(chalk27.gray(`Resume with: vibetasks resume ${taskId.slice(0, 8)}`));
4924
+ console.log(chalk27.gray(`View paused: vibetasks pause --list`));
4925
+ console.log();
4926
+ process.exit(0);
4927
+ } catch (error) {
4928
+ spinner.fail(chalk27.red("Failed to pause task"));
4929
+ if (error.message.includes("Not authenticated")) {
4930
+ console.error(chalk27.yellow("\nYou need to login first"));
4931
+ console.error(chalk27.gray("Run: vibetasks login\n"));
4932
+ } else {
4933
+ console.error(chalk27.red(`
4934
+ Error: ${error.message}
4935
+ `));
4936
+ }
4937
+ process.exit(1);
4938
+ }
4939
+ });
4940
+
4941
+ // src/commands/resume.ts
4942
+ import { Command as Command27 } from "commander";
4943
+ import chalk28 from "chalk";
4944
+ import ora17 from "ora";
4945
+ import inquirer7 from "inquirer";
4946
+ import { AuthManager as AuthManager26, TaskOperations as TaskOperations22 } from "@vibetasks/core";
4947
+ 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) => {
4948
+ const spinner = ora17();
4949
+ try {
4950
+ const authManager = new AuthManager26();
4951
+ const taskOps = await TaskOperations22.fromAuthManager(authManager);
4952
+ const allTasks = await taskOps.getTasks("all");
4953
+ const pausedTasks = allTasks.filter((t) => t.status === "paused");
4954
+ if (pausedTasks.length === 0) {
4955
+ console.log(chalk28.yellow("\nNo paused tasks to resume.\n"));
4956
+ console.log(chalk28.gray("Pause a task with: vibetasks pause <task-id>\n"));
4957
+ process.exit(0);
4958
+ }
4959
+ let taskId;
4960
+ if (!taskArg) {
4961
+ if (pausedTasks.length === 1) {
4962
+ taskId = pausedTasks[0].id;
4963
+ console.log(chalk28.gray(`
4964
+ Auto-selecting the only paused task...
4965
+ `));
4966
+ } else {
4967
+ const { selectedTask } = await inquirer7.prompt([{
4968
+ type: "list",
4969
+ name: "selectedTask",
4970
+ message: "Pick a paused task to resume:",
4971
+ choices: pausedTasks.map((t) => ({
4972
+ name: `${chalk28.yellow("\u23F8")} ${t.title}${t.context_notes ? chalk28.gray(` - ${t.context_notes.split("\n").pop()?.slice(0, 40)}...`) : ""}`,
4973
+ value: t.id
4974
+ }))
4975
+ }]);
4976
+ taskId = selectedTask;
4977
+ }
4978
+ } else {
4979
+ taskId = taskArg;
4980
+ if (/^\d+$/.test(taskArg)) {
4981
+ const shortIdManager = new ShortIdManager();
4982
+ const resolved = await shortIdManager.resolveId(taskArg);
4983
+ if (resolved) {
4984
+ taskId = resolved;
4985
+ } else {
4986
+ console.error(chalk28.red(`Task #${taskArg} not found`));
4987
+ console.error(chalk28.gray("Run: vibetasks list --short-ids to see available tasks"));
4988
+ process.exit(1);
4989
+ }
4990
+ } else if (taskArg.length < 32) {
4991
+ const matchingTask = pausedTasks.find((t) => t.id.startsWith(taskArg)) || allTasks.find((t) => t.id.startsWith(taskArg));
4992
+ if (!matchingTask) {
4993
+ console.error(chalk28.red(`Task not found: ${taskArg}`));
4994
+ process.exit(1);
4995
+ }
4996
+ taskId = matchingTask.id;
4997
+ }
4998
+ }
4999
+ const task = await taskOps.getTask(taskId);
5000
+ if (task.status !== "paused") {
5001
+ console.log(chalk28.yellow(`
5002
+ Task is not paused (status: ${task.status}): ${task.title}
5003
+ `));
5004
+ if (task.status === "todo") {
5005
+ console.log(chalk28.gray("Start vibing with: vibetasks vibing " + taskId.slice(0, 8) + "\n"));
5006
+ }
5007
+ process.exit(0);
5008
+ }
5009
+ spinner.start("Resuming task...");
5010
+ const resumedTask = await taskOps.resumeTask(taskId);
5011
+ spinner.succeed(chalk28.green("Task resumed!"));
5012
+ console.log();
5013
+ console.log(chalk28.magenta("\u26A1 ") + chalk28.white(resumedTask.title));
5014
+ console.log(chalk28.gray(` Status: vibing`));
5015
+ if (task.context_notes) {
5016
+ console.log(chalk28.gray(` Context: ${task.context_notes.split("\n").pop()?.slice(0, 60)}...`));
5017
+ }
5018
+ console.log();
5019
+ console.log(chalk28.gray(`Complete with: vibetasks done ${taskId.slice(0, 8)}`));
5020
+ console.log(chalk28.gray(`Pause again: vibetasks pause ${taskId.slice(0, 8)}`));
5021
+ console.log();
5022
+ process.exit(0);
5023
+ } catch (error) {
5024
+ spinner.fail(chalk28.red("Failed to resume task"));
5025
+ if (error.message.includes("Not authenticated")) {
5026
+ console.error(chalk28.yellow("\nYou need to login first"));
5027
+ console.error(chalk28.gray("Run: vibetasks login\n"));
5028
+ } else {
5029
+ console.error(chalk28.red(`
5030
+ Error: ${error.message}
5031
+ `));
5032
+ }
5033
+ process.exit(1);
5034
+ }
5035
+ });
5036
+
5037
+ // src/commands/revert.ts
5038
+ import { Command as Command28 } from "commander";
5039
+ import chalk29 from "chalk";
5040
+ import ora18 from "ora";
5041
+ import inquirer8 from "inquirer";
5042
+ import { AuthManager as AuthManager27, TaskOperations as TaskOperations23 } from "@vibetasks/core";
5043
+ 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) => {
5044
+ const spinner = ora18();
5045
+ try {
5046
+ const authManager = new AuthManager27();
5047
+ const taskOps = await TaskOperations23.fromAuthManager(authManager);
5048
+ if (options.task || !changeId) {
5049
+ let taskId;
5050
+ if (options.task) {
5051
+ if (/^\d+$/.test(options.task)) {
5052
+ const shortIdManager = new ShortIdManager();
5053
+ const resolved = await shortIdManager.resolveId(options.task);
5054
+ if (resolved) {
5055
+ taskId = resolved;
5056
+ } else {
5057
+ console.error(chalk29.red(`Task #${options.task} not found`));
5058
+ process.exit(1);
5059
+ }
5060
+ } else if (options.task.length < 32) {
5061
+ const allTasks = await taskOps.getTasks("all");
5062
+ const matchingTask = allTasks.find((t) => t.id.startsWith(options.task));
5063
+ if (matchingTask) {
5064
+ taskId = matchingTask.id;
5065
+ }
5066
+ } else {
5067
+ taskId = options.task;
5068
+ }
5069
+ }
5070
+ if (!taskId) {
5071
+ console.log(chalk29.yellow("\nUsage:"));
5072
+ console.log(chalk29.gray(" vibetasks revert <change-id> # Revert specific change"));
5073
+ console.log(chalk29.gray(" vibetasks revert --task <task-id> # Pick from task changes"));
5074
+ console.log(chalk29.gray(" vibetasks log <task-id> # View changes first\n"));
5075
+ process.exit(1);
5076
+ }
5077
+ const changes = await taskOps.getTaskChanges(taskId, { limit: 20 });
5078
+ const task2 = await taskOps.getTask(taskId);
5079
+ if (changes.length === 0) {
5080
+ console.log(chalk29.yellow(`
5081
+ No changes found for task: ${task2.title}
5082
+ `));
5083
+ process.exit(0);
5084
+ }
5085
+ const revertableChanges = changes.filter((c) => c.change_type !== "created" && c.change_type !== "revert");
5086
+ if (revertableChanges.length === 0) {
5087
+ console.log(chalk29.yellow(`
5088
+ No revertable changes for task: ${task2.title}`));
5089
+ console.log(chalk29.gray("Only the initial creation exists.\n"));
5090
+ process.exit(0);
5091
+ }
5092
+ console.log(chalk29.cyan(`
5093
+ Recent changes for: ${task2.title}
5094
+ `));
5095
+ const { selectedChange } = await inquirer8.prompt([{
5096
+ type: "list",
5097
+ name: "selectedChange",
5098
+ message: "Select a change to revert:",
5099
+ choices: revertableChanges.map((c) => {
5100
+ const date = new Date(c.created_at).toLocaleDateString();
5101
+ const typeColor = getTypeColor(c.change_type);
5102
+ return {
5103
+ name: `${chalk29.gray(c.id.slice(0, 8))} ${chalk29.gray(date)} ${typeColor(c.change_type)} - ${c.message || "No message"}`,
5104
+ value: c.id
5105
+ };
5106
+ })
5107
+ }]);
5108
+ changeId = selectedChange;
5109
+ }
5110
+ spinner.start("Loading change...");
5111
+ const change = await taskOps.getChange(changeId);
5112
+ const task = await taskOps.getTask(change.task_id);
5113
+ spinner.stop();
5114
+ console.log();
5115
+ console.log(chalk29.cyan("Change to revert:"));
5116
+ console.log(chalk29.gray(` ID: ${change.id}`));
5117
+ console.log(chalk29.gray(` Task: ${task.title}`));
5118
+ console.log(chalk29.gray(` Type: ${change.change_type}`));
5119
+ console.log(chalk29.gray(` Message: ${change.message || "No message"}`));
5120
+ if (change.old_value !== void 0 || change.new_value !== void 0) {
5121
+ console.log(chalk29.gray(` Field: ${change.field_changed}`));
5122
+ if (change.old_value !== void 0) {
5123
+ console.log(chalk29.red(` Old: ${formatValue2(change.old_value)}`));
5124
+ }
5125
+ if (change.new_value !== void 0) {
5126
+ console.log(chalk29.green(` New: ${formatValue2(change.new_value)}`));
5127
+ }
5128
+ }
5129
+ console.log();
5130
+ const { confirm } = await inquirer8.prompt([{
5131
+ type: "confirm",
5132
+ name: "confirm",
5133
+ message: "Revert this change?",
5134
+ default: true
5135
+ }]);
5136
+ if (!confirm) {
5137
+ console.log(chalk29.gray("\nRevert cancelled.\n"));
5138
+ process.exit(0);
5139
+ }
5140
+ spinner.start("Reverting change...");
5141
+ const revertChange = await taskOps.revertChange(changeId, options.message);
5142
+ spinner.succeed(chalk29.green("Change reverted!"));
5143
+ console.log();
5144
+ console.log(chalk29.yellow("\u21A9 ") + chalk29.white(`Reverted: ${change.message || change.change_type}`));
5145
+ console.log(chalk29.gray(` Revert ID: ${revertChange.id.slice(0, 8)}`));
5146
+ console.log();
5147
+ console.log(chalk29.gray(`View history: vibetasks log ${task.id.slice(0, 8)}`));
5148
+ console.log();
5149
+ process.exit(0);
5150
+ } catch (error) {
5151
+ spinner.fail(chalk29.red("Failed to revert change"));
5152
+ if (error.message.includes("Not authenticated")) {
5153
+ console.error(chalk29.yellow("\nYou need to login first"));
5154
+ console.error(chalk29.gray("Run: vibetasks login\n"));
5155
+ } else if (error.code === "PGRST116") {
5156
+ console.error(chalk29.red(`
5157
+ Change not found: ${changeId}
5158
+ `));
5159
+ console.error(chalk29.gray("Use: vibetasks log <task-id> to see valid change IDs\n"));
5160
+ } else {
5161
+ console.error(chalk29.red(`
5162
+ Error: ${error.message}
5163
+ `));
5164
+ }
5165
+ process.exit(1);
5166
+ }
5167
+ });
5168
+ function getTypeColor(changeType) {
5169
+ const colors = {
5170
+ created: chalk29.green,
5171
+ status_changed: chalk29.cyan,
5172
+ title_changed: chalk29.yellow,
5173
+ context_updated: chalk29.blue,
5174
+ priority_changed: chalk29.magenta,
5175
+ subtask_done: chalk29.green,
5176
+ subtask_undone: chalk29.red,
5177
+ subtask_added: chalk29.cyan,
5178
+ archived: chalk29.gray,
5179
+ restored: chalk29.green,
5180
+ revert: chalk29.red
5181
+ };
5182
+ return colors[changeType] || chalk29.white;
5183
+ }
5184
+ function formatValue2(value) {
5185
+ if (value === null || value === void 0) {
5186
+ return "(empty)";
5187
+ }
5188
+ if (typeof value === "object") {
5189
+ return JSON.stringify(value);
5190
+ }
5191
+ const str = String(value);
5192
+ return str.length > 50 ? str.slice(0, 47) + "..." : str;
5193
+ }
5194
+
5195
+ // src/commands/blame.ts
5196
+ import { Command as Command29 } from "commander";
5197
+ import chalk30 from "chalk";
5198
+ import { AuthManager as AuthManager28, TaskOperations as TaskOperations24 } from "@vibetasks/core";
5199
+ 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) => {
5200
+ try {
5201
+ const authManager = new AuthManager28();
5202
+ const taskOps = await TaskOperations24.fromAuthManager(authManager);
5203
+ let taskId = taskArg;
5204
+ if (/^\d+$/.test(taskArg)) {
5205
+ const shortIdManager = new ShortIdManager();
5206
+ const resolved = await shortIdManager.resolveId(taskArg);
5207
+ if (resolved) {
5208
+ taskId = resolved;
5209
+ } else {
5210
+ console.error(chalk30.red(`Task #${taskArg} not found`));
5211
+ console.error(chalk30.gray("Run: vibetasks list --short-ids to see available tasks"));
5212
+ process.exit(1);
5213
+ }
5214
+ } else if (taskArg.length < 32) {
5215
+ const allTasks = await taskOps.getTasks("all", true);
5216
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskArg));
5217
+ if (!matchingTask) {
5218
+ console.error(chalk30.red(`Task not found: ${taskArg}`));
5219
+ process.exit(1);
5220
+ }
5221
+ taskId = matchingTask.id;
5222
+ }
5223
+ const task = await taskOps.getTask(taskId);
5224
+ const changes = await taskOps.getTaskChanges(taskId, { limit: 100 });
5225
+ if (changes.length === 0) {
5226
+ console.log(chalk30.yellow(`
5227
+ No change history for task: ${task.title}
5228
+ `));
5229
+ process.exit(0);
5230
+ }
5231
+ const blameMap = {};
5232
+ const currentValues = {
5233
+ status: task.status,
5234
+ title: task.title,
5235
+ priority: task.priority,
5236
+ context_notes: task.context_notes,
5237
+ description: task.description
5238
+ };
5239
+ for (const change of changes) {
5240
+ if (!change.field_changed) continue;
5241
+ if (options.field && change.field_changed !== options.field) continue;
5242
+ if (!blameMap[change.field_changed]) {
5243
+ blameMap[change.field_changed] = {
5244
+ currentValue: currentValues[change.field_changed],
5245
+ changes: []
5246
+ };
5247
+ }
5248
+ blameMap[change.field_changed].changes.push({
5249
+ changeId: change.id,
5250
+ actor: change.session_id?.slice(0, 8) || "unknown",
5251
+ actorType: change.actor_type,
5252
+ date: new Date(change.created_at),
5253
+ oldValue: change.old_value,
5254
+ newValue: change.new_value,
5255
+ message: change.message || change.change_type
5256
+ });
5257
+ }
5258
+ console.log();
5259
+ console.log(chalk30.cyan.bold(`Blame for: ${task.title}`));
5260
+ console.log(chalk30.gray(`Task ID: ${taskId}`));
5261
+ console.log();
5262
+ const fields = options.field ? [options.field] : Object.keys(blameMap).sort();
5263
+ if (fields.length === 0) {
5264
+ console.log(chalk30.yellow("No field changes tracked."));
5265
+ console.log(chalk30.gray("Only status, title, priority, context_notes changes are tracked.\n"));
5266
+ process.exit(0);
5267
+ }
5268
+ for (const field of fields) {
5269
+ const blame = blameMap[field];
5270
+ if (!blame) continue;
5271
+ console.log(chalk30.white.bold(`${field}:`));
5272
+ console.log(chalk30.gray(` Current: ${formatValue3(blame.currentValue)}`));
5273
+ console.log();
5274
+ for (const change of blame.changes.slice(0, 5)) {
5275
+ const actorIcon = change.actorType === "ai" ? "\u{1F916}" : change.actorType === "human" ? "\u{1F464}" : "\u2699\uFE0F";
5276
+ const dateStr = change.date.toLocaleDateString();
5277
+ const timeStr = change.date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
5278
+ console.log(
5279
+ chalk30.gray(` ${chalk30.yellow(change.changeId.slice(0, 8))} `) + chalk30.gray(`${dateStr} ${timeStr} `) + `${actorIcon} ` + chalk30.white(change.message)
5280
+ );
5281
+ if (change.oldValue !== void 0 || change.newValue !== void 0) {
5282
+ if (field === "status") {
5283
+ console.log(chalk30.gray(` ${chalk30.red(String(change.oldValue))} \u2192 ${chalk30.green(String(change.newValue))}`));
5284
+ } else {
5285
+ const oldStr = formatValue3(change.oldValue);
5286
+ const newStr = formatValue3(change.newValue);
5287
+ if (oldStr) console.log(chalk30.red(` - ${oldStr}`));
5288
+ if (newStr) console.log(chalk30.green(` + ${newStr}`));
5289
+ }
5290
+ }
5291
+ }
5292
+ if (blame.changes.length > 5) {
5293
+ console.log(chalk30.gray(` ... and ${blame.changes.length - 5} more changes`));
5294
+ }
5295
+ console.log();
5296
+ }
5297
+ const totalChanges = Object.values(blameMap).reduce((sum, b) => sum + b.changes.length, 0);
5298
+ const aiChanges = changes.filter((c) => c.actor_type === "ai").length;
5299
+ const humanChanges = changes.filter((c) => c.actor_type === "human").length;
5300
+ console.log(chalk30.gray("\u2500".repeat(50)));
5301
+ console.log(chalk30.gray(`Total changes: ${totalChanges} | \u{1F916} AI: ${aiChanges} | \u{1F464} Human: ${humanChanges}`));
5302
+ console.log(chalk30.gray(`
5303
+ Full history: vibetasks log ${taskId.slice(0, 8)}`));
5304
+ console.log();
5305
+ process.exit(0);
5306
+ } catch (error) {
5307
+ if (error.message.includes("Not authenticated")) {
5308
+ console.error(chalk30.yellow("\nYou need to login first"));
5309
+ console.error(chalk30.gray("Run: vibetasks login\n"));
5310
+ } else {
5311
+ console.error(chalk30.red(`
5312
+ Error: ${error.message}
5313
+ `));
5314
+ }
5315
+ process.exit(1);
5316
+ }
5317
+ });
5318
+ function formatValue3(value) {
5319
+ if (value === null || value === void 0) {
5320
+ return "(empty)";
5321
+ }
5322
+ if (typeof value === "object") {
5323
+ return JSON.stringify(value);
5324
+ }
5325
+ const str = String(value);
5326
+ return str.length > 60 ? str.slice(0, 57) + "..." : str;
5327
+ }
5328
+
3838
5329
  // bin/vibetasks.ts
3839
5330
  var require2 = createRequire(import.meta.url);
3840
5331
  var pkg = require2("../../package.json");
3841
- var program = new Command21();
5332
+ var program = new Command30();
3842
5333
  program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version(pkg.version);
3843
5334
  program.addCommand(setupCommand);
3844
5335
  program.addCommand(loginCommand);
@@ -3861,4 +5352,13 @@ program.addCommand(sessionCommand);
3861
5352
  program.addCommand(subtaskCommand);
3862
5353
  program.addCommand(errorCommand);
3863
5354
  program.addCommand(inboxCommand);
5355
+ program.addCommand(handoffCommand);
5356
+ program.addCommand(parsePrdCommand);
5357
+ program.addCommand(checkpointCommand);
5358
+ program.addCommand(logCommand);
5359
+ program.addCommand(showCommand);
5360
+ program.addCommand(pauseCommand);
5361
+ program.addCommand(resumeCommand);
5362
+ program.addCommand(revertCommand);
5363
+ program.addCommand(blameCommand);
3864
5364
  program.parse();