@vibetasks/cli 0.4.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin/vibetasks.js +597 -14
  2. package/package.json +3 -3
@@ -11,7 +11,7 @@ import {
11
11
  } from "../chunk-2KRLRG4G.js";
12
12
 
13
13
  // bin/vibetasks.ts
14
- import { Command as Command16 } from "commander";
14
+ import { Command as Command18 } from "commander";
15
15
  import { createRequire } from "module";
16
16
 
17
17
  // src/commands/login.ts
@@ -485,7 +485,227 @@ function parseDate(dateStr) {
485
485
 
486
486
  // src/commands/add.ts
487
487
  import { detectProject } from "@vibetasks/shared/utils/project-detector";
488
- 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("--project <name>", "Override auto-detected project").option("-e, --energy <level>", "Energy required: low, medium, high").action(async (title, options) => {
488
+ import { randomUUID as randomUUID2 } from "crypto";
489
+
490
+ // src/utils/session-manager.ts
491
+ import { writeFile, readFile, mkdir } from "fs/promises";
492
+ import { join } from "path";
493
+ import { homedir } from "os";
494
+ import { randomUUID } from "crypto";
495
+ var ADJECTIVES = [
496
+ "swift",
497
+ "calm",
498
+ "bold",
499
+ "warm",
500
+ "cool",
501
+ "bright",
502
+ "dark",
503
+ "quick",
504
+ "slow",
505
+ "wise",
506
+ "keen",
507
+ "soft",
508
+ "loud",
509
+ "deep",
510
+ "high",
511
+ "low"
512
+ ];
513
+ var NOUNS = [
514
+ "fox",
515
+ "owl",
516
+ "bear",
517
+ "wolf",
518
+ "hawk",
519
+ "moon",
520
+ "star",
521
+ "sun",
522
+ "tree",
523
+ "wave",
524
+ "wind",
525
+ "fire",
526
+ "snow",
527
+ "rain",
528
+ "leaf",
529
+ "stone"
530
+ ];
531
+ var SessionManager = class {
532
+ storePath;
533
+ store = null;
534
+ constructor() {
535
+ this.storePath = join(homedir(), ".vibetasks", "sessions.json");
536
+ }
537
+ /**
538
+ * Generate a memorable session ID like "swift-fox-42"
539
+ */
540
+ generateSessionId() {
541
+ const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
542
+ const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
543
+ const num = Math.floor(Math.random() * 100);
544
+ return `${adj}-${noun}-${num}`;
545
+ }
546
+ /**
547
+ * Load sessions from disk
548
+ */
549
+ async load() {
550
+ if (this.store) return this.store;
551
+ try {
552
+ const data = await readFile(this.storePath, "utf-8");
553
+ this.store = JSON.parse(data);
554
+ return this.store;
555
+ } catch {
556
+ this.store = { sessions: [] };
557
+ return this.store;
558
+ }
559
+ }
560
+ /**
561
+ * Save sessions to disk
562
+ */
563
+ async save() {
564
+ const dir = join(homedir(), ".vibetasks");
565
+ await mkdir(dir, { recursive: true });
566
+ await writeFile(this.storePath, JSON.stringify(this.store, null, 2));
567
+ }
568
+ /**
569
+ * Start a new session
570
+ */
571
+ async startSession(project) {
572
+ const store = await this.load();
573
+ if (store.currentSession) {
574
+ await this.endSession();
575
+ }
576
+ const session = {
577
+ id: this.generateSessionId(),
578
+ fullId: randomUUID(),
579
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
580
+ project,
581
+ actions: []
582
+ };
583
+ store.sessions.push(session);
584
+ store.currentSession = session.id;
585
+ await this.save();
586
+ return session;
587
+ }
588
+ /**
589
+ * Get current session (or start one if none exists)
590
+ */
591
+ async getCurrentSession() {
592
+ const store = await this.load();
593
+ if (!store.currentSession) {
594
+ return null;
595
+ }
596
+ return store.sessions.find((s) => s.id === store.currentSession) || null;
597
+ }
598
+ /**
599
+ * Get or create current session
600
+ */
601
+ async getOrCreateSession(project) {
602
+ const current = await this.getCurrentSession();
603
+ if (current) return current;
604
+ return this.startSession(project);
605
+ }
606
+ /**
607
+ * Log an action in the current session
608
+ */
609
+ async logAction(action) {
610
+ const store = await this.load();
611
+ const session = store.sessions.find((s) => s.id === store.currentSession);
612
+ if (!session) {
613
+ const newSession = await this.startSession();
614
+ newSession.actions.push({
615
+ ...action,
616
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
617
+ });
618
+ } else {
619
+ session.actions.push({
620
+ ...action,
621
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
622
+ });
623
+ }
624
+ await this.save();
625
+ }
626
+ /**
627
+ * End the current session
628
+ */
629
+ async endSession(summary) {
630
+ const store = await this.load();
631
+ const session = store.sessions.find((s) => s.id === store.currentSession);
632
+ if (session) {
633
+ session.endedAt = (/* @__PURE__ */ new Date()).toISOString();
634
+ session.summary = summary;
635
+ store.currentSession = void 0;
636
+ await this.save();
637
+ }
638
+ return session || null;
639
+ }
640
+ /**
641
+ * Get a session by ID (partial match supported)
642
+ */
643
+ async getSession(id) {
644
+ const store = await this.load();
645
+ let session = store.sessions.find((s) => s.id === id);
646
+ if (session) return session;
647
+ session = store.sessions.find((s) => s.id.includes(id) || s.fullId.startsWith(id));
648
+ return session || null;
649
+ }
650
+ /**
651
+ * Get recent sessions
652
+ */
653
+ async getRecentSessions(limit = 10) {
654
+ const store = await this.load();
655
+ return store.sessions.slice(-limit).reverse();
656
+ }
657
+ /**
658
+ * Get session summary for handoff to next AI
659
+ */
660
+ async getSessionHandoff(sessionId) {
661
+ const session = await this.getSession(sessionId);
662
+ if (!session) {
663
+ return `Session "${sessionId}" not found.`;
664
+ }
665
+ const lines = [
666
+ `## Session: ${session.id}`,
667
+ `Started: ${session.startedAt}`,
668
+ session.endedAt ? `Ended: ${session.endedAt}` : `Status: ACTIVE`,
669
+ session.project ? `Project: ${session.project}` : "",
670
+ "",
671
+ `### Actions (${session.actions.length}):`
672
+ ];
673
+ for (const action of session.actions) {
674
+ const time = new Date(action.timestamp).toLocaleTimeString();
675
+ switch (action.type) {
676
+ case "task_created":
677
+ lines.push(`- [${time}] Created task: "${action.taskTitle}" (${action.taskId})`);
678
+ break;
679
+ case "task_completed":
680
+ lines.push(`- [${time}] Completed: "${action.taskTitle}"`);
681
+ break;
682
+ case "task_updated":
683
+ lines.push(`- [${time}] Updated: "${action.taskTitle}"`);
684
+ break;
685
+ case "file_modified":
686
+ lines.push(`- [${time}] Modified: ${action.filePath}`);
687
+ break;
688
+ case "note":
689
+ lines.push(`- [${time}] Note: ${action.description}`);
690
+ break;
691
+ }
692
+ }
693
+ if (session.summary) {
694
+ lines.push("", "### Summary:", session.summary);
695
+ }
696
+ return lines.filter(Boolean).join("\n");
697
+ }
698
+ };
699
+ var sessionManager = null;
700
+ function getSessionManager() {
701
+ if (!sessionManager) {
702
+ sessionManager = new SessionManager();
703
+ }
704
+ return sessionManager;
705
+ }
706
+
707
+ // src/commands/add.ts
708
+ 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) => {
489
709
  const spinner = ora3("Creating task...").start();
490
710
  try {
491
711
  const authManager = new AuthManager3();
@@ -523,6 +743,11 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
523
743
  throw new Error(`Invalid energy level. Must be one of: ${validEnergy.join(", ")}`);
524
744
  }
525
745
  }
746
+ const subtasksJson = options.subtasks?.map((subtaskTitle) => ({
747
+ id: randomUUID2(),
748
+ title: subtaskTitle,
749
+ done: false
750
+ })) || [];
526
751
  const task = await taskOps.createTask({
527
752
  title,
528
753
  notes: options.notes,
@@ -535,7 +760,8 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
535
760
  // ai or human
536
761
  status: createdBy === "ai" ? "vibing" : "todo",
537
762
  // AI tasks start vibing
538
- energy_required: options.energy
763
+ energy_required: options.energy,
764
+ subtasks_json: subtasksJson
539
765
  });
540
766
  if (options.tags && options.tags.length > 0) {
541
767
  const tagIds = [];
@@ -546,6 +772,15 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
546
772
  await taskOps.linkTaskTags(task.id, tagIds);
547
773
  }
548
774
  spinner.succeed(chalk3.green("Task created!"));
775
+ if (createdBy === "ai") {
776
+ const sessionManager2 = getSessionManager();
777
+ await sessionManager2.logAction({
778
+ type: "task_created",
779
+ description: `Created task: "${title}"`,
780
+ taskId: task.id,
781
+ taskTitle: title
782
+ });
783
+ }
549
784
  console.log();
550
785
  if (projectTag) {
551
786
  console.log(chalk3.gray(`\u{1F4C1} Project: ${chalk3.white(projectTag)}`));
@@ -577,6 +812,12 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
577
812
  if (options.tags && options.tags.length > 0) {
578
813
  console.log(chalk3.magenta(`Tags: ${options.tags.join(", ")}`));
579
814
  }
815
+ if (subtasksJson.length > 0) {
816
+ console.log(chalk3.cyan(`Subtasks: ${subtasksJson.length}`));
817
+ subtasksJson.forEach((st, i) => {
818
+ console.log(chalk3.gray(` ${i + 1}. ${st.title}`));
819
+ });
820
+ }
580
821
  console.log();
581
822
  process.exit(0);
582
823
  } catch (error) {
@@ -601,7 +842,81 @@ import { Command as Command3 } from "commander";
601
842
  import chalk4 from "chalk";
602
843
  import Table from "cli-table3";
603
844
  import { AuthManager as AuthManager4, TaskOperations as TaskOperations2 } from "@vibetasks/core";
604
- 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").action(async (filter, options) => {
845
+
846
+ // src/utils/short-ids.ts
847
+ import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
848
+ import { join as join2 } from "path";
849
+ import { homedir as homedir2 } from "os";
850
+ var ShortIdManager = class {
851
+ storePath;
852
+ maxIds = 99;
853
+ constructor() {
854
+ this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
855
+ }
856
+ /**
857
+ * Save mappings from a list of task IDs
858
+ * Short IDs are assigned 1, 2, 3... based on array order
859
+ */
860
+ async saveMappings(taskIds) {
861
+ const mappings = taskIds.slice(0, this.maxIds).map((fullId, index) => ({
862
+ shortId: index + 1,
863
+ fullId,
864
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
865
+ }));
866
+ const store = {
867
+ mappings,
868
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
869
+ };
870
+ const dir = join2(homedir2(), ".vibetasks");
871
+ await mkdir2(dir, { recursive: true });
872
+ await writeFile2(this.storePath, JSON.stringify(store, null, 2));
873
+ }
874
+ /**
875
+ * Get the full UUID for a short ID
876
+ */
877
+ async getFullId(shortId) {
878
+ try {
879
+ const content = await readFile2(this.storePath, "utf-8");
880
+ const store = JSON.parse(content);
881
+ const mapping = store.mappings.find((m) => m.shortId === shortId);
882
+ return mapping?.fullId || null;
883
+ } catch {
884
+ return null;
885
+ }
886
+ }
887
+ /**
888
+ * Get the short ID for a full UUID (if it exists in current mappings)
889
+ */
890
+ async getShortId(fullId) {
891
+ try {
892
+ const content = await readFile2(this.storePath, "utf-8");
893
+ const store = JSON.parse(content);
894
+ const mapping = store.mappings.find((m) => m.fullId === fullId);
895
+ return mapping?.shortId || null;
896
+ } catch {
897
+ return null;
898
+ }
899
+ }
900
+ /**
901
+ * Resolve an ID that could be either short (1-99) or full UUID
902
+ */
903
+ async resolveId(idOrShortId) {
904
+ const shortId = parseInt(idOrShortId, 10);
905
+ if (!isNaN(shortId) && shortId >= 1 && shortId <= this.maxIds) {
906
+ const fullId = await this.getFullId(shortId);
907
+ if (fullId) {
908
+ return fullId;
909
+ }
910
+ throw new Error(
911
+ `Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
912
+ );
913
+ }
914
+ return idOrShortId;
915
+ }
916
+ };
917
+
918
+ // src/commands/list.ts
919
+ 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("--short-ids", "Show short numeric IDs (1-99) for easy reference").action(async (filter, options) => {
605
920
  try {
606
921
  const validFilters = ["all", "today", "upcoming", "completed"];
607
922
  if (!validFilters.includes(filter)) {
@@ -645,8 +960,18 @@ ${filterMessages[filter] || "No tasks found"}.
645
960
  }
646
961
  const limit = parseInt(options.limit, 10);
647
962
  const displayTasks = tasks.slice(0, limit);
963
+ const shortIdManager = new ShortIdManager();
964
+ if (options.shortIds) {
965
+ await shortIdManager.saveMappings(displayTasks.map((t) => t.id));
966
+ }
648
967
  const table = new Table({
649
- head: [
968
+ head: options.shortIds ? [
969
+ chalk4.cyan("#"),
970
+ chalk4.cyan("Status"),
971
+ chalk4.cyan("Title"),
972
+ chalk4.cyan("Progress"),
973
+ chalk4.cyan("Due")
974
+ ] : [
650
975
  chalk4.cyan("ID"),
651
976
  chalk4.cyan("Status"),
652
977
  chalk4.cyan("Title"),
@@ -654,10 +979,10 @@ ${filterMessages[filter] || "No tasks found"}.
654
979
  chalk4.cyan("Priority"),
655
980
  chalk4.cyan("Due")
656
981
  ],
657
- colWidths: [10, 10, 35, 15, 10, 12],
982
+ colWidths: options.shortIds ? [5, 10, 40, 12, 12] : [10, 10, 35, 15, 10, 12],
658
983
  wordWrap: true
659
984
  });
660
- displayTasks.forEach((task) => {
985
+ displayTasks.forEach((task, index) => {
661
986
  const priorityColors = {
662
987
  high: chalk4.red,
663
988
  medium: chalk4.yellow,
@@ -676,6 +1001,7 @@ ${filterMessages[filter] || "No tasks found"}.
676
1001
  };
677
1002
  const priorityColor = priorityColors[task.priority || "none"];
678
1003
  const statusColor = statusColors[task.status || "todo"];
1004
+ const shortId = chalk4.yellow.bold(`${index + 1}`);
679
1005
  const id = task.id.substring(0, 8);
680
1006
  const status = statusColor(`${statusEmojis[task.status || "todo"]} ${task.status || "todo"}`);
681
1007
  const title = task.status === "done" ? chalk4.strikethrough(task.title) : task.title;
@@ -683,7 +1009,15 @@ ${filterMessages[filter] || "No tasks found"}.
683
1009
  const project = task.project_tag ? chalk4.blue(task.project_tag) : chalk4.gray("-");
684
1010
  const priority = priorityColor(task.priority || "none");
685
1011
  const dueDate = task.due_date ? task.due_date.split("T")[0] : chalk4.gray("-");
686
- table.push([id, status, titleWithAI, project, priority, dueDate]);
1012
+ const subtasks = task.subtasks_json || task.subtasks || [];
1013
+ const doneCount = subtasks.filter((s) => s.done).length;
1014
+ const totalCount = subtasks.length;
1015
+ const progress = totalCount > 0 ? chalk4.cyan(`${doneCount}/${totalCount}`) : chalk4.gray("-");
1016
+ if (options.shortIds) {
1017
+ table.push([shortId, status, titleWithAI, progress, dueDate]);
1018
+ } else {
1019
+ table.push([id, status, titleWithAI, project, priority, dueDate]);
1020
+ }
687
1021
  });
688
1022
  console.log("\n" + table.toString());
689
1023
  const filterLabels = {
@@ -697,6 +1031,10 @@ Total: ${tasks.length} ${filterLabels[filter]} task${tasks.length === 1 ? "" : "
697
1031
  if (tasks.length > limit) {
698
1032
  console.log(chalk4.yellow(`Showing ${limit} of ${tasks.length} tasks. Use --limit to show more.`));
699
1033
  }
1034
+ if (options.shortIds) {
1035
+ console.log(chalk4.gray(`
1036
+ Use: vibetasks vibing <#> to start working on a task`));
1037
+ }
700
1038
  console.log();
701
1039
  process.exit(0);
702
1040
  } catch (error) {
@@ -717,15 +1055,16 @@ import { Command as Command4 } from "commander";
717
1055
  import ora4 from "ora";
718
1056
  import chalk5 from "chalk";
719
1057
  import { AuthManager as AuthManager5, TaskOperations as TaskOperations3 } from "@vibetasks/core";
720
- var doneCommand = new Command4("done").description("Mark a task as complete").argument("<id>", "Task ID (full or first 8 characters)").action(async (id) => {
1058
+ var doneCommand = new Command4("done").description("Mark a task as complete").argument("<id>", "Task ID, short # (1-99), or first 8 characters").action(async (id) => {
721
1059
  const spinner = ora4("Completing task...").start();
722
1060
  try {
723
1061
  const authManager = new AuthManager5();
724
1062
  const taskOps = await TaskOperations3.fromAuthManager(authManager);
725
- let taskId = id;
726
- if (id.length < 32) {
1063
+ const shortIdManager = new ShortIdManager();
1064
+ let taskId = await shortIdManager.resolveId(id);
1065
+ if (taskId.length < 32) {
727
1066
  const allTasks = await taskOps.getTasks("all");
728
- const matchingTask = allTasks.find((t) => t.id.startsWith(id));
1067
+ const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
729
1068
  if (!matchingTask) {
730
1069
  spinner.fail(chalk5.red("Task not found"));
731
1070
  console.error(chalk5.gray(`
@@ -736,6 +1075,13 @@ No task found with ID starting with: ${id}
736
1075
  taskId = matchingTask.id;
737
1076
  }
738
1077
  const task = await taskOps.completeTask(taskId);
1078
+ const sessionManager2 = getSessionManager();
1079
+ await sessionManager2.logAction({
1080
+ type: "task_completed",
1081
+ description: `Completed: "${task.title}"`,
1082
+ taskId: task.id,
1083
+ taskTitle: task.title
1084
+ });
739
1085
  spinner.succeed(chalk5.green("Task completed!"));
740
1086
  console.log(chalk5.gray(`
741
1087
  \u2713 ${task.title}`));
@@ -763,11 +1109,16 @@ import ora5 from "ora";
763
1109
  import inquirer2 from "inquirer";
764
1110
  import { AuthManager as AuthManager6, TaskOperations as TaskOperations4 } from "@vibetasks/core";
765
1111
  var WIP_LIMIT = 3;
766
- var vibingCommand = new Command5("vibing").alias("start").alias("v").description("Start working on a task (move to vibing status)").argument("[task-id]", "Task ID to start vibing on").option("-p, --pick", "Pick from todo tasks interactively").action(async (taskId, options) => {
1112
+ 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) => {
767
1113
  const spinner = ora5();
768
1114
  try {
769
1115
  const authManager = new AuthManager6();
770
1116
  const taskOps = await TaskOperations4.fromAuthManager(authManager);
1117
+ const shortIdManager = new ShortIdManager();
1118
+ let taskId;
1119
+ if (taskIdInput) {
1120
+ taskId = await shortIdManager.resolveId(taskIdInput);
1121
+ }
771
1122
  const allTasks = await taskOps.getTasks("all");
772
1123
  const vibingTasks = allTasks.filter((t) => t.status === "vibing" && !t.completed);
773
1124
  if (vibingTasks.length >= WIP_LIMIT) {
@@ -2732,10 +3083,240 @@ function getTagColor2(category) {
2732
3083
  return colors[category] || "#FF6B6B";
2733
3084
  }
2734
3085
 
3086
+ // src/commands/next.ts
3087
+ import { Command as Command16 } from "commander";
3088
+ import chalk17 from "chalk";
3089
+ import { AuthManager as AuthManager16, TaskOperations as TaskOperations12 } from "@vibetasks/core";
3090
+ var PRIORITY_ORDER = { high: 0, medium: 1, low: 2, none: 3 };
3091
+ 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) => {
3092
+ try {
3093
+ const authManager = new AuthManager16();
3094
+ const taskOps = await TaskOperations12.fromAuthManager(authManager);
3095
+ let tasks = await taskOps.getTasks("all");
3096
+ tasks = tasks.filter((t) => t.status !== "done" && t.status !== "archived" && !t.completed);
3097
+ if (options.project) {
3098
+ tasks = tasks.filter((t) => t.project_tag === options.project);
3099
+ }
3100
+ tasks.sort((a, b) => {
3101
+ const priorityDiff = (PRIORITY_ORDER[a.priority || "none"] || 3) - (PRIORITY_ORDER[b.priority || "none"] || 3);
3102
+ if (priorityDiff !== 0) return priorityDiff;
3103
+ if (a.status === "vibing" && b.status !== "vibing") return -1;
3104
+ if (b.status === "vibing" && a.status !== "vibing") return 1;
3105
+ return (a.position || 0) - (b.position || 0);
3106
+ });
3107
+ if (tasks.length === 0) {
3108
+ console.log(chalk17.gray("\n\u{1F389} No tasks in queue! You're all caught up.\n"));
3109
+ process.exit(0);
3110
+ }
3111
+ const nextTask = tasks[0];
3112
+ if (options.start && nextTask.status !== "vibing") {
3113
+ await taskOps.updateTaskStatus(nextTask.id, "vibing");
3114
+ console.log(chalk17.green(`
3115
+ \u26A1 Started vibing on task!
3116
+ `));
3117
+ }
3118
+ const priorityColors = {
3119
+ high: chalk17.red,
3120
+ medium: chalk17.yellow,
3121
+ low: chalk17.blue,
3122
+ none: chalk17.gray
3123
+ };
3124
+ const priorityEmojis = {
3125
+ high: "\u{1F534}",
3126
+ medium: "\u{1F7E1}",
3127
+ low: "\u{1F535}",
3128
+ none: "\u26AA"
3129
+ };
3130
+ const statusColors = {
3131
+ todo: chalk17.gray,
3132
+ vibing: chalk17.magenta,
3133
+ done: chalk17.green
3134
+ };
3135
+ const priority = nextTask.priority || "none";
3136
+ const priorityColor = priorityColors[priority];
3137
+ const priorityEmoji = priorityEmojis[priority];
3138
+ const statusColor = statusColors[nextTask.status || "todo"];
3139
+ const shortIdManager = new ShortIdManager();
3140
+ await shortIdManager.saveMappings([nextTask.id]);
3141
+ console.log("\n" + chalk17.bold("\u2501".repeat(50)));
3142
+ console.log(chalk17.bold.cyan("\n\u{1F4CD} NEXT UP:\n"));
3143
+ console.log(chalk17.bold.white(` ${nextTask.title}`));
3144
+ console.log();
3145
+ console.log(` ${priorityEmoji} Priority: ${priorityColor(priority.charAt(0).toUpperCase() + priority.slice(1))}`);
3146
+ console.log(` \u{1F4CA} Status: ${statusColor(nextTask.status || "todo")}`);
3147
+ if (nextTask.project_tag) {
3148
+ console.log(` \u{1F4C1} Project: ${chalk17.blue(nextTask.project_tag)}`);
3149
+ }
3150
+ if (nextTask.due_date) {
3151
+ const dueDate = new Date(nextTask.due_date);
3152
+ const today = /* @__PURE__ */ new Date();
3153
+ const isOverdue = dueDate < today;
3154
+ const dateStr = nextTask.due_date.split("T")[0];
3155
+ console.log(` \u{1F4C5} Due: ${isOverdue ? chalk17.red(dateStr + " (OVERDUE)") : chalk17.gray(dateStr)}`);
3156
+ }
3157
+ if (nextTask.context_notes) {
3158
+ console.log(`
3159
+ ${chalk17.blue("\u{1F4AC} Context:")}`);
3160
+ console.log(chalk17.gray(` ${nextTask.context_notes}`));
3161
+ }
3162
+ const subtasks = nextTask.subtasks_json || nextTask.subtasks || [];
3163
+ if (subtasks.length > 0) {
3164
+ const doneCount = subtasks.filter((s) => s.done).length;
3165
+ console.log(`
3166
+ ${chalk17.cyan("\u2713 Subtasks:")} ${doneCount}/${subtasks.length} done`);
3167
+ subtasks.slice(0, 5).forEach((s) => {
3168
+ const icon = s.done ? chalk17.green("\u2713") : chalk17.gray("\u25CB");
3169
+ const text = s.done ? chalk17.strikethrough.gray(s.title) : s.title;
3170
+ console.log(` ${icon} ${text}`);
3171
+ });
3172
+ if (subtasks.length > 5) {
3173
+ console.log(chalk17.gray(` ... and ${subtasks.length - 5} more`));
3174
+ }
3175
+ }
3176
+ console.log("\n" + chalk17.bold("\u2501".repeat(50)));
3177
+ console.log(chalk17.gray("\n Quick actions:"));
3178
+ if (nextTask.status !== "vibing") {
3179
+ console.log(chalk17.gray(` \u2022 ${chalk17.cyan("vibetasks next --start")} - Start vibing on this task`));
3180
+ }
3181
+ console.log(chalk17.gray(` \u2022 ${chalk17.cyan("vibetasks vibing 1")} - Start working (short ID #1)`));
3182
+ console.log(chalk17.gray(` \u2022 ${chalk17.cyan("vibetasks done 1")} - Mark as done`));
3183
+ console.log();
3184
+ process.exit(0);
3185
+ } catch (error) {
3186
+ if (error.message.includes("Not authenticated")) {
3187
+ console.error(chalk17.yellow("\n\u26A0\uFE0F You need to login first"));
3188
+ console.error(chalk17.gray("Run: vibetasks login\n"));
3189
+ } else {
3190
+ console.error(chalk17.red(`
3191
+ Error: ${error.message}
3192
+ `));
3193
+ }
3194
+ process.exit(1);
3195
+ }
3196
+ });
3197
+
3198
+ // src/commands/session.ts
3199
+ import { Command as Command17 } from "commander";
3200
+ import chalk18 from "chalk";
3201
+ import { detectProject as detectProject5 } from "@vibetasks/shared/utils/project-detector";
3202
+ var sessionCommand = new Command17("session").description("Manage AI sessions for crash recovery and handoffs").addCommand(
3203
+ new Command17("start").description("Start a new session").action(async () => {
3204
+ const sessionManager2 = getSessionManager();
3205
+ const project = await detectProject5();
3206
+ const session = await sessionManager2.startSession(project?.name);
3207
+ console.log(chalk18.green("\u2713 Session started"));
3208
+ console.log();
3209
+ console.log(chalk18.cyan("Session ID:"), chalk18.bold(session.id));
3210
+ console.log(chalk18.gray("Full ID:"), session.fullId.slice(0, 8));
3211
+ if (session.project) {
3212
+ console.log(chalk18.gray("Project:"), session.project);
3213
+ }
3214
+ console.log();
3215
+ console.log(chalk18.yellow("Tip:"), "If the AI crashes, tell the next one:");
3216
+ console.log(chalk18.white(` "Continue from session ${session.id}"`));
3217
+ })
3218
+ ).addCommand(
3219
+ new Command17("current").alias("status").description("Show current session status").action(async () => {
3220
+ const sessionManager2 = getSessionManager();
3221
+ const session = await sessionManager2.getCurrentSession();
3222
+ if (!session) {
3223
+ console.log(chalk18.yellow("No active session"));
3224
+ console.log(chalk18.gray("Start one with: vibetasks session start"));
3225
+ return;
3226
+ }
3227
+ console.log(chalk18.cyan("Current Session:"), chalk18.bold(session.id));
3228
+ console.log(chalk18.gray("Started:"), new Date(session.startedAt).toLocaleString());
3229
+ if (session.project) {
3230
+ console.log(chalk18.gray("Project:"), session.project);
3231
+ }
3232
+ console.log(chalk18.gray("Actions:"), session.actions.length);
3233
+ console.log();
3234
+ if (session.actions.length > 0) {
3235
+ console.log(chalk18.cyan("Recent actions:"));
3236
+ const recent = session.actions.slice(-5);
3237
+ for (const action of recent) {
3238
+ const time = new Date(action.timestamp).toLocaleTimeString();
3239
+ const icon = {
3240
+ task_created: "\u2795",
3241
+ task_completed: "\u2713",
3242
+ task_updated: "\u{1F4DD}",
3243
+ file_modified: "\u{1F4C4}",
3244
+ note: "\u{1F4AC}"
3245
+ }[action.type];
3246
+ console.log(chalk18.gray(` ${icon} [${time}]`), action.description);
3247
+ }
3248
+ if (session.actions.length > 5) {
3249
+ console.log(chalk18.gray(` ... and ${session.actions.length - 5} more`));
3250
+ }
3251
+ }
3252
+ })
3253
+ ).addCommand(
3254
+ new Command17("end").description("End the current session").option("-s, --summary <text>", "Add a summary of what was done").action(async (options) => {
3255
+ const sessionManager2 = getSessionManager();
3256
+ const session = await sessionManager2.endSession(options.summary);
3257
+ if (!session) {
3258
+ console.log(chalk18.yellow("No active session to end"));
3259
+ return;
3260
+ }
3261
+ console.log(chalk18.green("\u2713 Session ended:"), chalk18.bold(session.id));
3262
+ console.log(chalk18.gray("Duration:"), formatDuration(session.startedAt, session.endedAt));
3263
+ console.log(chalk18.gray("Actions:"), session.actions.length);
3264
+ if (options.summary) {
3265
+ console.log(chalk18.gray("Summary:"), options.summary);
3266
+ }
3267
+ })
3268
+ ).addCommand(
3269
+ new Command17("list").description("List recent sessions").option("-n, --limit <number>", "Number of sessions to show", "10").action(async (options) => {
3270
+ const sessionManager2 = getSessionManager();
3271
+ const sessions = await sessionManager2.getRecentSessions(parseInt(options.limit));
3272
+ if (sessions.length === 0) {
3273
+ console.log(chalk18.yellow("No sessions found"));
3274
+ return;
3275
+ }
3276
+ console.log(chalk18.cyan("Recent Sessions:"));
3277
+ console.log();
3278
+ for (const session of sessions) {
3279
+ const status = session.endedAt ? chalk18.gray("ended") : chalk18.green("active");
3280
+ const date = new Date(session.startedAt).toLocaleDateString();
3281
+ const actions = session.actions.length;
3282
+ console.log(
3283
+ chalk18.bold(session.id),
3284
+ status,
3285
+ chalk18.gray(`| ${date} | ${actions} actions`),
3286
+ session.project ? chalk18.blue(`[${session.project}]`) : ""
3287
+ );
3288
+ }
3289
+ })
3290
+ ).addCommand(
3291
+ 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) => {
3292
+ const sessionManager2 = getSessionManager();
3293
+ const handoff = await sessionManager2.getSessionHandoff(sessionId);
3294
+ console.log(handoff);
3295
+ })
3296
+ ).addCommand(
3297
+ new Command17("log").description("Log an action or note to the current session").argument("<message>", "Note to log").action(async (message) => {
3298
+ const sessionManager2 = getSessionManager();
3299
+ await sessionManager2.logAction({
3300
+ type: "note",
3301
+ description: message
3302
+ });
3303
+ console.log(chalk18.green("\u2713 Logged to session"));
3304
+ })
3305
+ );
3306
+ function formatDuration(start, end) {
3307
+ const ms = new Date(end).getTime() - new Date(start).getTime();
3308
+ const minutes = Math.floor(ms / 6e4);
3309
+ const hours = Math.floor(minutes / 60);
3310
+ if (hours > 0) {
3311
+ return `${hours}h ${minutes % 60}m`;
3312
+ }
3313
+ return `${minutes}m`;
3314
+ }
3315
+
2735
3316
  // bin/vibetasks.ts
2736
3317
  var require2 = createRequire(import.meta.url);
2737
3318
  var pkg = require2("../../package.json");
2738
- var program = new Command16();
3319
+ var program = new Command18();
2739
3320
  program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version(pkg.version);
2740
3321
  program.addCommand(setupCommand);
2741
3322
  program.addCommand(loginCommand);
@@ -2753,4 +3334,6 @@ program.addCommand(watchCommand);
2753
3334
  program.addCommand(checkCommand);
2754
3335
  program.addCommand(archiveCommand);
2755
3336
  program.addCommand(daemonCommand);
3337
+ program.addCommand(nextCommand);
3338
+ program.addCommand(sessionCommand);
2756
3339
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibetasks/cli",
3
- "version": "0.4.7",
3
+ "version": "0.5.0",
4
4
  "description": "VibeTasks CLI - Lightning-fast task management from your terminal. Works with Claude Code, Cursor, and all AI coding tools.",
5
5
  "author": "Vyas",
6
6
  "license": "MIT",
@@ -45,8 +45,8 @@
45
45
  "typecheck": "tsc --noEmit"
46
46
  },
47
47
  "dependencies": {
48
- "@vibetasks/core": "^0.4.2",
49
- "@vibetasks/shared": "^1.1.3",
48
+ "@vibetasks/core": "^0.5.0",
49
+ "@vibetasks/shared": "^1.2.0",
50
50
  "commander": "^11.1.0",
51
51
  "chalk": "^5.3.0",
52
52
  "ora": "^8.0.1",