@vibetasks/cli 0.6.10 → 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.
- package/dist/bin/vibetasks.js +1641 -140
- package/package.json +2 -2
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 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
861
|
+
const authManager = new AuthManager3();
|
|
639
862
|
const taskOps = await TaskOperations.fromAuthManager(authManager);
|
|
640
863
|
let projectTag;
|
|
641
864
|
if (options.project) {
|
|
@@ -675,6 +898,13 @@ 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
910
|
const sessionManager2 = getSessionManager();
|
|
@@ -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,6 +949,12 @@ 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!"));
|
|
952
|
+
try {
|
|
953
|
+
const shortIdManager = new ShortIdManager();
|
|
954
|
+
const allTasks = await taskOps.getTasks("all");
|
|
955
|
+
await shortIdManager.saveMappings(allTasks);
|
|
956
|
+
} catch (e) {
|
|
957
|
+
}
|
|
718
958
|
if (currentSession) {
|
|
719
959
|
await sessionManager2.logAction({
|
|
720
960
|
type: "task_created",
|
|
@@ -760,6 +1000,18 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
760
1000
|
console.log(chalk3.gray(` ${i + 1}. ${st.title}`));
|
|
761
1001
|
});
|
|
762
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
|
+
}
|
|
763
1015
|
console.log();
|
|
764
1016
|
process.exit(0);
|
|
765
1017
|
} catch (error) {
|
|
@@ -783,82 +1035,7 @@ function isRunningInClaudeCode() {
|
|
|
783
1035
|
import { Command as Command3 } from "commander";
|
|
784
1036
|
import chalk4 from "chalk";
|
|
785
1037
|
import Table from "cli-table3";
|
|
786
|
-
import { AuthManager as
|
|
787
|
-
|
|
788
|
-
// src/utils/short-ids.ts
|
|
789
|
-
import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
790
|
-
import { join as join2 } from "path";
|
|
791
|
-
import { homedir as homedir2 } from "os";
|
|
792
|
-
var ShortIdManager = class {
|
|
793
|
-
storePath;
|
|
794
|
-
maxIds = 99;
|
|
795
|
-
constructor() {
|
|
796
|
-
this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Save mappings from a list of tasks with their short_ids
|
|
800
|
-
* Uses the stable short_id from the database
|
|
801
|
-
*/
|
|
802
|
-
async saveMappings(tasks) {
|
|
803
|
-
const mappings = tasks.filter((t) => t.short_id).slice(0, this.maxIds).map((task, index) => ({
|
|
804
|
-
shortId: task.short_id,
|
|
805
|
-
// Use actual short_id from database
|
|
806
|
-
fullId: task.id,
|
|
807
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
808
|
-
}));
|
|
809
|
-
const store = {
|
|
810
|
-
mappings,
|
|
811
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
812
|
-
};
|
|
813
|
-
const dir = join2(homedir2(), ".vibetasks");
|
|
814
|
-
await mkdir2(dir, { recursive: true });
|
|
815
|
-
await writeFile2(this.storePath, JSON.stringify(store, null, 2));
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Get the full UUID for a short ID
|
|
819
|
-
*/
|
|
820
|
-
async getFullId(shortId) {
|
|
821
|
-
try {
|
|
822
|
-
const content = await readFile2(this.storePath, "utf-8");
|
|
823
|
-
const store = JSON.parse(content);
|
|
824
|
-
const mapping = store.mappings.find((m) => m.shortId === shortId);
|
|
825
|
-
return mapping?.fullId || null;
|
|
826
|
-
} catch {
|
|
827
|
-
return null;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* Get the short ID for a full UUID (if it exists in current mappings)
|
|
832
|
-
*/
|
|
833
|
-
async getShortId(fullId) {
|
|
834
|
-
try {
|
|
835
|
-
const content = await readFile2(this.storePath, "utf-8");
|
|
836
|
-
const store = JSON.parse(content);
|
|
837
|
-
const mapping = store.mappings.find((m) => m.fullId === fullId);
|
|
838
|
-
return mapping?.shortId || null;
|
|
839
|
-
} catch {
|
|
840
|
-
return null;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Resolve an ID that could be either short (1-99) or full UUID
|
|
845
|
-
*/
|
|
846
|
-
async resolveId(idOrShortId) {
|
|
847
|
-
const shortId = parseInt(idOrShortId, 10);
|
|
848
|
-
if (!isNaN(shortId) && shortId >= 1 && shortId <= this.maxIds) {
|
|
849
|
-
const fullId = await this.getFullId(shortId);
|
|
850
|
-
if (fullId) {
|
|
851
|
-
return fullId;
|
|
852
|
-
}
|
|
853
|
-
throw new Error(
|
|
854
|
-
`Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
|
|
855
|
-
);
|
|
856
|
-
}
|
|
857
|
-
return idOrShortId;
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
|
|
861
|
-
// src/commands/list.ts
|
|
1038
|
+
import { AuthManager as AuthManager4, TaskOperations as TaskOperations2 } from "@vibetasks/core";
|
|
862
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) => {
|
|
863
1040
|
try {
|
|
864
1041
|
const validFilters = ["all", "today", "upcoming", "completed"];
|
|
@@ -867,7 +1044,7 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
|
|
|
867
1044
|
console.error(chalk4.gray(`Valid filters: ${validFilters.join(", ")}`));
|
|
868
1045
|
process.exit(1);
|
|
869
1046
|
}
|
|
870
|
-
const authManager = new
|
|
1047
|
+
const authManager = new AuthManager4();
|
|
871
1048
|
const taskOps = await TaskOperations2.fromAuthManager(authManager);
|
|
872
1049
|
let tasks = await taskOps.getTasks(filter);
|
|
873
1050
|
if (options.project) {
|
|
@@ -1030,11 +1207,11 @@ Error: ${error.message}
|
|
|
1030
1207
|
import { Command as Command4 } from "commander";
|
|
1031
1208
|
import ora3 from "ora";
|
|
1032
1209
|
import chalk5 from "chalk";
|
|
1033
|
-
import { AuthManager as
|
|
1210
|
+
import { AuthManager as AuthManager5, TaskOperations as TaskOperations3 } from "@vibetasks/core";
|
|
1034
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) => {
|
|
1035
1212
|
const spinner = ora3("Completing task...").start();
|
|
1036
1213
|
try {
|
|
1037
|
-
const authManager = new
|
|
1214
|
+
const authManager = new AuthManager5();
|
|
1038
1215
|
const taskOps = await TaskOperations3.fromAuthManager(authManager);
|
|
1039
1216
|
const shortIdManager = new ShortIdManager();
|
|
1040
1217
|
let taskId = await shortIdManager.resolveId(id);
|
|
@@ -1107,11 +1284,11 @@ import { Command as Command5 } from "commander";
|
|
|
1107
1284
|
import chalk6 from "chalk";
|
|
1108
1285
|
import ora4 from "ora";
|
|
1109
1286
|
import inquirer from "inquirer";
|
|
1110
|
-
import { AuthManager as
|
|
1287
|
+
import { AuthManager as AuthManager6, TaskOperations as TaskOperations4 } from "@vibetasks/core";
|
|
1111
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) => {
|
|
1112
1289
|
const spinner = ora4();
|
|
1113
1290
|
try {
|
|
1114
|
-
const authManager = new
|
|
1291
|
+
const authManager = new AuthManager6();
|
|
1115
1292
|
const taskOps = await TaskOperations4.fromAuthManager(authManager);
|
|
1116
1293
|
const shortIdManager = new ShortIdManager();
|
|
1117
1294
|
let taskId;
|
|
@@ -1189,10 +1366,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1189
1366
|
import { Command as Command6 } from "commander";
|
|
1190
1367
|
import chalk7 from "chalk";
|
|
1191
1368
|
import Table2 from "cli-table3";
|
|
1192
|
-
import { AuthManager as
|
|
1369
|
+
import { AuthManager as AuthManager7, TaskOperations as TaskOperations5 } from "@vibetasks/core";
|
|
1193
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) => {
|
|
1194
1371
|
try {
|
|
1195
|
-
const authManager = new
|
|
1372
|
+
const authManager = new AuthManager7();
|
|
1196
1373
|
const taskOps = await TaskOperations5.fromAuthManager(authManager);
|
|
1197
1374
|
const limit = parseInt(options.limit, 10);
|
|
1198
1375
|
const tasks = await taskOps.searchTasks(query, limit);
|
|
@@ -1249,7 +1426,7 @@ Error: ${error.message}
|
|
|
1249
1426
|
import { Command as Command7 } from "commander";
|
|
1250
1427
|
import ora5 from "ora";
|
|
1251
1428
|
import chalk8 from "chalk";
|
|
1252
|
-
import { AuthManager as
|
|
1429
|
+
import { AuthManager as AuthManager8, TaskOperations as TaskOperations6 } from "@vibetasks/core";
|
|
1253
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) => {
|
|
1254
1431
|
if (!options.title && !options.notes && !options.due && !options.priority && !options.status && !options.project && !options.energy && !options.context) {
|
|
1255
1432
|
console.error(chalk8.red("\nError: No updates specified"));
|
|
@@ -1258,7 +1435,7 @@ var updateCommand = new Command7("update").description("Update a task").argument
|
|
|
1258
1435
|
}
|
|
1259
1436
|
const spinner = ora5("Updating task...").start();
|
|
1260
1437
|
try {
|
|
1261
|
-
const authManager = new
|
|
1438
|
+
const authManager = new AuthManager8();
|
|
1262
1439
|
const taskOps = await TaskOperations6.fromAuthManager(authManager);
|
|
1263
1440
|
let taskId = id;
|
|
1264
1441
|
if (id.length < 32) {
|
|
@@ -1325,10 +1502,10 @@ import { Command as Command8 } from "commander";
|
|
|
1325
1502
|
import ora6 from "ora";
|
|
1326
1503
|
import chalk9 from "chalk";
|
|
1327
1504
|
import inquirer2 from "inquirer";
|
|
1328
|
-
import { AuthManager as
|
|
1505
|
+
import { AuthManager as AuthManager9, TaskOperations as TaskOperations7 } from "@vibetasks/core";
|
|
1329
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) => {
|
|
1330
1507
|
try {
|
|
1331
|
-
const authManager = new
|
|
1508
|
+
const authManager = new AuthManager9();
|
|
1332
1509
|
const taskOps = await TaskOperations7.fromAuthManager(authManager);
|
|
1333
1510
|
let taskId = id;
|
|
1334
1511
|
let taskTitle = "";
|
|
@@ -1384,10 +1561,10 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
|
|
|
1384
1561
|
// src/commands/config.ts
|
|
1385
1562
|
import { Command as Command9 } from "commander";
|
|
1386
1563
|
import chalk10 from "chalk";
|
|
1387
|
-
import { AuthManager as
|
|
1564
|
+
import { AuthManager as AuthManager10 } from "@vibetasks/core";
|
|
1388
1565
|
var configCommand = new Command9("config").description("View or set configuration").argument("[key]", "Configuration key").argument("[value]", "Configuration value").action(async (key, value) => {
|
|
1389
1566
|
try {
|
|
1390
|
-
const authManager = new
|
|
1567
|
+
const authManager = new AuthManager10();
|
|
1391
1568
|
if (!key) {
|
|
1392
1569
|
const configManager = authManager["configManager"];
|
|
1393
1570
|
const config = await configManager.getConfig();
|
|
@@ -1621,12 +1798,12 @@ import { Command as Command11 } from "commander";
|
|
|
1621
1798
|
import inquirer3 from "inquirer";
|
|
1622
1799
|
import ora8 from "ora";
|
|
1623
1800
|
import chalk12 from "chalk";
|
|
1624
|
-
import { AuthManager as
|
|
1801
|
+
import { AuthManager as AuthManager11 } from "@vibetasks/core";
|
|
1625
1802
|
import { detectProject as detectProject2 } from "@vibetasks/shared/utils/project-detector";
|
|
1626
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) => {
|
|
1627
1804
|
const spinner = ora8("Detecting project...").start();
|
|
1628
1805
|
try {
|
|
1629
|
-
const authManager = new
|
|
1806
|
+
const authManager = new AuthManager11();
|
|
1630
1807
|
const cwd = process.cwd();
|
|
1631
1808
|
let project;
|
|
1632
1809
|
if (options.name) {
|
|
@@ -1691,7 +1868,7 @@ import os from "os";
|
|
|
1691
1868
|
import { createServer as createServer2 } from "http";
|
|
1692
1869
|
import { exec as exec2 } from "child_process";
|
|
1693
1870
|
import { promisify as promisify2 } from "util";
|
|
1694
|
-
import { AuthManager as
|
|
1871
|
+
import { AuthManager as AuthManager12, TaskOperations as TaskOperations8, createSupabaseClient } from "@vibetasks/core";
|
|
1695
1872
|
import { detectProject as detectProject3 } from "@vibetasks/shared/utils/project-detector";
|
|
1696
1873
|
var execAsync2 = promisify2(exec2);
|
|
1697
1874
|
var SUPABASE_URL = "https://ihmayqzxqyednchbezya.supabase.co";
|
|
@@ -1756,7 +1933,7 @@ function showWelcome() {
|
|
|
1756
1933
|
}
|
|
1757
1934
|
async function checkExistingAuth() {
|
|
1758
1935
|
try {
|
|
1759
|
-
const authManager = new
|
|
1936
|
+
const authManager = new AuthManager12();
|
|
1760
1937
|
const token = await authManager.getAccessToken();
|
|
1761
1938
|
if (!token) return { authenticated: false };
|
|
1762
1939
|
const supabaseUrl = await authManager.getConfig("supabase_url") || SUPABASE_URL;
|
|
@@ -1800,7 +1977,7 @@ async function runBrowserAuth() {
|
|
|
1800
1977
|
return;
|
|
1801
1978
|
}
|
|
1802
1979
|
try {
|
|
1803
|
-
const authManager = new
|
|
1980
|
+
const authManager = new AuthManager12();
|
|
1804
1981
|
await authManager.setAccessToken(accessToken);
|
|
1805
1982
|
await authManager.setRefreshToken(refreshToken);
|
|
1806
1983
|
if (supabaseUrl) await authManager.setConfig("supabase_url", supabaseUrl);
|
|
@@ -1893,7 +2070,7 @@ async function stepAuthentication() {
|
|
|
1893
2070
|
]);
|
|
1894
2071
|
const spinner = ora9("Authenticating...").start();
|
|
1895
2072
|
try {
|
|
1896
|
-
const authManager = new
|
|
2073
|
+
const authManager = new AuthManager12();
|
|
1897
2074
|
await authManager.setConfig("supabase_url", SUPABASE_URL);
|
|
1898
2075
|
await authManager.setConfig("supabase_key", SUPABASE_ANON_KEY);
|
|
1899
2076
|
const supabase = createSupabaseClient({
|
|
@@ -2042,7 +2219,7 @@ async function stepProjectInit() {
|
|
|
2042
2219
|
return { success: true, skipped: true };
|
|
2043
2220
|
}
|
|
2044
2221
|
try {
|
|
2045
|
-
const authManager = new
|
|
2222
|
+
const authManager = new AuthManager12();
|
|
2046
2223
|
await authManager.setConfig(`project_${process.cwd()}`, project.name);
|
|
2047
2224
|
console.log(chalk13.green("\n \u2713") + chalk13.white(` Project "${project.name}" configured`));
|
|
2048
2225
|
return { success: true, projectName: project.name };
|
|
@@ -2059,7 +2236,7 @@ async function stepVerify() {
|
|
|
2059
2236
|
};
|
|
2060
2237
|
const supabaseSpinner = ora9("Testing Supabase connection...").start();
|
|
2061
2238
|
try {
|
|
2062
|
-
const authManager = new
|
|
2239
|
+
const authManager = new AuthManager12();
|
|
2063
2240
|
const taskOps = await TaskOperations8.fromAuthManager(authManager);
|
|
2064
2241
|
const tasks = await taskOps.getTasks("all");
|
|
2065
2242
|
result.supabaseConnected = true;
|
|
@@ -2232,7 +2409,7 @@ async function runNonInteractiveSetup() {
|
|
|
2232
2409
|
if (authResult.success) {
|
|
2233
2410
|
console.log(chalk13.gray("\nVerifying connection..."));
|
|
2234
2411
|
try {
|
|
2235
|
-
const authManager = new
|
|
2412
|
+
const authManager = new AuthManager12();
|
|
2236
2413
|
const taskOps = await TaskOperations8.fromAuthManager(authManager);
|
|
2237
2414
|
const tasks = await taskOps.getTasks("all");
|
|
2238
2415
|
console.log(chalk13.green("\u2713") + ` Connected (${tasks.length} tasks)`);
|
|
@@ -2303,7 +2480,7 @@ import { Command as Command13 } from "commander";
|
|
|
2303
2480
|
import chalk14 from "chalk";
|
|
2304
2481
|
import ora10 from "ora";
|
|
2305
2482
|
import inquirer5 from "inquirer";
|
|
2306
|
-
import { AuthManager as
|
|
2483
|
+
import { AuthManager as AuthManager13, TaskOperations as TaskOperations9 } from "@vibetasks/core";
|
|
2307
2484
|
import { detectProject as detectProject4 } from "@vibetasks/shared/utils/project-detector";
|
|
2308
2485
|
import { generateErrorTaskTitle } from "@vibetasks/shared";
|
|
2309
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) => {
|
|
@@ -2317,7 +2494,7 @@ var watchCommand = new Command13("watch").description("Monitor clipboard for err
|
|
|
2317
2494
|
let authManager;
|
|
2318
2495
|
let taskOps;
|
|
2319
2496
|
try {
|
|
2320
|
-
authManager = new
|
|
2497
|
+
authManager = new AuthManager13();
|
|
2321
2498
|
taskOps = await TaskOperations9.fromAuthManager(authManager);
|
|
2322
2499
|
} catch (error) {
|
|
2323
2500
|
if (error.message.includes("Not authenticated")) {
|
|
@@ -2527,11 +2704,11 @@ import { Command as Command14 } from "commander";
|
|
|
2527
2704
|
import chalk15 from "chalk";
|
|
2528
2705
|
import ora11 from "ora";
|
|
2529
2706
|
import inquirer6 from "inquirer";
|
|
2530
|
-
import { AuthManager as
|
|
2707
|
+
import { AuthManager as AuthManager14, TaskOperations as TaskOperations10 } from "@vibetasks/core";
|
|
2531
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) => {
|
|
2532
2709
|
const spinner = ora11();
|
|
2533
2710
|
try {
|
|
2534
|
-
const authManager = new
|
|
2711
|
+
const authManager = new AuthManager14();
|
|
2535
2712
|
const taskOps = await TaskOperations10.fromAuthManager(authManager);
|
|
2536
2713
|
if (options.list) {
|
|
2537
2714
|
spinner.start("Fetching archived tasks...");
|
|
@@ -2646,7 +2823,7 @@ import ora12 from "ora";
|
|
|
2646
2823
|
import { spawn } from "child_process";
|
|
2647
2824
|
import path3 from "path";
|
|
2648
2825
|
import { fileURLToPath } from "url";
|
|
2649
|
-
import { AuthManager as
|
|
2826
|
+
import { AuthManager as AuthManager15, TaskOperations as TaskOperations11 } from "@vibetasks/core";
|
|
2650
2827
|
import { generateErrorTaskTitle as generateErrorTaskTitle2 } from "@vibetasks/shared";
|
|
2651
2828
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
2652
2829
|
var __dirname2 = path3.dirname(__filename2);
|
|
@@ -2970,7 +3147,7 @@ async function showNotification(title, message, type = "info", duration) {
|
|
|
2970
3147
|
async function createTaskFromError2(error) {
|
|
2971
3148
|
const spinner = ora12("Creating task from error...").start();
|
|
2972
3149
|
try {
|
|
2973
|
-
const authManager = new
|
|
3150
|
+
const authManager = new AuthManager15();
|
|
2974
3151
|
const taskOps = await TaskOperations11.fromAuthManager(authManager);
|
|
2975
3152
|
const title = generateErrorTaskTitle2(error);
|
|
2976
3153
|
const notes = formatErrorForNotes(error);
|
|
@@ -3033,11 +3210,11 @@ function getTagColor2(category) {
|
|
|
3033
3210
|
// src/commands/next.ts
|
|
3034
3211
|
import { Command as Command16 } from "commander";
|
|
3035
3212
|
import chalk17 from "chalk";
|
|
3036
|
-
import { AuthManager as
|
|
3213
|
+
import { AuthManager as AuthManager16, TaskOperations as TaskOperations12 } from "@vibetasks/core";
|
|
3037
3214
|
var PRIORITY_ORDER = { high: 0, medium: 1, low: 2, none: 3 };
|
|
3038
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) => {
|
|
3039
3216
|
try {
|
|
3040
|
-
const authManager = new
|
|
3217
|
+
const authManager = new AuthManager16();
|
|
3041
3218
|
const taskOps = await TaskOperations12.fromAuthManager(authManager);
|
|
3042
3219
|
let tasks = await taskOps.getTasks("all");
|
|
3043
3220
|
tasks = tasks.filter((t) => t.status !== "done" && t.status !== "archived" && !t.completed);
|
|
@@ -3198,7 +3375,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3198
3375
|
}
|
|
3199
3376
|
})
|
|
3200
3377
|
).addCommand(
|
|
3201
|
-
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) => {
|
|
3202
3379
|
const sessionManager2 = getSessionManager();
|
|
3203
3380
|
const session = await sessionManager2.endSession(options.summary);
|
|
3204
3381
|
if (!session) {
|
|
@@ -3206,10 +3383,51 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3206
3383
|
return;
|
|
3207
3384
|
}
|
|
3208
3385
|
console.log(chalk18.green("\u2713 Session ended:"), chalk18.bold(session.id));
|
|
3209
|
-
console.log(
|
|
3210
|
-
|
|
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
|
+
}
|
|
3211
3420
|
if (options.summary) {
|
|
3212
|
-
console.log(chalk18.
|
|
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!"));
|
|
3213
3431
|
}
|
|
3214
3432
|
})
|
|
3215
3433
|
).addCommand(
|
|
@@ -3235,7 +3453,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3235
3453
|
}
|
|
3236
3454
|
})
|
|
3237
3455
|
).addCommand(
|
|
3238
|
-
new Command17("show").
|
|
3456
|
+
new Command17("show").description("Show session details").argument("<session-id>", "Session ID (partial match supported)").action(async (sessionId) => {
|
|
3239
3457
|
const sessionManager2 = getSessionManager();
|
|
3240
3458
|
const handoff = await sessionManager2.getSessionHandoff(sessionId);
|
|
3241
3459
|
console.log(handoff);
|
|
@@ -3249,9 +3467,26 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3249
3467
|
});
|
|
3250
3468
|
console.log(chalk18.green("\u2713 Logged to session"));
|
|
3251
3469
|
})
|
|
3252
|
-
)
|
|
3253
|
-
|
|
3254
|
-
|
|
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
|
+
})
|
|
3487
|
+
);
|
|
3488
|
+
function formatDuration(start, end) {
|
|
3489
|
+
const ms = new Date(end).getTime() - new Date(start).getTime();
|
|
3255
3490
|
const minutes = Math.floor(ms / 6e4);
|
|
3256
3491
|
const hours = Math.floor(minutes / 60);
|
|
3257
3492
|
if (hours > 0) {
|
|
@@ -3264,7 +3499,7 @@ function formatDuration(start, end) {
|
|
|
3264
3499
|
import { Command as Command18 } from "commander";
|
|
3265
3500
|
import ora13 from "ora";
|
|
3266
3501
|
import chalk19 from "chalk";
|
|
3267
|
-
import { AuthManager as
|
|
3502
|
+
import { AuthManager as AuthManager17, TaskOperations as TaskOperations13 } from "@vibetasks/core";
|
|
3268
3503
|
function parseIndices(args) {
|
|
3269
3504
|
const indices = /* @__PURE__ */ new Set();
|
|
3270
3505
|
for (const arg of args) {
|
|
@@ -3306,7 +3541,7 @@ var subtaskCommand = new Command18("subtask").description("Manage subtasks withi
|
|
|
3306
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) => {
|
|
3307
3542
|
const spinner = ora13("Loading subtasks...").start();
|
|
3308
3543
|
try {
|
|
3309
|
-
const authManager = new
|
|
3544
|
+
const authManager = new AuthManager17();
|
|
3310
3545
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3311
3546
|
const shortIdManager = new ShortIdManager();
|
|
3312
3547
|
let taskId = await shortIdManager.resolveId(taskIdArg);
|
|
@@ -3354,7 +3589,7 @@ Error: ${error.message}
|
|
|
3354
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) => {
|
|
3355
3590
|
const spinner = ora13("Updating subtasks...").start();
|
|
3356
3591
|
try {
|
|
3357
|
-
const authManager = new
|
|
3592
|
+
const authManager = new AuthManager17();
|
|
3358
3593
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3359
3594
|
const shortIdManager = new ShortIdManager();
|
|
3360
3595
|
let taskId = await shortIdManager.resolveId(taskIdArg);
|
|
@@ -3430,7 +3665,7 @@ Error: ${error.message}
|
|
|
3430
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) => {
|
|
3431
3666
|
const spinner = ora13("Updating subtasks...").start();
|
|
3432
3667
|
try {
|
|
3433
|
-
const authManager = new
|
|
3668
|
+
const authManager = new AuthManager17();
|
|
3434
3669
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3435
3670
|
const shortIdManager = new ShortIdManager();
|
|
3436
3671
|
let taskId = await shortIdManager.resolveId(taskIdArg);
|
|
@@ -3504,7 +3739,7 @@ Error: ${error.message}
|
|
|
3504
3739
|
import { Command as Command19 } from "commander";
|
|
3505
3740
|
import chalk20 from "chalk";
|
|
3506
3741
|
import ora14 from "ora";
|
|
3507
|
-
import { AuthManager as
|
|
3742
|
+
import { AuthManager as AuthManager18, TaskOperations as TaskOperations14 } from "@vibetasks/core";
|
|
3508
3743
|
import {
|
|
3509
3744
|
parseError,
|
|
3510
3745
|
generateErrorTaskTitle as generateErrorTaskTitle3,
|
|
@@ -3602,7 +3837,7 @@ var errorCommand = new Command19("error").description("Capture an error and crea
|
|
|
3602
3837
|
process.exit(1);
|
|
3603
3838
|
}
|
|
3604
3839
|
spinner.start("Creating error task...");
|
|
3605
|
-
const authManager = new
|
|
3840
|
+
const authManager = new AuthManager18();
|
|
3606
3841
|
const taskOps = await TaskOperations14.fromAuthManager(authManager);
|
|
3607
3842
|
const errorContext = parseError(errorText, "terminal");
|
|
3608
3843
|
const title = errorContext ? generateErrorTaskTitle3(errorContext) : `Error: ${errorText.split("\n")[0].slice(0, 80)}`;
|
|
@@ -3642,7 +3877,7 @@ ${errorText}
|
|
|
3642
3877
|
import { Command as Command20 } from "commander";
|
|
3643
3878
|
import chalk21 from "chalk";
|
|
3644
3879
|
import Table3 from "cli-table3";
|
|
3645
|
-
import { AuthManager as
|
|
3880
|
+
import { AuthManager as AuthManager19, TaskOperations as TaskOperations15 } from "@vibetasks/core";
|
|
3646
3881
|
var sourceDisplay = {
|
|
3647
3882
|
sentry: { label: "Sentry", color: chalk21.red },
|
|
3648
3883
|
ci: { label: "CI", color: chalk21.yellow },
|
|
@@ -3669,7 +3904,7 @@ function formatSource(sourceType) {
|
|
|
3669
3904
|
}
|
|
3670
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) => {
|
|
3671
3906
|
try {
|
|
3672
|
-
const authManager = new
|
|
3907
|
+
const authManager = new AuthManager19();
|
|
3673
3908
|
const taskOps = await TaskOperations15.fromAuthManager(authManager);
|
|
3674
3909
|
let sourceTypes;
|
|
3675
3910
|
if (options.source) {
|
|
@@ -3778,7 +4013,7 @@ Error: ${error.message}
|
|
|
3778
4013
|
});
|
|
3779
4014
|
var inboxStatsCommand = new Command20("stats").description("Show inbox statistics").action(async () => {
|
|
3780
4015
|
try {
|
|
3781
|
-
const authManager = new
|
|
4016
|
+
const authManager = new AuthManager19();
|
|
3782
4017
|
const taskOps = await TaskOperations15.fromAuthManager(authManager);
|
|
3783
4018
|
const stats = await taskOps.getInboxStats();
|
|
3784
4019
|
console.log("\n" + chalk21.bold("Inbox Statistics") + "\n");
|
|
@@ -3834,10 +4069,1267 @@ Error: ${error.message}
|
|
|
3834
4069
|
});
|
|
3835
4070
|
inboxCommand.addCommand(inboxStatsCommand);
|
|
3836
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
|
+
|
|
3837
5329
|
// bin/vibetasks.ts
|
|
3838
5330
|
var require2 = createRequire(import.meta.url);
|
|
3839
5331
|
var pkg = require2("../../package.json");
|
|
3840
|
-
var program = new
|
|
5332
|
+
var program = new Command30();
|
|
3841
5333
|
program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version(pkg.version);
|
|
3842
5334
|
program.addCommand(setupCommand);
|
|
3843
5335
|
program.addCommand(loginCommand);
|
|
@@ -3860,4 +5352,13 @@ program.addCommand(sessionCommand);
|
|
|
3860
5352
|
program.addCommand(subtaskCommand);
|
|
3861
5353
|
program.addCommand(errorCommand);
|
|
3862
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);
|
|
3863
5364
|
program.parse();
|