@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.
- package/dist/bin/vibetasks.js +597 -14
- package/package.json +3 -3
package/dist/bin/vibetasks.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "../chunk-2KRLRG4G.js";
|
|
12
12
|
|
|
13
13
|
// bin/vibetasks.ts
|
|
14
|
-
import { Command as
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
726
|
-
|
|
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(
|
|
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 (
|
|
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
|
|
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.
|
|
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.
|
|
49
|
-
"@vibetasks/shared": "^1.
|
|
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",
|