@vibetasks/cli 0.6.10 → 0.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/vibetasks.js +3299 -272
- package/package.json +3 -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 Command37 } from "commander";
|
|
15
15
|
import { createRequire } from "module";
|
|
16
16
|
|
|
17
17
|
// src/commands/login.ts
|
|
@@ -368,7 +368,7 @@ var loginCommand = new Command("login").description("Authenticate with VibeTasks
|
|
|
368
368
|
import { Command as Command2 } from "commander";
|
|
369
369
|
import ora2 from "ora";
|
|
370
370
|
import chalk3 from "chalk";
|
|
371
|
-
import { AuthManager as
|
|
371
|
+
import { AuthManager as AuthManager3, TaskOperations } from "@vibetasks/core";
|
|
372
372
|
|
|
373
373
|
// src/utils/date-parser.ts
|
|
374
374
|
import { addDays, addWeeks, addMonths, format, parse, isValid } from "date-fns";
|
|
@@ -415,7 +415,8 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
415
415
|
import { writeFile, readFile, mkdir } from "fs/promises";
|
|
416
416
|
import { join } from "path";
|
|
417
417
|
import { homedir } from "os";
|
|
418
|
-
import { randomUUID } from "crypto";
|
|
418
|
+
import { randomUUID, randomInt } from "crypto";
|
|
419
|
+
import { AuthManager as AuthManager2 } from "@vibetasks/core";
|
|
419
420
|
var ADJECTIVES = [
|
|
420
421
|
"swift",
|
|
421
422
|
"calm",
|
|
@@ -455,16 +456,111 @@ var NOUNS = [
|
|
|
455
456
|
var SessionManager = class {
|
|
456
457
|
storePath;
|
|
457
458
|
store = null;
|
|
459
|
+
authManager;
|
|
458
460
|
constructor() {
|
|
459
461
|
this.storePath = join(homedir(), ".vibetasks", "sessions.json");
|
|
462
|
+
this.authManager = new AuthManager2();
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Get the API base URL for syncing sessions
|
|
466
|
+
*/
|
|
467
|
+
getApiBaseUrl() {
|
|
468
|
+
if (process.env.VIBETASKS_AUTH_URL) {
|
|
469
|
+
return process.env.VIBETASKS_AUTH_URL;
|
|
470
|
+
}
|
|
471
|
+
if (process.env.TASKFLOW_WEB_PORT) {
|
|
472
|
+
return `http://localhost:${process.env.TASKFLOW_WEB_PORT}`;
|
|
473
|
+
}
|
|
474
|
+
return "https://vibetasks.dev";
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Sync a single action to the server in real-time
|
|
478
|
+
* Called after each action is logged locally
|
|
479
|
+
*/
|
|
480
|
+
async syncActionToServer(sessionId, action) {
|
|
481
|
+
try {
|
|
482
|
+
const token = await this.authManager.getValidAccessToken();
|
|
483
|
+
if (!token) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
const apiUrl = `${this.getApiBaseUrl()}/api/sessions/${sessionId}`;
|
|
487
|
+
const payload = {
|
|
488
|
+
action_type: action.type,
|
|
489
|
+
description: action.description,
|
|
490
|
+
timestamp: action.timestamp,
|
|
491
|
+
task_id: action.taskId
|
|
492
|
+
};
|
|
493
|
+
const response = await fetch(apiUrl, {
|
|
494
|
+
method: "POST",
|
|
495
|
+
headers: {
|
|
496
|
+
"Content-Type": "application/json",
|
|
497
|
+
"Authorization": `Bearer ${token}`
|
|
498
|
+
},
|
|
499
|
+
body: JSON.stringify(payload)
|
|
500
|
+
});
|
|
501
|
+
return response.ok;
|
|
502
|
+
} catch {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Sync a session to the server database
|
|
508
|
+
* Called automatically when a session ends
|
|
509
|
+
*/
|
|
510
|
+
async syncToServer(session) {
|
|
511
|
+
try {
|
|
512
|
+
const token = await this.authManager.getValidAccessToken();
|
|
513
|
+
if (!token) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
const apiUrl = `${this.getApiBaseUrl()}/api/sessions`;
|
|
517
|
+
const payload = {
|
|
518
|
+
session_id: session.id,
|
|
519
|
+
status: session.endedAt ? "ended" : "active",
|
|
520
|
+
started_at: session.startedAt,
|
|
521
|
+
ended_at: session.endedAt,
|
|
522
|
+
project_tag: session.project,
|
|
523
|
+
summary: session.summary,
|
|
524
|
+
handoff_notes: session.summary,
|
|
525
|
+
// Use summary as handoff notes
|
|
526
|
+
actions: session.actions.map((action) => ({
|
|
527
|
+
action_type: action.type,
|
|
528
|
+
description: action.description,
|
|
529
|
+
timestamp: action.timestamp,
|
|
530
|
+
task_id: action.taskId
|
|
531
|
+
}))
|
|
532
|
+
};
|
|
533
|
+
const response = await fetch(apiUrl, {
|
|
534
|
+
method: "POST",
|
|
535
|
+
headers: {
|
|
536
|
+
"Content-Type": "application/json",
|
|
537
|
+
"Authorization": `Bearer ${token}`
|
|
538
|
+
},
|
|
539
|
+
body: JSON.stringify(payload)
|
|
540
|
+
});
|
|
541
|
+
if (!response.ok) {
|
|
542
|
+
const error = await response.text();
|
|
543
|
+
if (process.stderr.isTTY) {
|
|
544
|
+
console.error(`Session sync failed: ${error}`);
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
return true;
|
|
549
|
+
} catch (error) {
|
|
550
|
+
if (process.stderr.isTTY) {
|
|
551
|
+
console.error(`Session sync error: ${error.message}`);
|
|
552
|
+
}
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
460
555
|
}
|
|
461
556
|
/**
|
|
462
557
|
* Generate a memorable session ID like "swift-fox-42"
|
|
558
|
+
* Uses cryptographically secure random generation
|
|
463
559
|
*/
|
|
464
560
|
generateSessionId() {
|
|
465
|
-
const adj = ADJECTIVES[
|
|
466
|
-
const noun = NOUNS[
|
|
467
|
-
const num =
|
|
561
|
+
const adj = ADJECTIVES[randomInt(ADJECTIVES.length)];
|
|
562
|
+
const noun = NOUNS[randomInt(NOUNS.length)];
|
|
563
|
+
const num = randomInt(100);
|
|
468
564
|
return `${adj}-${noun}-${num}`;
|
|
469
565
|
}
|
|
470
566
|
/**
|
|
@@ -510,6 +606,8 @@ var SessionManager = class {
|
|
|
510
606
|
store.sessions.push(session);
|
|
511
607
|
store.currentSession = session.id;
|
|
512
608
|
await this.save();
|
|
609
|
+
await this.syncToServer(session).catch(() => {
|
|
610
|
+
});
|
|
513
611
|
return session;
|
|
514
612
|
}
|
|
515
613
|
/**
|
|
@@ -532,23 +630,22 @@ var SessionManager = class {
|
|
|
532
630
|
}
|
|
533
631
|
/**
|
|
534
632
|
* Log an action in the current session
|
|
633
|
+
* Syncs to server in real-time (non-blocking)
|
|
535
634
|
*/
|
|
536
635
|
async logAction(action) {
|
|
537
636
|
const store = await this.load();
|
|
538
|
-
|
|
637
|
+
let session = store.sessions.find((s) => s.id === store.currentSession);
|
|
638
|
+
const actionWithTimestamp = {
|
|
639
|
+
...action,
|
|
640
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
641
|
+
};
|
|
539
642
|
if (!session) {
|
|
540
|
-
|
|
541
|
-
newSession.actions.push({
|
|
542
|
-
...action,
|
|
543
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
544
|
-
});
|
|
545
|
-
} else {
|
|
546
|
-
session.actions.push({
|
|
547
|
-
...action,
|
|
548
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
549
|
-
});
|
|
643
|
+
session = await this.startSession();
|
|
550
644
|
}
|
|
645
|
+
session.actions.push(actionWithTimestamp);
|
|
551
646
|
await this.save();
|
|
647
|
+
this.syncActionToServer(session.id, actionWithTimestamp).catch(() => {
|
|
648
|
+
});
|
|
552
649
|
}
|
|
553
650
|
/**
|
|
554
651
|
* End the current session
|
|
@@ -561,6 +658,8 @@ var SessionManager = class {
|
|
|
561
658
|
session.summary = summary;
|
|
562
659
|
store.currentSession = void 0;
|
|
563
660
|
await this.save();
|
|
661
|
+
this.syncToServer(session).catch(() => {
|
|
662
|
+
});
|
|
564
663
|
}
|
|
565
664
|
return session || null;
|
|
566
665
|
}
|
|
@@ -622,6 +721,58 @@ var SessionManager = class {
|
|
|
622
721
|
}
|
|
623
722
|
return lines.filter(Boolean).join("\n");
|
|
624
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Generate a comprehensive handoff for the next AI
|
|
726
|
+
* Includes: vibing tasks with context, git status, files changed, next steps
|
|
727
|
+
*/
|
|
728
|
+
async generateHandoff() {
|
|
729
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
730
|
+
const lines = [];
|
|
731
|
+
lines.push("# AI Handoff");
|
|
732
|
+
lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
|
|
733
|
+
lines.push("");
|
|
734
|
+
const currentSession = await this.getCurrentSession();
|
|
735
|
+
if (currentSession) {
|
|
736
|
+
lines.push(`## Current Session: ${currentSession.id}`);
|
|
737
|
+
lines.push(`Project: ${currentSession.project || "unknown"}`);
|
|
738
|
+
lines.push(`Started: ${new Date(currentSession.startedAt).toLocaleString()}`);
|
|
739
|
+
lines.push(`Actions: ${currentSession.actions.length}`);
|
|
740
|
+
lines.push("");
|
|
741
|
+
}
|
|
742
|
+
try {
|
|
743
|
+
const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
744
|
+
if (gitStatus) {
|
|
745
|
+
const changedFiles = gitStatus.split("\n").slice(0, 20);
|
|
746
|
+
lines.push("## Uncommitted Changes");
|
|
747
|
+
changedFiles.forEach((f) => lines.push(`- ${f}`));
|
|
748
|
+
if (gitStatus.split("\n").length > 20) {
|
|
749
|
+
lines.push(`- ... and ${gitStatus.split("\n").length - 20} more files`);
|
|
750
|
+
}
|
|
751
|
+
lines.push("");
|
|
752
|
+
}
|
|
753
|
+
} catch {
|
|
754
|
+
}
|
|
755
|
+
try {
|
|
756
|
+
const recentCommits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
757
|
+
if (recentCommits) {
|
|
758
|
+
lines.push("## Recent Commits");
|
|
759
|
+
recentCommits.split("\n").forEach((c) => lines.push(`- ${c}`));
|
|
760
|
+
lines.push("");
|
|
761
|
+
}
|
|
762
|
+
} catch {
|
|
763
|
+
}
|
|
764
|
+
lines.push("---");
|
|
765
|
+
lines.push("## For Next AI");
|
|
766
|
+
lines.push("");
|
|
767
|
+
lines.push("1. Check vibing tasks: `vibetasks list --status vibing --short-ids`");
|
|
768
|
+
lines.push('2. Read context_notes on vibing tasks for "where I left off"');
|
|
769
|
+
lines.push("3. Check git status for uncommitted work");
|
|
770
|
+
lines.push("");
|
|
771
|
+
if (currentSession) {
|
|
772
|
+
lines.push(`To continue this session's work: "Continue from session ${currentSession.id}"`);
|
|
773
|
+
}
|
|
774
|
+
return lines.join("\n");
|
|
775
|
+
}
|
|
625
776
|
};
|
|
626
777
|
var sessionManager = null;
|
|
627
778
|
function getSessionManager() {
|
|
@@ -631,11 +782,114 @@ function getSessionManager() {
|
|
|
631
782
|
return sessionManager;
|
|
632
783
|
}
|
|
633
784
|
|
|
785
|
+
// src/utils/short-ids.ts
|
|
786
|
+
import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
787
|
+
import { join as join2 } from "path";
|
|
788
|
+
import { homedir as homedir2 } from "os";
|
|
789
|
+
var ShortIdManager = class {
|
|
790
|
+
storePath;
|
|
791
|
+
maxIds = 9999;
|
|
792
|
+
// Support up to 9999 short IDs (database assigns them)
|
|
793
|
+
constructor() {
|
|
794
|
+
this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Save mappings from a list of tasks with their short_ids
|
|
798
|
+
* Uses the stable short_id from the database
|
|
799
|
+
*/
|
|
800
|
+
async saveMappings(tasks) {
|
|
801
|
+
const mappings = tasks.filter((t) => t.short_id).slice(0, this.maxIds).map((task, index) => ({
|
|
802
|
+
shortId: task.short_id,
|
|
803
|
+
// Use actual short_id from database
|
|
804
|
+
fullId: task.id,
|
|
805
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
806
|
+
}));
|
|
807
|
+
const store = {
|
|
808
|
+
mappings,
|
|
809
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
810
|
+
};
|
|
811
|
+
const dir = join2(homedir2(), ".vibetasks");
|
|
812
|
+
await mkdir2(dir, { recursive: true });
|
|
813
|
+
await writeFile2(this.storePath, JSON.stringify(store, null, 2));
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Get the full UUID for a short ID
|
|
817
|
+
*/
|
|
818
|
+
async getFullId(shortId) {
|
|
819
|
+
try {
|
|
820
|
+
const content = await readFile2(this.storePath, "utf-8");
|
|
821
|
+
const store = JSON.parse(content);
|
|
822
|
+
const mapping = store.mappings.find((m) => m.shortId === shortId);
|
|
823
|
+
return mapping?.fullId || null;
|
|
824
|
+
} catch {
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Get the short ID for a full UUID (if it exists in current mappings)
|
|
830
|
+
*/
|
|
831
|
+
async getShortId(fullId) {
|
|
832
|
+
try {
|
|
833
|
+
const content = await readFile2(this.storePath, "utf-8");
|
|
834
|
+
const store = JSON.parse(content);
|
|
835
|
+
const mapping = store.mappings.find((m) => m.fullId === fullId);
|
|
836
|
+
return mapping?.shortId || null;
|
|
837
|
+
} catch {
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Resolve a partial UUID (8 characters) to full UUID by searching database
|
|
843
|
+
*/
|
|
844
|
+
async resolvePartialUuid(partialId, taskOps) {
|
|
845
|
+
if (!/^[0-9a-f]{8}$/i.test(partialId)) {
|
|
846
|
+
return partialId;
|
|
847
|
+
}
|
|
848
|
+
const allTasks = await taskOps.getTasks("all");
|
|
849
|
+
const matches = allTasks.filter((t) => t.id.toLowerCase().startsWith(partialId.toLowerCase()));
|
|
850
|
+
if (matches.length === 0) {
|
|
851
|
+
throw new Error(
|
|
852
|
+
`No task found starting with "${partialId}". Run 'vibetasks list' to see all tasks.`
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
if (matches.length > 1) {
|
|
856
|
+
const shortIds = matches.map((t) => t.short_id).filter(Boolean);
|
|
857
|
+
const suggestion = shortIds.length > 0 ? `Try using short ID: ${shortIds.join(" or ")}` : "Provide more characters to narrow down";
|
|
858
|
+
throw new Error(
|
|
859
|
+
`Multiple tasks match "${partialId}". ${suggestion}`
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
return matches[0].id;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Resolve an ID that could be either short (1-9999), partial UUID (8 chars), or full UUID
|
|
866
|
+
*/
|
|
867
|
+
async resolveId(idOrShortId, taskOps) {
|
|
868
|
+
const isOnlyNumeric = /^\d+$/.test(idOrShortId);
|
|
869
|
+
if (isOnlyNumeric) {
|
|
870
|
+
const shortId = parseInt(idOrShortId, 10);
|
|
871
|
+
if (shortId >= 1 && shortId <= this.maxIds) {
|
|
872
|
+
const fullId = await this.getFullId(shortId);
|
|
873
|
+
if (fullId) {
|
|
874
|
+
return fullId;
|
|
875
|
+
}
|
|
876
|
+
throw new Error(
|
|
877
|
+
`Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (taskOps && /^[0-9a-f]{8}$/i.test(idOrShortId)) {
|
|
882
|
+
return await this.resolvePartialUuid(idOrShortId, taskOps);
|
|
883
|
+
}
|
|
884
|
+
return idOrShortId;
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
|
|
634
888
|
// src/commands/add.ts
|
|
635
|
-
var addCommand = new Command2("add").description("Add a new task").argument("<title>", "Task title").option("-n, --notes <notes>", "Task notes (markdown supported)").option("-d, --due <date>", 'Due date (YYYY-MM-DD, "today", "tomorrow", "+3d")').option("-p, --priority <level>", "Priority: low, medium, high", "none").option("-t, --tags <tags...>", "Tags (space-separated)").option("-s, --subtasks <subtasks...>", "Subtasks (space-separated, use quotes for multi-word)").option("--project <name>", "Override auto-detected project").option("-e, --energy <level>", "Energy required: low, medium, high").action(async (title, options) => {
|
|
889
|
+
var addCommand = new Command2("add").description("Add a new task").argument("<title>", "Task title").option("-n, --notes <notes>", "Task notes (markdown supported)").option("-d, --due <date>", 'Due date (YYYY-MM-DD, "today", "tomorrow", "+3d")').option("-p, --priority <level>", "Priority: low, medium, high", "none").option("-t, --tags <tags...>", "Tags (space-separated)").option("-s, --subtasks <subtasks...>", "Subtasks (space-separated, use quotes for multi-word)").option("--subtasks-json <json>", "Rich subtasks as JSON (with instructions, links, warnings, etc.)").option("--project <name>", "Override auto-detected project").option("-e, --energy <level>", "Energy required: low, medium, high").option("-a, --acceptance <criteria...>", 'Acceptance criteria - "DONE WHEN" conditions (use quotes for multi-word)').option("-f, --files <files...>", "Relevant files for this task").option("--non-goals <goals...>", "Explicit non-goals / out of scope items").option("--for <who>", "Who should complete this: human, ai, both (default: both)").action(async (title, options) => {
|
|
636
890
|
const spinner = ora2("Creating task...").start();
|
|
637
891
|
try {
|
|
638
|
-
const authManager = new
|
|
892
|
+
const authManager = new AuthManager3();
|
|
639
893
|
const taskOps = await TaskOperations.fromAuthManager(authManager);
|
|
640
894
|
let projectTag;
|
|
641
895
|
if (options.project) {
|
|
@@ -670,11 +924,41 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
670
924
|
throw new Error(`Invalid energy level. Must be one of: ${validEnergy.join(", ")}`);
|
|
671
925
|
}
|
|
672
926
|
}
|
|
673
|
-
|
|
927
|
+
let subtasksJson = [];
|
|
928
|
+
if (options.subtasksJson) {
|
|
929
|
+
try {
|
|
930
|
+
const parsed = JSON.parse(options.subtasksJson);
|
|
931
|
+
subtasksJson = Array.isArray(parsed) ? parsed : [parsed];
|
|
932
|
+
subtasksJson = subtasksJson.map((st) => ({
|
|
933
|
+
id: st.id || randomUUID2(),
|
|
934
|
+
title: st.title,
|
|
935
|
+
done: st.done || false,
|
|
936
|
+
notes: st.notes,
|
|
937
|
+
assigned_to: st.assigned_to,
|
|
938
|
+
link: st.link,
|
|
939
|
+
instructions: st.instructions,
|
|
940
|
+
aiPrompt: st.aiPrompt,
|
|
941
|
+
code: st.code,
|
|
942
|
+
warning: st.warning,
|
|
943
|
+
tip: st.tip
|
|
944
|
+
}));
|
|
945
|
+
} catch (error) {
|
|
946
|
+
throw new Error(`Invalid subtasks JSON: ${error.message}`);
|
|
947
|
+
}
|
|
948
|
+
} else if (options.subtasks) {
|
|
949
|
+
subtasksJson = options.subtasks.map((subtaskTitle) => ({
|
|
950
|
+
id: randomUUID2(),
|
|
951
|
+
title: subtaskTitle,
|
|
952
|
+
done: false
|
|
953
|
+
}));
|
|
954
|
+
}
|
|
955
|
+
const acceptanceCriteria = options.acceptance?.map((text) => ({
|
|
674
956
|
id: randomUUID2(),
|
|
675
|
-
|
|
957
|
+
text,
|
|
676
958
|
done: false
|
|
677
959
|
})) || [];
|
|
960
|
+
const relevantFiles = options.files || [];
|
|
961
|
+
const nonGoals = options.nonGoals || [];
|
|
678
962
|
let sessionId;
|
|
679
963
|
let sessionHistory = [];
|
|
680
964
|
const sessionManager2 = getSessionManager();
|
|
@@ -687,6 +971,7 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
687
971
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
688
972
|
}];
|
|
689
973
|
}
|
|
974
|
+
const assignedTo = options.for === "human" ? "human" : options.for === "ai" ? "ai" : options.for === "both" ? "both" : "both";
|
|
690
975
|
const task = await taskOps.createTask({
|
|
691
976
|
title,
|
|
692
977
|
notes: options.notes,
|
|
@@ -697,14 +982,20 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
697
982
|
// AUTO-TAGGED!
|
|
698
983
|
created_by: createdBy,
|
|
699
984
|
// ai or human
|
|
985
|
+
assigned_to: assignedTo,
|
|
986
|
+
// who can complete this task
|
|
700
987
|
status: createdBy === "ai" ? "vibing" : "todo",
|
|
701
988
|
// AI tasks start vibing
|
|
702
989
|
energy_required: options.energy,
|
|
703
990
|
subtasks_json: subtasksJson,
|
|
704
991
|
session_id: sessionId,
|
|
705
992
|
// Current AI session
|
|
706
|
-
session_history: sessionHistory
|
|
993
|
+
session_history: sessionHistory,
|
|
707
994
|
// Full history of sessions
|
|
995
|
+
// New AI-assisted planning fields
|
|
996
|
+
acceptance_criteria: acceptanceCriteria.length > 0 ? acceptanceCriteria : void 0,
|
|
997
|
+
relevant_files: relevantFiles.length > 0 ? relevantFiles : void 0,
|
|
998
|
+
non_goals: nonGoals.length > 0 ? nonGoals : void 0
|
|
708
999
|
});
|
|
709
1000
|
if (options.tags && options.tags.length > 0) {
|
|
710
1001
|
const tagIds = [];
|
|
@@ -715,6 +1006,12 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
715
1006
|
await taskOps.linkTaskTags(task.id, tagIds);
|
|
716
1007
|
}
|
|
717
1008
|
spinner.succeed(chalk3.green("Task created!"));
|
|
1009
|
+
try {
|
|
1010
|
+
const shortIdManager = new ShortIdManager();
|
|
1011
|
+
const allTasks = await taskOps.getTasks("all");
|
|
1012
|
+
await shortIdManager.saveMappings(allTasks);
|
|
1013
|
+
} catch (e) {
|
|
1014
|
+
}
|
|
718
1015
|
if (currentSession) {
|
|
719
1016
|
await sessionManager2.logAction({
|
|
720
1017
|
type: "task_created",
|
|
@@ -731,6 +1028,11 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
731
1028
|
console.log(chalk3.gray(`\u{1F916} Created by: ${chalk3.cyan("AI")}`));
|
|
732
1029
|
console.log(chalk3.gray(`\u26A1 Status: ${chalk3.magenta("vibing")}`));
|
|
733
1030
|
}
|
|
1031
|
+
if (assignedTo === "human") {
|
|
1032
|
+
console.log(chalk3.gray(`\u{1F464} For: ${chalk3.yellow("HUMAN TASK")} (AI cannot complete)`));
|
|
1033
|
+
} else if (assignedTo === "ai") {
|
|
1034
|
+
console.log(chalk3.gray(`\u{1F916} For: ${chalk3.cyan("AI TASK")} (human review optional)`));
|
|
1035
|
+
}
|
|
734
1036
|
console.log(chalk3.gray(`ID: ${task.id.substring(0, 8)}...`));
|
|
735
1037
|
console.log(chalk3.white.bold(task.title));
|
|
736
1038
|
if (task.notes) {
|
|
@@ -758,8 +1060,35 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
758
1060
|
console.log(chalk3.cyan(`Subtasks: ${subtasksJson.length}`));
|
|
759
1061
|
subtasksJson.forEach((st, i) => {
|
|
760
1062
|
console.log(chalk3.gray(` ${i + 1}. ${st.title}`));
|
|
1063
|
+
if (st.instructions) {
|
|
1064
|
+
console.log(chalk3.gray(` \u{1F4DD} Has instructions`));
|
|
1065
|
+
}
|
|
1066
|
+
if (st.link) {
|
|
1067
|
+
console.log(chalk3.gray(` \u{1F517} Link: ${st.link.label || st.link.url}`));
|
|
1068
|
+
}
|
|
1069
|
+
if (st.code) {
|
|
1070
|
+
console.log(chalk3.gray(` \u{1F4BB} Has code snippet`));
|
|
1071
|
+
}
|
|
1072
|
+
if (st.warning) {
|
|
1073
|
+
console.log(chalk3.red(` \u26A0\uFE0F ${st.warning}`));
|
|
1074
|
+
}
|
|
1075
|
+
if (st.tip) {
|
|
1076
|
+
console.log(chalk3.blue(` \u{1F4A1} ${st.tip}`));
|
|
1077
|
+
}
|
|
761
1078
|
});
|
|
762
1079
|
}
|
|
1080
|
+
if (acceptanceCriteria.length > 0) {
|
|
1081
|
+
console.log(chalk3.cyan(`Done when:`));
|
|
1082
|
+
acceptanceCriteria.forEach((ac, i) => {
|
|
1083
|
+
console.log(chalk3.gray(` - ${ac.text}`));
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
if (relevantFiles.length > 0) {
|
|
1087
|
+
console.log(chalk3.yellow(`Files: ${relevantFiles.join(", ")}`));
|
|
1088
|
+
}
|
|
1089
|
+
if (nonGoals.length > 0) {
|
|
1090
|
+
console.log(chalk3.gray(`Not in scope: ${nonGoals.join(", ")}`));
|
|
1091
|
+
}
|
|
763
1092
|
console.log();
|
|
764
1093
|
process.exit(0);
|
|
765
1094
|
} catch (error) {
|
|
@@ -783,82 +1112,7 @@ function isRunningInClaudeCode() {
|
|
|
783
1112
|
import { Command as Command3 } from "commander";
|
|
784
1113
|
import chalk4 from "chalk";
|
|
785
1114
|
import Table from "cli-table3";
|
|
786
|
-
import { AuthManager as
|
|
787
|
-
|
|
788
|
-
// src/utils/short-ids.ts
|
|
789
|
-
import { writeFile as writeFile2, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
790
|
-
import { join as join2 } from "path";
|
|
791
|
-
import { homedir as homedir2 } from "os";
|
|
792
|
-
var ShortIdManager = class {
|
|
793
|
-
storePath;
|
|
794
|
-
maxIds = 99;
|
|
795
|
-
constructor() {
|
|
796
|
-
this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Save mappings from a list of tasks with their short_ids
|
|
800
|
-
* Uses the stable short_id from the database
|
|
801
|
-
*/
|
|
802
|
-
async saveMappings(tasks) {
|
|
803
|
-
const mappings = tasks.filter((t) => t.short_id).slice(0, this.maxIds).map((task, index) => ({
|
|
804
|
-
shortId: task.short_id,
|
|
805
|
-
// Use actual short_id from database
|
|
806
|
-
fullId: task.id,
|
|
807
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
808
|
-
}));
|
|
809
|
-
const store = {
|
|
810
|
-
mappings,
|
|
811
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
812
|
-
};
|
|
813
|
-
const dir = join2(homedir2(), ".vibetasks");
|
|
814
|
-
await mkdir2(dir, { recursive: true });
|
|
815
|
-
await writeFile2(this.storePath, JSON.stringify(store, null, 2));
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Get the full UUID for a short ID
|
|
819
|
-
*/
|
|
820
|
-
async getFullId(shortId) {
|
|
821
|
-
try {
|
|
822
|
-
const content = await readFile2(this.storePath, "utf-8");
|
|
823
|
-
const store = JSON.parse(content);
|
|
824
|
-
const mapping = store.mappings.find((m) => m.shortId === shortId);
|
|
825
|
-
return mapping?.fullId || null;
|
|
826
|
-
} catch {
|
|
827
|
-
return null;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* Get the short ID for a full UUID (if it exists in current mappings)
|
|
832
|
-
*/
|
|
833
|
-
async getShortId(fullId) {
|
|
834
|
-
try {
|
|
835
|
-
const content = await readFile2(this.storePath, "utf-8");
|
|
836
|
-
const store = JSON.parse(content);
|
|
837
|
-
const mapping = store.mappings.find((m) => m.fullId === fullId);
|
|
838
|
-
return mapping?.shortId || null;
|
|
839
|
-
} catch {
|
|
840
|
-
return null;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Resolve an ID that could be either short (1-99) or full UUID
|
|
845
|
-
*/
|
|
846
|
-
async resolveId(idOrShortId) {
|
|
847
|
-
const shortId = parseInt(idOrShortId, 10);
|
|
848
|
-
if (!isNaN(shortId) && shortId >= 1 && shortId <= this.maxIds) {
|
|
849
|
-
const fullId = await this.getFullId(shortId);
|
|
850
|
-
if (fullId) {
|
|
851
|
-
return fullId;
|
|
852
|
-
}
|
|
853
|
-
throw new Error(
|
|
854
|
-
`Short ID #${shortId} not found. Run 'vibetasks list --short-ids' first to generate mappings.`
|
|
855
|
-
);
|
|
856
|
-
}
|
|
857
|
-
return idOrShortId;
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
|
|
861
|
-
// src/commands/list.ts
|
|
1115
|
+
import { AuthManager as AuthManager4, TaskOperations as TaskOperations2 } from "@vibetasks/core";
|
|
862
1116
|
var listCommand = new Command3("list").description("List tasks").argument("[filter]", "Filter: all, today, upcoming, completed", "all").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--project <name>", "Filter by project tag").option("--created-by <source>", "Filter by creator: ai or human").option("--status <status>", "Filter by status: todo, vibing, done").option("--session [id]", "Filter by session (current session if no ID given)").option("--my-session", "Only show tasks from current AI session").option("--short-ids", "Show short numeric IDs (1-99) for easy reference").action(async (filter, options) => {
|
|
863
1117
|
try {
|
|
864
1118
|
const validFilters = ["all", "today", "upcoming", "completed"];
|
|
@@ -867,7 +1121,7 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
|
|
|
867
1121
|
console.error(chalk4.gray(`Valid filters: ${validFilters.join(", ")}`));
|
|
868
1122
|
process.exit(1);
|
|
869
1123
|
}
|
|
870
|
-
const authManager = new
|
|
1124
|
+
const authManager = new AuthManager4();
|
|
871
1125
|
const taskOps = await TaskOperations2.fromAuthManager(authManager);
|
|
872
1126
|
let tasks = await taskOps.getTasks(filter);
|
|
873
1127
|
if (options.project) {
|
|
@@ -882,9 +1136,9 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
|
|
|
882
1136
|
tasks = tasks.filter((t) => t.created_by === options.createdBy);
|
|
883
1137
|
}
|
|
884
1138
|
if (options.status) {
|
|
885
|
-
const validStatuses = ["todo", "vibing", "done"];
|
|
1139
|
+
const validStatuses = ["todo", "vibing", "done", "paused", "archived"];
|
|
886
1140
|
if (!validStatuses.includes(options.status)) {
|
|
887
|
-
console.error(chalk4.red(`Invalid status filter. Must be: todo, vibing, or
|
|
1141
|
+
console.error(chalk4.red(`Invalid status filter. Must be: todo, vibing, done, paused, or archived`));
|
|
888
1142
|
process.exit(1);
|
|
889
1143
|
}
|
|
890
1144
|
tasks = tasks.filter((t) => t.status === options.status);
|
|
@@ -980,7 +1234,13 @@ ${filterMessages[filter] || "No tasks found"}.
|
|
|
980
1234
|
const id = task.id.substring(0, 8);
|
|
981
1235
|
const status = statusColor(`${statusEmojis[task.status || "todo"]} ${task.status || "todo"}`);
|
|
982
1236
|
const title = task.status === "done" ? chalk4.strikethrough(task.title) : task.title;
|
|
983
|
-
|
|
1237
|
+
let titleWithIndicators = title;
|
|
1238
|
+
if (task.assigned_to === "human") {
|
|
1239
|
+
titleWithIndicators = `\u{1F464} ${title}`;
|
|
1240
|
+
} else if (task.created_by === "ai") {
|
|
1241
|
+
titleWithIndicators = `\u{1F916} ${title}`;
|
|
1242
|
+
}
|
|
1243
|
+
const titleWithAI = titleWithIndicators;
|
|
984
1244
|
const project = task.project_tag ? chalk4.blue(task.project_tag) : chalk4.gray("-");
|
|
985
1245
|
const priority = priorityColor(task.priority || "none");
|
|
986
1246
|
const dueDate = task.due_date ? task.due_date.split("T")[0] : chalk4.gray("-");
|
|
@@ -1030,17 +1290,17 @@ Error: ${error.message}
|
|
|
1030
1290
|
import { Command as Command4 } from "commander";
|
|
1031
1291
|
import ora3 from "ora";
|
|
1032
1292
|
import chalk5 from "chalk";
|
|
1033
|
-
import { AuthManager as
|
|
1293
|
+
import { AuthManager as AuthManager5, TaskOperations as TaskOperations3 } from "@vibetasks/core";
|
|
1034
1294
|
var doneCommand = new Command4("done").description("Mark a task as complete").argument("<id>", "Task ID, short # (1-99), or first 8 characters").option("-n, --notes <notes>", "Completion notes - what was done, any final notes").option("-c, --context <context>", "Context notes (alias for --notes)").action(async (id, options) => {
|
|
1035
1295
|
const spinner = ora3("Completing task...").start();
|
|
1036
1296
|
try {
|
|
1037
|
-
const authManager = new
|
|
1297
|
+
const authManager = new AuthManager5();
|
|
1038
1298
|
const taskOps = await TaskOperations3.fromAuthManager(authManager);
|
|
1039
1299
|
const shortIdManager = new ShortIdManager();
|
|
1040
|
-
let
|
|
1041
|
-
if (
|
|
1300
|
+
let taskId2 = await shortIdManager.resolveId(id, taskOps);
|
|
1301
|
+
if (taskId2.length < 32) {
|
|
1042
1302
|
const allTasks = await taskOps.getTasks("all");
|
|
1043
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(
|
|
1303
|
+
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId2));
|
|
1044
1304
|
if (!matchingTask) {
|
|
1045
1305
|
spinner.fail(chalk5.red("Task not found"));
|
|
1046
1306
|
console.error(chalk5.gray(`
|
|
@@ -1048,11 +1308,11 @@ No task found with ID starting with: ${id}
|
|
|
1048
1308
|
`));
|
|
1049
1309
|
process.exit(1);
|
|
1050
1310
|
}
|
|
1051
|
-
|
|
1311
|
+
taskId2 = matchingTask.id;
|
|
1052
1312
|
}
|
|
1053
1313
|
const sessionManager2 = getSessionManager();
|
|
1054
1314
|
const currentSession = await sessionManager2.getOrCreateSession();
|
|
1055
|
-
const existingTask = await taskOps.getTask(
|
|
1315
|
+
const existingTask = await taskOps.getTask(taskId2);
|
|
1056
1316
|
const existingHistory = existingTask?.session_history || [];
|
|
1057
1317
|
const updatedHistory = [
|
|
1058
1318
|
...existingHistory,
|
|
@@ -1071,8 +1331,8 @@ No task found with ID starting with: ${id}
|
|
|
1071
1331
|
if (contextNotes) {
|
|
1072
1332
|
updatePayload.context_notes = contextNotes;
|
|
1073
1333
|
}
|
|
1074
|
-
await taskOps.updateTask(
|
|
1075
|
-
const task = await taskOps.completeTask(
|
|
1334
|
+
await taskOps.updateTask(taskId2, updatePayload);
|
|
1335
|
+
const task = await taskOps.completeTask(taskId2);
|
|
1076
1336
|
await sessionManager2.logAction({
|
|
1077
1337
|
type: "task_completed",
|
|
1078
1338
|
description: `Completed: "${task.title}"`,
|
|
@@ -1093,10 +1353,22 @@ No task found with ID starting with: ${id}
|
|
|
1093
1353
|
if (error.message.includes("Not authenticated")) {
|
|
1094
1354
|
console.error(chalk5.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
1095
1355
|
console.error(chalk5.gray("Run: taskflow login\n"));
|
|
1356
|
+
} else if (error.message.includes("not found")) {
|
|
1357
|
+
console.error(chalk5.red(`
|
|
1358
|
+
Task not found: ${id}`));
|
|
1359
|
+
console.error(chalk5.gray('Tip: Run "vibetasks list" to see all tasks\n'));
|
|
1360
|
+
} else if (error.message.includes("JSON")) {
|
|
1361
|
+
console.error(chalk5.red(`
|
|
1362
|
+
Database error: ${error.message}`));
|
|
1363
|
+
console.error(chalk5.gray("This might be a database schema issue. Check task status in the web dashboard.\n"));
|
|
1096
1364
|
} else {
|
|
1097
1365
|
console.error(chalk5.red(`
|
|
1098
|
-
Error: ${error.message}
|
|
1099
|
-
`
|
|
1366
|
+
Error: ${error.message}`));
|
|
1367
|
+
console.error(chalk5.gray(`
|
|
1368
|
+
Task ID: ${taskId || id}`));
|
|
1369
|
+
console.error(chalk5.gray("Full error details:"));
|
|
1370
|
+
console.error(error);
|
|
1371
|
+
console.log();
|
|
1100
1372
|
}
|
|
1101
1373
|
process.exit(1);
|
|
1102
1374
|
}
|
|
@@ -1107,16 +1379,20 @@ import { Command as Command5 } from "commander";
|
|
|
1107
1379
|
import chalk6 from "chalk";
|
|
1108
1380
|
import ora4 from "ora";
|
|
1109
1381
|
import inquirer from "inquirer";
|
|
1110
|
-
import {
|
|
1111
|
-
|
|
1382
|
+
import { spawn, execSync } from "child_process";
|
|
1383
|
+
import path from "path";
|
|
1384
|
+
import { fileURLToPath } from "url";
|
|
1385
|
+
import { AuthManager as AuthManager6, TaskOperations as TaskOperations4, createBranchName } from "@vibetasks/core";
|
|
1386
|
+
var __dirname2 = path.dirname(fileURLToPath(import.meta.url));
|
|
1387
|
+
var vibingCommand = new Command5("vibing").alias("start").alias("v").description("Start working on a task (move to vibing status)").argument("[task-id]", "Task ID or short # (1-99) to start vibing on").option("-p, --pick", "Pick from todo tasks interactively").option("--no-branch", "Skip automatic Git branch creation").option("--branch-type <type>", "Branch type: feature, fix, bugfix, hotfix, chore", "feature").option("--autonomous", "Launch autonomous Ralph loop (external orchestration)").option("--max-iterations <n>", "Maximum iterations for autonomous loop", "10").option("--agent <type>", "Agent to use: vibebot|opencode|claude", "vibebot").option("--no-verify-build", "Skip build verification in autonomous mode").option("--no-verify-tests", "Skip test verification in autonomous mode").action(async (taskIdInput, options) => {
|
|
1112
1388
|
const spinner = ora4();
|
|
1113
1389
|
try {
|
|
1114
|
-
const authManager = new
|
|
1390
|
+
const authManager = new AuthManager6();
|
|
1115
1391
|
const taskOps = await TaskOperations4.fromAuthManager(authManager);
|
|
1116
1392
|
const shortIdManager = new ShortIdManager();
|
|
1117
|
-
let
|
|
1393
|
+
let taskId2;
|
|
1118
1394
|
if (taskIdInput) {
|
|
1119
|
-
|
|
1395
|
+
taskId2 = await shortIdManager.resolveId(taskIdInput, taskOps);
|
|
1120
1396
|
}
|
|
1121
1397
|
const allTasks = await taskOps.getTasks("all");
|
|
1122
1398
|
const vibingTasks = allTasks.filter((t) => t.status === "vibing" && !t.completed);
|
|
@@ -1124,7 +1400,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1124
1400
|
console.log(chalk6.gray(`
|
|
1125
1401
|
Currently vibing on ${vibingTasks.length} task(s)`));
|
|
1126
1402
|
}
|
|
1127
|
-
if (!
|
|
1403
|
+
if (!taskId2 || options.pick) {
|
|
1128
1404
|
const todoTasks = allTasks.filter((t) => t.status === "todo" && !t.completed);
|
|
1129
1405
|
if (todoTasks.length === 0) {
|
|
1130
1406
|
console.log(chalk6.yellow('\nNo todo tasks found. Add one with `vibetasks add "task title"`\n'));
|
|
@@ -1139,10 +1415,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1139
1415
|
value: t.id
|
|
1140
1416
|
}))
|
|
1141
1417
|
}]);
|
|
1142
|
-
|
|
1418
|
+
taskId2 = selectedTask;
|
|
1143
1419
|
}
|
|
1144
1420
|
spinner.start("Starting task...");
|
|
1145
|
-
const task = allTasks.find((t) => t.id ===
|
|
1421
|
+
const task = allTasks.find((t) => t.id === taskId2);
|
|
1146
1422
|
const sessionManager2 = getSessionManager();
|
|
1147
1423
|
const currentSession = await sessionManager2.getOrCreateSession();
|
|
1148
1424
|
const existingSessionHistory = task?.session_history || [];
|
|
@@ -1158,7 +1434,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1158
1434
|
}
|
|
1159
1435
|
];
|
|
1160
1436
|
}
|
|
1161
|
-
await taskOps.updateTask(
|
|
1437
|
+
await taskOps.updateTask(taskId2, {
|
|
1162
1438
|
status: "vibing",
|
|
1163
1439
|
completed: false,
|
|
1164
1440
|
session_id: currentSession.id,
|
|
@@ -1167,16 +1443,88 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1167
1443
|
});
|
|
1168
1444
|
spinner.succeed(chalk6.green("Now vibing on:"));
|
|
1169
1445
|
console.log(chalk6.white(`
|
|
1170
|
-
${task?.title ||
|
|
1446
|
+
${task?.title || taskId2}
|
|
1171
1447
|
`));
|
|
1172
1448
|
if (lastSession && lastSession.session_id !== currentSession.id) {
|
|
1173
1449
|
console.log(chalk6.cyan(` \u{1F4CB} Continuing from session: ${lastSession.session_id}`));
|
|
1174
1450
|
console.log(chalk6.gray(` This task has been worked on by ${existingSessionHistory.length} session(s)
|
|
1175
1451
|
`));
|
|
1176
1452
|
}
|
|
1453
|
+
if (options.branch !== false && task) {
|
|
1454
|
+
const branchType = options.branchType || "feature";
|
|
1455
|
+
const branchName = createBranchName(
|
|
1456
|
+
{ short_id: task.short_id, title: task.title },
|
|
1457
|
+
branchType
|
|
1458
|
+
);
|
|
1459
|
+
console.log(chalk6.cyan(`
|
|
1460
|
+
\u{1F33F} Creating branch: ${branchName}`));
|
|
1461
|
+
try {
|
|
1462
|
+
execSync("git rev-parse --git-dir", { stdio: "pipe" });
|
|
1463
|
+
try {
|
|
1464
|
+
execSync(`git rev-parse --verify ${branchName}`, { stdio: "pipe" });
|
|
1465
|
+
console.log(chalk6.yellow(` Branch already exists. Checking out...`));
|
|
1466
|
+
execSync(`git checkout ${branchName}`, { stdio: "inherit" });
|
|
1467
|
+
} catch {
|
|
1468
|
+
execSync(`git checkout -b ${branchName}`, { stdio: "inherit" });
|
|
1469
|
+
}
|
|
1470
|
+
console.log(chalk6.green(` \u2713 On branch: ${branchName}`));
|
|
1471
|
+
console.log(chalk6.gray(` When you merge a PR from this branch, VT-${task.short_id} will auto-complete!
|
|
1472
|
+
`));
|
|
1473
|
+
await taskOps.updateTask(taskId2, {
|
|
1474
|
+
context_notes: `Branch: ${branchName}
|
|
1475
|
+
${task.context_notes || ""}`
|
|
1476
|
+
});
|
|
1477
|
+
} catch (err) {
|
|
1478
|
+
console.log(chalk6.yellow(` \u26A0\uFE0F Not in a git repo or git error: ${err.message}`));
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
if (options.autonomous) {
|
|
1482
|
+
console.log(chalk6.cyan("\n\u{1F3B5} Starting autonomous Ralph loop...\n"));
|
|
1483
|
+
console.log(chalk6.gray(` Agent: ${options.agent}`));
|
|
1484
|
+
console.log(chalk6.gray(` Max Iterations: ${options.maxIterations}`));
|
|
1485
|
+
console.log(chalk6.gray(` Build Verification: ${options.verifyBuild !== false ? "enabled" : "disabled"}`));
|
|
1486
|
+
console.log(chalk6.gray(` Test Verification: ${options.verifyTests !== false ? "enabled" : "disabled"}`));
|
|
1487
|
+
console.log(chalk6.gray(`
|
|
1488
|
+
Press Ctrl+C to stop the loop at any time.
|
|
1489
|
+
`));
|
|
1490
|
+
const scriptPath = path.join(__dirname2, "../../scripts/vibing-loop.sh");
|
|
1491
|
+
const env = {
|
|
1492
|
+
...process.env,
|
|
1493
|
+
VERIFY_BUILD: options.verifyBuild !== false ? "true" : "false",
|
|
1494
|
+
VERIFY_TESTS: options.verifyTests !== false ? "true" : "false"
|
|
1495
|
+
};
|
|
1496
|
+
const loopProcess = spawn("bash", [
|
|
1497
|
+
scriptPath,
|
|
1498
|
+
taskId2,
|
|
1499
|
+
options.maxIterations || "10",
|
|
1500
|
+
options.agent || "vibebot"
|
|
1501
|
+
], {
|
|
1502
|
+
stdio: "inherit",
|
|
1503
|
+
cwd: process.cwd(),
|
|
1504
|
+
env
|
|
1505
|
+
});
|
|
1506
|
+
loopProcess.on("exit", (code) => {
|
|
1507
|
+
if (code === 0) {
|
|
1508
|
+
console.log(chalk6.green("\n\u2705 Autonomous loop completed successfully\n"));
|
|
1509
|
+
} else if (code === null) {
|
|
1510
|
+
console.log(chalk6.yellow("\n\u26A0\uFE0F Loop interrupted by user\n"));
|
|
1511
|
+
} else {
|
|
1512
|
+
console.log(chalk6.red(`
|
|
1513
|
+
\u274C Loop exited with code ${code}
|
|
1514
|
+
`));
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1517
|
+
loopProcess.on("error", (err) => {
|
|
1518
|
+
console.error(chalk6.red("\n\u274C Failed to start loop:"), err.message);
|
|
1519
|
+
process.exit(1);
|
|
1520
|
+
});
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1177
1523
|
console.log(chalk6.gray(" Tips:"));
|
|
1178
1524
|
console.log(chalk6.gray(" \u2022 Update context notes as you work"));
|
|
1179
|
-
console.log(chalk6.gray(" \u2022 Run `vibetasks done " +
|
|
1525
|
+
console.log(chalk6.gray(" \u2022 Run `vibetasks done " + taskId2.slice(0, 8) + "` when finished"));
|
|
1526
|
+
console.log(chalk6.gray(" \u2022 Use --autonomous flag to start Ralph loop"));
|
|
1527
|
+
console.log(chalk6.gray(" \u2022 Use --no-branch to skip auto branch creation"));
|
|
1180
1528
|
console.log(chalk6.gray(' \u2022 In Claude Code, say "show my vibing tasks"\n'));
|
|
1181
1529
|
} catch (error) {
|
|
1182
1530
|
spinner.fail(chalk6.red("Failed to start task"));
|
|
@@ -1189,10 +1537,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1189
1537
|
import { Command as Command6 } from "commander";
|
|
1190
1538
|
import chalk7 from "chalk";
|
|
1191
1539
|
import Table2 from "cli-table3";
|
|
1192
|
-
import { AuthManager as
|
|
1540
|
+
import { AuthManager as AuthManager7, TaskOperations as TaskOperations5 } from "@vibetasks/core";
|
|
1193
1541
|
var searchCommand = new Command6("search").description("Search tasks by title").argument("<query>", "Search query").option("-l, --limit <number>", "Maximum number of results", "20").action(async (query, options) => {
|
|
1194
1542
|
try {
|
|
1195
|
-
const authManager = new
|
|
1543
|
+
const authManager = new AuthManager7();
|
|
1196
1544
|
const taskOps = await TaskOperations5.fromAuthManager(authManager);
|
|
1197
1545
|
const limit = parseInt(options.limit, 10);
|
|
1198
1546
|
const tasks = await taskOps.searchTasks(query, limit);
|
|
@@ -1249,7 +1597,7 @@ Error: ${error.message}
|
|
|
1249
1597
|
import { Command as Command7 } from "commander";
|
|
1250
1598
|
import ora5 from "ora";
|
|
1251
1599
|
import chalk8 from "chalk";
|
|
1252
|
-
import { AuthManager as
|
|
1600
|
+
import { AuthManager as AuthManager8, TaskOperations as TaskOperations6 } from "@vibetasks/core";
|
|
1253
1601
|
var updateCommand = new Command7("update").description("Update a task").argument("<id>", "Task ID (full or first 8 characters)").option("-t, --title <title>", "New title").option("-n, --notes <notes>", "New notes").option("-d, --due <date>", "New due date").option("-p, --priority <level>", "New priority: low, medium, high, none").option("-s, --status <status>", "New status: todo, vibing, done").option("--project <name>", "New project tag").option("-e, --energy <level>", "Energy required: low, medium, high").option("-c, --context <notes>", "Update context notes").action(async (id, options) => {
|
|
1254
1602
|
if (!options.title && !options.notes && !options.due && !options.priority && !options.status && !options.project && !options.energy && !options.context) {
|
|
1255
1603
|
console.error(chalk8.red("\nError: No updates specified"));
|
|
@@ -1258,21 +1606,10 @@ var updateCommand = new Command7("update").description("Update a task").argument
|
|
|
1258
1606
|
}
|
|
1259
1607
|
const spinner = ora5("Updating task...").start();
|
|
1260
1608
|
try {
|
|
1261
|
-
const authManager = new
|
|
1609
|
+
const authManager = new AuthManager8();
|
|
1262
1610
|
const taskOps = await TaskOperations6.fromAuthManager(authManager);
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
const allTasks = await taskOps.getTasks("all");
|
|
1266
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(id));
|
|
1267
|
-
if (!matchingTask) {
|
|
1268
|
-
spinner.fail(chalk8.red("Task not found"));
|
|
1269
|
-
console.error(chalk8.gray(`
|
|
1270
|
-
No task found with ID starting with: ${id}
|
|
1271
|
-
`));
|
|
1272
|
-
process.exit(1);
|
|
1273
|
-
}
|
|
1274
|
-
taskId = matchingTask.id;
|
|
1275
|
-
}
|
|
1611
|
+
const shortIdManager = new ShortIdManager();
|
|
1612
|
+
let taskId2 = await shortIdManager.resolveId(id, taskOps);
|
|
1276
1613
|
const updates = {};
|
|
1277
1614
|
if (options.title) updates.title = options.title;
|
|
1278
1615
|
if (options.notes) updates.notes = options.notes;
|
|
@@ -1300,7 +1637,7 @@ No task found with ID starting with: ${id}
|
|
|
1300
1637
|
}
|
|
1301
1638
|
updates.energy_required = options.energy;
|
|
1302
1639
|
}
|
|
1303
|
-
const task = await taskOps.updateTask(
|
|
1640
|
+
const task = await taskOps.updateTask(taskId2, updates);
|
|
1304
1641
|
spinner.succeed(chalk8.green("Task updated!"));
|
|
1305
1642
|
console.log(chalk8.gray(`
|
|
1306
1643
|
\u2713 ${task.title}
|
|
@@ -1325,28 +1662,15 @@ import { Command as Command8 } from "commander";
|
|
|
1325
1662
|
import ora6 from "ora";
|
|
1326
1663
|
import chalk9 from "chalk";
|
|
1327
1664
|
import inquirer2 from "inquirer";
|
|
1328
|
-
import { AuthManager as
|
|
1665
|
+
import { AuthManager as AuthManager9, TaskOperations as TaskOperations7 } from "@vibetasks/core";
|
|
1329
1666
|
var deleteCommand = new Command8("delete").description("Delete a task").argument("<id>", "Task ID (full or first 8 characters)").option("-y, --yes", "Skip confirmation").action(async (id, options) => {
|
|
1330
1667
|
try {
|
|
1331
|
-
const authManager = new
|
|
1668
|
+
const authManager = new AuthManager9();
|
|
1332
1669
|
const taskOps = await TaskOperations7.fromAuthManager(authManager);
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(id));
|
|
1338
|
-
if (!matchingTask) {
|
|
1339
|
-
console.error(chalk9.red("\nTask not found"));
|
|
1340
|
-
console.error(chalk9.gray(`No task found with ID starting with: ${id}
|
|
1341
|
-
`));
|
|
1342
|
-
process.exit(1);
|
|
1343
|
-
}
|
|
1344
|
-
taskId = matchingTask.id;
|
|
1345
|
-
taskTitle = matchingTask.title;
|
|
1346
|
-
} else {
|
|
1347
|
-
const task = await taskOps.getTask(taskId);
|
|
1348
|
-
taskTitle = task.title;
|
|
1349
|
-
}
|
|
1670
|
+
const shortIdManager = new ShortIdManager();
|
|
1671
|
+
const taskId2 = await shortIdManager.resolveId(id, taskOps);
|
|
1672
|
+
const task = await taskOps.getTask(taskId2);
|
|
1673
|
+
const taskTitle = task.title;
|
|
1350
1674
|
if (!options.yes) {
|
|
1351
1675
|
const answers = await inquirer2.prompt([
|
|
1352
1676
|
{
|
|
@@ -1362,7 +1686,7 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
|
|
|
1362
1686
|
}
|
|
1363
1687
|
}
|
|
1364
1688
|
const spinner = ora6("Deleting task...").start();
|
|
1365
|
-
await taskOps.deleteTask(
|
|
1689
|
+
await taskOps.deleteTask(taskId2);
|
|
1366
1690
|
spinner.succeed(chalk9.green("Task deleted!"));
|
|
1367
1691
|
console.log(chalk9.gray(`
|
|
1368
1692
|
\u2717 ${taskTitle}
|
|
@@ -1384,10 +1708,10 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
|
|
|
1384
1708
|
// src/commands/config.ts
|
|
1385
1709
|
import { Command as Command9 } from "commander";
|
|
1386
1710
|
import chalk10 from "chalk";
|
|
1387
|
-
import { AuthManager as
|
|
1711
|
+
import { AuthManager as AuthManager10 } from "@vibetasks/core";
|
|
1388
1712
|
var configCommand = new Command9("config").description("View or set configuration").argument("[key]", "Configuration key").argument("[value]", "Configuration value").action(async (key, value) => {
|
|
1389
1713
|
try {
|
|
1390
|
-
const authManager = new
|
|
1714
|
+
const authManager = new AuthManager10();
|
|
1391
1715
|
if (!key) {
|
|
1392
1716
|
const configManager = authManager["configManager"];
|
|
1393
1717
|
const config = await configManager.getConfig();
|
|
@@ -1446,17 +1770,17 @@ import { Command as Command10 } from "commander";
|
|
|
1446
1770
|
import chalk11 from "chalk";
|
|
1447
1771
|
import ora7 from "ora";
|
|
1448
1772
|
import fs from "fs/promises";
|
|
1449
|
-
import
|
|
1773
|
+
import path2 from "path";
|
|
1450
1774
|
function getVibetasksHooksPath() {
|
|
1451
|
-
const cliRoot =
|
|
1775
|
+
const cliRoot = path2.dirname(path2.dirname(path2.dirname(new URL(import.meta.url).pathname)));
|
|
1452
1776
|
const normalizedPath = process.platform === "win32" && cliRoot.startsWith("/") ? cliRoot.slice(1) : cliRoot;
|
|
1453
|
-
return
|
|
1777
|
+
return path2.join(normalizedPath, "hooks");
|
|
1454
1778
|
}
|
|
1455
1779
|
var hooksCommand = new Command10("hooks").description("Manage hooks integration (git and Claude Code)");
|
|
1456
1780
|
hooksCommand.command("install").description("Install git hooks in current repository").action(async () => {
|
|
1457
1781
|
const spinner = ora7("Installing git hooks...").start();
|
|
1458
1782
|
try {
|
|
1459
|
-
const gitDir =
|
|
1783
|
+
const gitDir = path2.join(process.cwd(), ".git");
|
|
1460
1784
|
try {
|
|
1461
1785
|
await fs.access(gitDir);
|
|
1462
1786
|
} catch {
|
|
@@ -1464,9 +1788,9 @@ hooksCommand.command("install").description("Install git hooks in current reposi
|
|
|
1464
1788
|
console.error(chalk11.gray("\nRun this command from the root of a git repository.\n"));
|
|
1465
1789
|
process.exit(1);
|
|
1466
1790
|
}
|
|
1467
|
-
const hooksDir =
|
|
1791
|
+
const hooksDir = path2.join(gitDir, "hooks");
|
|
1468
1792
|
await fs.mkdir(hooksDir, { recursive: true });
|
|
1469
|
-
const postCommitPath =
|
|
1793
|
+
const postCommitPath = path2.join(hooksDir, "post-commit");
|
|
1470
1794
|
const hookScript = `#!/bin/sh
|
|
1471
1795
|
# VibeTasks post-commit hook
|
|
1472
1796
|
# Automatically updates tasks based on commit messages
|
|
@@ -1520,7 +1844,7 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
|
|
|
1520
1844
|
try {
|
|
1521
1845
|
const projectPath = options.project || process.cwd();
|
|
1522
1846
|
const hooksPath = getVibetasksHooksPath();
|
|
1523
|
-
const syncScriptPath =
|
|
1847
|
+
const syncScriptPath = path2.join(hooksPath, "sync-todos.js");
|
|
1524
1848
|
const hookConfig = {
|
|
1525
1849
|
hooks: {
|
|
1526
1850
|
SubagentStop: [
|
|
@@ -1539,9 +1863,9 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
|
|
|
1539
1863
|
console.log(JSON.stringify(hookConfig, null, 2));
|
|
1540
1864
|
process.exit(0);
|
|
1541
1865
|
}
|
|
1542
|
-
const claudeDir =
|
|
1866
|
+
const claudeDir = path2.join(projectPath, ".claude");
|
|
1543
1867
|
await fs.mkdir(claudeDir, { recursive: true });
|
|
1544
|
-
const settingsPath =
|
|
1868
|
+
const settingsPath = path2.join(claudeDir, "settings.json");
|
|
1545
1869
|
let existingSettings = {};
|
|
1546
1870
|
try {
|
|
1547
1871
|
const data = await fs.readFile(settingsPath, "utf-8");
|
|
@@ -1586,8 +1910,8 @@ Error: ${error.message}
|
|
|
1586
1910
|
});
|
|
1587
1911
|
hooksCommand.command("status").description("Show status of installed hooks").action(async () => {
|
|
1588
1912
|
console.log(chalk11.blue.bold("\n VibeTasks Hooks Status\n"));
|
|
1589
|
-
const gitHooksDir =
|
|
1590
|
-
const postCommitPath =
|
|
1913
|
+
const gitHooksDir = path2.join(process.cwd(), ".git", "hooks");
|
|
1914
|
+
const postCommitPath = path2.join(gitHooksDir, "post-commit");
|
|
1591
1915
|
try {
|
|
1592
1916
|
const content = await fs.readFile(postCommitPath, "utf-8");
|
|
1593
1917
|
if (content.includes("vibetasks") || content.includes("VibeTasks")) {
|
|
@@ -1598,7 +1922,7 @@ hooksCommand.command("status").description("Show status of installed hooks").act
|
|
|
1598
1922
|
} catch {
|
|
1599
1923
|
console.log(chalk11.gray(" [ ] Git post-commit hook not installed"));
|
|
1600
1924
|
}
|
|
1601
|
-
const claudeSettingsPath =
|
|
1925
|
+
const claudeSettingsPath = path2.join(process.cwd(), ".claude", "settings.json");
|
|
1602
1926
|
try {
|
|
1603
1927
|
const content = await fs.readFile(claudeSettingsPath, "utf-8");
|
|
1604
1928
|
const settings = JSON.parse(content);
|
|
@@ -1621,12 +1945,12 @@ import { Command as Command11 } from "commander";
|
|
|
1621
1945
|
import inquirer3 from "inquirer";
|
|
1622
1946
|
import ora8 from "ora";
|
|
1623
1947
|
import chalk12 from "chalk";
|
|
1624
|
-
import { AuthManager as
|
|
1948
|
+
import { AuthManager as AuthManager11 } from "@vibetasks/core";
|
|
1625
1949
|
import { detectProject as detectProject2 } from "@vibetasks/shared/utils/project-detector";
|
|
1626
1950
|
var initCommand = new Command11("init").description("Initialize TaskFlow for current project (auto-detect from git)").option("--name <name>", "Manually specify project name").action(async (options) => {
|
|
1627
1951
|
const spinner = ora8("Detecting project...").start();
|
|
1628
1952
|
try {
|
|
1629
|
-
const authManager = new
|
|
1953
|
+
const authManager = new AuthManager11();
|
|
1630
1954
|
const cwd = process.cwd();
|
|
1631
1955
|
let project;
|
|
1632
1956
|
if (options.name) {
|
|
@@ -1686,12 +2010,12 @@ import inquirer4 from "inquirer";
|
|
|
1686
2010
|
import ora9 from "ora";
|
|
1687
2011
|
import chalk13 from "chalk";
|
|
1688
2012
|
import fs2 from "fs/promises";
|
|
1689
|
-
import
|
|
2013
|
+
import path3 from "path";
|
|
1690
2014
|
import os from "os";
|
|
1691
2015
|
import { createServer as createServer2 } from "http";
|
|
1692
2016
|
import { exec as exec2 } from "child_process";
|
|
1693
2017
|
import { promisify as promisify2 } from "util";
|
|
1694
|
-
import { AuthManager as
|
|
2018
|
+
import { AuthManager as AuthManager12, TaskOperations as TaskOperations8, createSupabaseClient } from "@vibetasks/core";
|
|
1695
2019
|
import { detectProject as detectProject3 } from "@vibetasks/shared/utils/project-detector";
|
|
1696
2020
|
var execAsync2 = promisify2(exec2);
|
|
1697
2021
|
var SUPABASE_URL = "https://ihmayqzxqyednchbezya.supabase.co";
|
|
@@ -1728,12 +2052,12 @@ async function getAvailablePort2(startPort = 3737) {
|
|
|
1728
2052
|
});
|
|
1729
2053
|
}
|
|
1730
2054
|
function getClaudeConfigPath() {
|
|
1731
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
1732
|
-
return
|
|
2055
|
+
const configHome = process.env.XDG_CONFIG_HOME || path3.join(os.homedir(), ".config");
|
|
2056
|
+
return path3.join(configHome, "claude-code", "config.json");
|
|
1733
2057
|
}
|
|
1734
2058
|
async function claudeCodeConfigExists() {
|
|
1735
2059
|
try {
|
|
1736
|
-
const configDir =
|
|
2060
|
+
const configDir = path3.dirname(getClaudeConfigPath());
|
|
1737
2061
|
await fs2.access(configDir);
|
|
1738
2062
|
return true;
|
|
1739
2063
|
} catch {
|
|
@@ -1756,7 +2080,7 @@ function showWelcome() {
|
|
|
1756
2080
|
}
|
|
1757
2081
|
async function checkExistingAuth() {
|
|
1758
2082
|
try {
|
|
1759
|
-
const authManager = new
|
|
2083
|
+
const authManager = new AuthManager12();
|
|
1760
2084
|
const token = await authManager.getAccessToken();
|
|
1761
2085
|
if (!token) return { authenticated: false };
|
|
1762
2086
|
const supabaseUrl = await authManager.getConfig("supabase_url") || SUPABASE_URL;
|
|
@@ -1800,7 +2124,7 @@ async function runBrowserAuth() {
|
|
|
1800
2124
|
return;
|
|
1801
2125
|
}
|
|
1802
2126
|
try {
|
|
1803
|
-
const authManager = new
|
|
2127
|
+
const authManager = new AuthManager12();
|
|
1804
2128
|
await authManager.setAccessToken(accessToken);
|
|
1805
2129
|
await authManager.setRefreshToken(refreshToken);
|
|
1806
2130
|
if (supabaseUrl) await authManager.setConfig("supabase_url", supabaseUrl);
|
|
@@ -1893,7 +2217,7 @@ async function stepAuthentication() {
|
|
|
1893
2217
|
]);
|
|
1894
2218
|
const spinner = ora9("Authenticating...").start();
|
|
1895
2219
|
try {
|
|
1896
|
-
const authManager = new
|
|
2220
|
+
const authManager = new AuthManager12();
|
|
1897
2221
|
await authManager.setConfig("supabase_url", SUPABASE_URL);
|
|
1898
2222
|
await authManager.setConfig("supabase_key", SUPABASE_ANON_KEY);
|
|
1899
2223
|
const supabase = createSupabaseClient({
|
|
@@ -1948,7 +2272,7 @@ async function stepClaudeCodeConfig() {
|
|
|
1948
2272
|
const spinner = ora9("Configuring Claude Code...").start();
|
|
1949
2273
|
try {
|
|
1950
2274
|
const configPath = getClaudeConfigPath();
|
|
1951
|
-
const configDir =
|
|
2275
|
+
const configDir = path3.dirname(configPath);
|
|
1952
2276
|
await fs2.mkdir(configDir, { recursive: true });
|
|
1953
2277
|
let config = {};
|
|
1954
2278
|
try {
|
|
@@ -2042,7 +2366,7 @@ async function stepProjectInit() {
|
|
|
2042
2366
|
return { success: true, skipped: true };
|
|
2043
2367
|
}
|
|
2044
2368
|
try {
|
|
2045
|
-
const authManager = new
|
|
2369
|
+
const authManager = new AuthManager12();
|
|
2046
2370
|
await authManager.setConfig(`project_${process.cwd()}`, project.name);
|
|
2047
2371
|
console.log(chalk13.green("\n \u2713") + chalk13.white(` Project "${project.name}" configured`));
|
|
2048
2372
|
return { success: true, projectName: project.name };
|
|
@@ -2059,7 +2383,7 @@ async function stepVerify() {
|
|
|
2059
2383
|
};
|
|
2060
2384
|
const supabaseSpinner = ora9("Testing Supabase connection...").start();
|
|
2061
2385
|
try {
|
|
2062
|
-
const authManager = new
|
|
2386
|
+
const authManager = new AuthManager12();
|
|
2063
2387
|
const taskOps = await TaskOperations8.fromAuthManager(authManager);
|
|
2064
2388
|
const tasks = await taskOps.getTasks("all");
|
|
2065
2389
|
result.supabaseConnected = true;
|
|
@@ -2197,7 +2521,7 @@ async function runNonInteractiveSetup() {
|
|
|
2197
2521
|
}
|
|
2198
2522
|
console.log(chalk13.gray("\nConfiguring Claude Code MCP server..."));
|
|
2199
2523
|
const configPath = getClaudeConfigPath();
|
|
2200
|
-
const configDir =
|
|
2524
|
+
const configDir = path3.dirname(configPath);
|
|
2201
2525
|
try {
|
|
2202
2526
|
await fs2.mkdir(configDir, { recursive: true });
|
|
2203
2527
|
let config = {};
|
|
@@ -2232,7 +2556,7 @@ async function runNonInteractiveSetup() {
|
|
|
2232
2556
|
if (authResult.success) {
|
|
2233
2557
|
console.log(chalk13.gray("\nVerifying connection..."));
|
|
2234
2558
|
try {
|
|
2235
|
-
const authManager = new
|
|
2559
|
+
const authManager = new AuthManager12();
|
|
2236
2560
|
const taskOps = await TaskOperations8.fromAuthManager(authManager);
|
|
2237
2561
|
const tasks = await taskOps.getTasks("all");
|
|
2238
2562
|
console.log(chalk13.green("\u2713") + ` Connected (${tasks.length} tasks)`);
|
|
@@ -2303,7 +2627,7 @@ import { Command as Command13 } from "commander";
|
|
|
2303
2627
|
import chalk14 from "chalk";
|
|
2304
2628
|
import ora10 from "ora";
|
|
2305
2629
|
import inquirer5 from "inquirer";
|
|
2306
|
-
import { AuthManager as
|
|
2630
|
+
import { AuthManager as AuthManager13, TaskOperations as TaskOperations9 } from "@vibetasks/core";
|
|
2307
2631
|
import { detectProject as detectProject4 } from "@vibetasks/shared/utils/project-detector";
|
|
2308
2632
|
import { generateErrorTaskTitle } from "@vibetasks/shared";
|
|
2309
2633
|
var watchCommand = new Command13("watch").description("Monitor clipboard for errors and offer to capture them as tasks").option("-i, --interval <ms>", "Polling interval in milliseconds", "1000").option("-a, --auto", "Auto-create tasks without prompting").option("-q, --quiet", "Minimal output (only show errors)").option("--project <name>", "Override auto-detected project").action(async (options) => {
|
|
@@ -2317,7 +2641,7 @@ var watchCommand = new Command13("watch").description("Monitor clipboard for err
|
|
|
2317
2641
|
let authManager;
|
|
2318
2642
|
let taskOps;
|
|
2319
2643
|
try {
|
|
2320
|
-
authManager = new
|
|
2644
|
+
authManager = new AuthManager13();
|
|
2321
2645
|
taskOps = await TaskOperations9.fromAuthManager(authManager);
|
|
2322
2646
|
} catch (error) {
|
|
2323
2647
|
if (error.message.includes("Not authenticated")) {
|
|
@@ -2527,11 +2851,11 @@ import { Command as Command14 } from "commander";
|
|
|
2527
2851
|
import chalk15 from "chalk";
|
|
2528
2852
|
import ora11 from "ora";
|
|
2529
2853
|
import inquirer6 from "inquirer";
|
|
2530
|
-
import { AuthManager as
|
|
2531
|
-
var archiveCommand = new Command14("archive").description("Archive a task or manage archived tasks").argument("[task-id]", "Task ID to archive (full or first 8 characters)").option("-l, --list", "List all archived tasks").option("-u, --unarchive <id>", "Unarchive a task by ID").option("--pick", "Pick from completed tasks to archive").action(async (
|
|
2854
|
+
import { AuthManager as AuthManager14, TaskOperations as TaskOperations10 } from "@vibetasks/core";
|
|
2855
|
+
var archiveCommand = new Command14("archive").description("Archive a task or manage archived tasks").argument("[task-id]", "Task ID to archive (full or first 8 characters)").option("-l, --list", "List all archived tasks").option("-u, --unarchive <id>", "Unarchive a task by ID").option("--pick", "Pick from completed tasks to archive").action(async (taskId2, options) => {
|
|
2532
2856
|
const spinner = ora11();
|
|
2533
2857
|
try {
|
|
2534
|
-
const authManager = new
|
|
2858
|
+
const authManager = new AuthManager14();
|
|
2535
2859
|
const taskOps = await TaskOperations10.fromAuthManager(authManager);
|
|
2536
2860
|
if (options.list) {
|
|
2537
2861
|
spinner.start("Fetching archived tasks...");
|
|
@@ -2577,7 +2901,7 @@ Restored: ${task2.title}`));
|
|
|
2577
2901
|
`));
|
|
2578
2902
|
process.exit(0);
|
|
2579
2903
|
}
|
|
2580
|
-
if (options.pick || !
|
|
2904
|
+
if (options.pick || !taskId2) {
|
|
2581
2905
|
const allTasks = await taskOps.getTasks("completed");
|
|
2582
2906
|
if (allTasks.length === 0) {
|
|
2583
2907
|
console.log(chalk15.yellow("\nNo completed tasks to archive. Complete some tasks first!\n"));
|
|
@@ -2592,16 +2916,16 @@ Restored: ${task2.title}`));
|
|
|
2592
2916
|
value: t.id
|
|
2593
2917
|
}))
|
|
2594
2918
|
}]);
|
|
2595
|
-
|
|
2919
|
+
taskId2 = selectedTask;
|
|
2596
2920
|
}
|
|
2597
|
-
if (!
|
|
2921
|
+
if (!taskId2) {
|
|
2598
2922
|
console.log(chalk15.yellow("\nUsage: vibetasks archive <task-id>"));
|
|
2599
2923
|
console.log(chalk15.gray(" or: vibetasks archive --pick"));
|
|
2600
2924
|
console.log(chalk15.gray(" or: vibetasks archive --list"));
|
|
2601
2925
|
console.log(chalk15.gray(" or: vibetasks archive --unarchive <id>\n"));
|
|
2602
2926
|
process.exit(1);
|
|
2603
2927
|
}
|
|
2604
|
-
const validTaskId =
|
|
2928
|
+
const validTaskId = taskId2;
|
|
2605
2929
|
spinner.start("Archiving task...");
|
|
2606
2930
|
let targetId = validTaskId;
|
|
2607
2931
|
if (validTaskId.length < 32) {
|
|
@@ -2610,7 +2934,7 @@ Restored: ${task2.title}`));
|
|
|
2610
2934
|
if (!matchingTask) {
|
|
2611
2935
|
spinner.fail(chalk15.red("Task not found"));
|
|
2612
2936
|
console.error(chalk15.gray(`
|
|
2613
|
-
No task found with ID starting with: ${
|
|
2937
|
+
No task found with ID starting with: ${taskId2}
|
|
2614
2938
|
`));
|
|
2615
2939
|
process.exit(1);
|
|
2616
2940
|
}
|
|
@@ -2643,13 +2967,13 @@ Error: ${error.message}
|
|
|
2643
2967
|
import { Command as Command15 } from "commander";
|
|
2644
2968
|
import chalk16 from "chalk";
|
|
2645
2969
|
import ora12 from "ora";
|
|
2646
|
-
import { spawn } from "child_process";
|
|
2647
|
-
import
|
|
2648
|
-
import { fileURLToPath } from "url";
|
|
2649
|
-
import { AuthManager as
|
|
2970
|
+
import { spawn as spawn2 } from "child_process";
|
|
2971
|
+
import path4 from "path";
|
|
2972
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2973
|
+
import { AuthManager as AuthManager15, TaskOperations as TaskOperations11 } from "@vibetasks/core";
|
|
2650
2974
|
import { generateErrorTaskTitle as generateErrorTaskTitle2 } from "@vibetasks/shared";
|
|
2651
|
-
var __filename2 =
|
|
2652
|
-
var
|
|
2975
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2976
|
+
var __dirname3 = path4.dirname(__filename2);
|
|
2653
2977
|
var daemonCommand = new Command15("daemon").description("Manage the VibeTasks error capture daemon").addCommand(createStartCommand()).addCommand(createStopCommand()).addCommand(createStatusCommand()).addCommand(createConfigureCommand()).addCommand(createCaptureCommand());
|
|
2654
2978
|
function createStartCommand() {
|
|
2655
2979
|
return new Command15("start").description("Start the error capture daemon").option("-f, --foreground", "Run in foreground (useful for debugging)").option("--no-notify", "Disable startup notification").action(async (options) => {
|
|
@@ -2849,8 +3173,8 @@ function createCaptureCommand() {
|
|
|
2849
3173
|
});
|
|
2850
3174
|
}
|
|
2851
3175
|
async function startDaemonBackground(config) {
|
|
2852
|
-
const workerPath =
|
|
2853
|
-
const child =
|
|
3176
|
+
const workerPath = path4.resolve(__dirname3, "..", "daemon-worker.js");
|
|
3177
|
+
const child = spawn2(process.execPath, [workerPath], {
|
|
2854
3178
|
detached: true,
|
|
2855
3179
|
stdio: "ignore",
|
|
2856
3180
|
env: {
|
|
@@ -2970,7 +3294,7 @@ async function showNotification(title, message, type = "info", duration) {
|
|
|
2970
3294
|
async function createTaskFromError2(error) {
|
|
2971
3295
|
const spinner = ora12("Creating task from error...").start();
|
|
2972
3296
|
try {
|
|
2973
|
-
const authManager = new
|
|
3297
|
+
const authManager = new AuthManager15();
|
|
2974
3298
|
const taskOps = await TaskOperations11.fromAuthManager(authManager);
|
|
2975
3299
|
const title = generateErrorTaskTitle2(error);
|
|
2976
3300
|
const notes = formatErrorForNotes(error);
|
|
@@ -3033,11 +3357,11 @@ function getTagColor2(category) {
|
|
|
3033
3357
|
// src/commands/next.ts
|
|
3034
3358
|
import { Command as Command16 } from "commander";
|
|
3035
3359
|
import chalk17 from "chalk";
|
|
3036
|
-
import { AuthManager as
|
|
3360
|
+
import { AuthManager as AuthManager16, TaskOperations as TaskOperations12 } from "@vibetasks/core";
|
|
3037
3361
|
var PRIORITY_ORDER = { high: 0, medium: 1, low: 2, none: 3 };
|
|
3038
3362
|
var nextCommand = new Command16("next").description("Show and optionally start the highest priority task").option("-s, --start", "Start vibing on the task immediately").option("--project <name>", "Filter by project tag").action(async (options) => {
|
|
3039
3363
|
try {
|
|
3040
|
-
const authManager = new
|
|
3364
|
+
const authManager = new AuthManager16();
|
|
3041
3365
|
const taskOps = await TaskOperations12.fromAuthManager(authManager);
|
|
3042
3366
|
let tasks = await taskOps.getTasks("all");
|
|
3043
3367
|
tasks = tasks.filter((t) => t.status !== "done" && t.status !== "archived" && !t.completed);
|
|
@@ -3198,7 +3522,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3198
3522
|
}
|
|
3199
3523
|
})
|
|
3200
3524
|
).addCommand(
|
|
3201
|
-
new Command17("end").description("End the current session").option("-s, --summary <text>", "Add a summary of what was done").action(async (options) => {
|
|
3525
|
+
new Command17("end").description("End the current session").option("-s, --summary <text>", "Add a summary of what was done").option("--no-stats", "Skip showing session statistics").action(async (options) => {
|
|
3202
3526
|
const sessionManager2 = getSessionManager();
|
|
3203
3527
|
const session = await sessionManager2.endSession(options.summary);
|
|
3204
3528
|
if (!session) {
|
|
@@ -3206,10 +3530,51 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3206
3530
|
return;
|
|
3207
3531
|
}
|
|
3208
3532
|
console.log(chalk18.green("\u2713 Session ended:"), chalk18.bold(session.id));
|
|
3209
|
-
console.log(
|
|
3210
|
-
|
|
3533
|
+
console.log();
|
|
3534
|
+
if (options.stats !== false) {
|
|
3535
|
+
const duration = formatDuration(session.startedAt, session.endedAt);
|
|
3536
|
+
const tasksCreated = session.actions.filter((a) => a.type === "task_created").length;
|
|
3537
|
+
const tasksCompleted = session.actions.filter((a) => a.type === "task_completed").length;
|
|
3538
|
+
const tasksUpdated = session.actions.filter((a) => a.type === "task_updated").length;
|
|
3539
|
+
const filesModified = session.actions.filter((a) => a.type === "file_modified").length;
|
|
3540
|
+
const notes = session.actions.filter((a) => a.type === "note").length;
|
|
3541
|
+
console.log(chalk18.cyan("\u{1F4CA} Session Statistics"));
|
|
3542
|
+
console.log(chalk18.gray("\u2500".repeat(40)));
|
|
3543
|
+
console.log(chalk18.white(` Duration: ${chalk18.bold(duration)}`));
|
|
3544
|
+
console.log(chalk18.white(` Actions logged: ${chalk18.bold(session.actions.length.toString())}`));
|
|
3545
|
+
console.log();
|
|
3546
|
+
if (tasksCreated > 0 || tasksCompleted > 0 || tasksUpdated > 0) {
|
|
3547
|
+
console.log(chalk18.cyan(" Tasks:"));
|
|
3548
|
+
if (tasksCreated > 0) {
|
|
3549
|
+
console.log(chalk18.green(` \u2795 Created: ${tasksCreated}`));
|
|
3550
|
+
}
|
|
3551
|
+
if (tasksCompleted > 0) {
|
|
3552
|
+
console.log(chalk18.green(` \u2713 Completed: ${tasksCompleted}`));
|
|
3553
|
+
}
|
|
3554
|
+
if (tasksUpdated > 0) {
|
|
3555
|
+
console.log(chalk18.blue(` \u{1F4DD} Updated: ${tasksUpdated}`));
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
if (filesModified > 0) {
|
|
3559
|
+
console.log(chalk18.yellow(` \u{1F4C4} Files modified: ${filesModified}`));
|
|
3560
|
+
}
|
|
3561
|
+
if (notes > 0) {
|
|
3562
|
+
console.log(chalk18.gray(` \u{1F4AC} Notes logged: ${notes}`));
|
|
3563
|
+
}
|
|
3564
|
+
console.log(chalk18.gray("\u2500".repeat(40)));
|
|
3565
|
+
console.log();
|
|
3566
|
+
}
|
|
3211
3567
|
if (options.summary) {
|
|
3212
|
-
console.log(chalk18.
|
|
3568
|
+
console.log(chalk18.cyan("Summary:"), options.summary);
|
|
3569
|
+
console.log();
|
|
3570
|
+
}
|
|
3571
|
+
const totalTasks = session.actions.filter(
|
|
3572
|
+
(a) => a.type === "task_created" || a.type === "task_completed"
|
|
3573
|
+
).length;
|
|
3574
|
+
if (totalTasks === 0) {
|
|
3575
|
+
console.log(chalk18.yellow("\u{1F4A1} Tip: Use `vibetasks add` and `vibetasks done` to track work"));
|
|
3576
|
+
} else if (totalTasks >= 5) {
|
|
3577
|
+
console.log(chalk18.green("\u{1F525} Productive session!"));
|
|
3213
3578
|
}
|
|
3214
3579
|
})
|
|
3215
3580
|
).addCommand(
|
|
@@ -3235,7 +3600,7 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3235
3600
|
}
|
|
3236
3601
|
})
|
|
3237
3602
|
).addCommand(
|
|
3238
|
-
new Command17("show").
|
|
3603
|
+
new Command17("show").description("Show session details").argument("<session-id>", "Session ID (partial match supported)").action(async (sessionId) => {
|
|
3239
3604
|
const sessionManager2 = getSessionManager();
|
|
3240
3605
|
const handoff = await sessionManager2.getSessionHandoff(sessionId);
|
|
3241
3606
|
console.log(handoff);
|
|
@@ -3249,6 +3614,48 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3249
3614
|
});
|
|
3250
3615
|
console.log(chalk18.green("\u2713 Logged to session"));
|
|
3251
3616
|
})
|
|
3617
|
+
).addCommand(
|
|
3618
|
+
new Command17("sync").description("Sync current session and all actions to the server").action(async () => {
|
|
3619
|
+
const sessionManager2 = getSessionManager();
|
|
3620
|
+
const session = await sessionManager2.getCurrentSession();
|
|
3621
|
+
if (!session) {
|
|
3622
|
+
console.log(chalk18.yellow("No active session to sync"));
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
console.log(chalk18.cyan("Syncing session:"), chalk18.bold(session.id));
|
|
3626
|
+
console.log(chalk18.gray(`Actions to sync: ${session.actions.length}`));
|
|
3627
|
+
const success = await sessionManager2.syncToServer(session);
|
|
3628
|
+
if (success) {
|
|
3629
|
+
console.log(chalk18.green("\u2713 Session synced to server"));
|
|
3630
|
+
} else {
|
|
3631
|
+
console.log(chalk18.yellow("\u26A0 Sync failed (not logged in or server unavailable)"));
|
|
3632
|
+
}
|
|
3633
|
+
})
|
|
3634
|
+
).addCommand(
|
|
3635
|
+
new Command17("compress").description("Compress session history to reduce context usage").option("--dry-run", "Show what would be compressed without making changes").action(async (options) => {
|
|
3636
|
+
const sessionManager2 = getSessionManager();
|
|
3637
|
+
const session = await sessionManager2.getCurrentSession();
|
|
3638
|
+
if (!session) {
|
|
3639
|
+
console.log(chalk18.yellow("No active session to compress"));
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3642
|
+
console.log(chalk18.cyan("Compressing session:"), chalk18.bold(session.id));
|
|
3643
|
+
console.log(chalk18.gray(`Current actions: ${session.actions.length}`));
|
|
3644
|
+
if (options.dryRun) {
|
|
3645
|
+
console.log(chalk18.yellow("DRY RUN - No changes will be made"));
|
|
3646
|
+
console.log();
|
|
3647
|
+
console.log(chalk18.cyan("Compression would:"));
|
|
3648
|
+
console.log(chalk18.gray(" \u2022 Summarize completed subtasks"));
|
|
3649
|
+
console.log(chalk18.gray(" \u2022 Remove redundant action logs"));
|
|
3650
|
+
console.log(chalk18.gray(" \u2022 Preserve critical decisions and blockers"));
|
|
3651
|
+
console.log(chalk18.gray(" \u2022 Maintain active task context"));
|
|
3652
|
+
console.log();
|
|
3653
|
+
console.log(chalk18.white("Estimated reduction: 60-80% of context size"));
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3656
|
+
console.log(chalk18.yellow("\u26A0 Compression not yet implemented"));
|
|
3657
|
+
console.log(chalk18.gray("This feature will be available in the next version"));
|
|
3658
|
+
})
|
|
3252
3659
|
);
|
|
3253
3660
|
function formatDuration(start, end) {
|
|
3254
3661
|
const ms = new Date(end).getTime() - new Date(start).getTime();
|
|
@@ -3264,7 +3671,7 @@ function formatDuration(start, end) {
|
|
|
3264
3671
|
import { Command as Command18 } from "commander";
|
|
3265
3672
|
import ora13 from "ora";
|
|
3266
3673
|
import chalk19 from "chalk";
|
|
3267
|
-
import { AuthManager as
|
|
3674
|
+
import { AuthManager as AuthManager17, TaskOperations as TaskOperations13 } from "@vibetasks/core";
|
|
3268
3675
|
function parseIndices(args) {
|
|
3269
3676
|
const indices = /* @__PURE__ */ new Set();
|
|
3270
3677
|
for (const arg of args) {
|
|
@@ -3306,23 +3713,11 @@ var subtaskCommand = new Command18("subtask").description("Manage subtasks withi
|
|
|
3306
3713
|
subtaskCommand.command("list").description("List subtasks for a task").argument("<task-id>", "Task ID, short # (1-99), or first 8 characters").action(async (taskIdArg) => {
|
|
3307
3714
|
const spinner = ora13("Loading subtasks...").start();
|
|
3308
3715
|
try {
|
|
3309
|
-
const authManager = new
|
|
3716
|
+
const authManager = new AuthManager17();
|
|
3310
3717
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3311
3718
|
const shortIdManager = new ShortIdManager();
|
|
3312
|
-
let
|
|
3313
|
-
|
|
3314
|
-
const allTasks = await taskOps.getTasks("all");
|
|
3315
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
|
|
3316
|
-
if (!matchingTask) {
|
|
3317
|
-
spinner.fail(chalk19.red("Task not found"));
|
|
3318
|
-
console.error(chalk19.gray(`
|
|
3319
|
-
No task found with ID: ${taskIdArg}
|
|
3320
|
-
`));
|
|
3321
|
-
process.exit(1);
|
|
3322
|
-
}
|
|
3323
|
-
taskId = matchingTask.id;
|
|
3324
|
-
}
|
|
3325
|
-
const task = await taskOps.getTask(taskId);
|
|
3719
|
+
let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
|
|
3720
|
+
const task = await taskOps.getTask(taskId2);
|
|
3326
3721
|
const subtasks = task.subtasks_json || [];
|
|
3327
3722
|
spinner.stop();
|
|
3328
3723
|
console.log();
|
|
@@ -3354,23 +3749,11 @@ Error: ${error.message}
|
|
|
3354
3749
|
subtaskCommand.command("done").description("Mark subtasks as complete").argument("<task-id>", "Task ID, short # (1-99), or first 8 characters").argument("<indices...>", 'Subtask indices (1-based): "1 2 3", "1-5", "1,3,5", or partial title match').action(async (taskIdArg, indicesArgs) => {
|
|
3355
3750
|
const spinner = ora13("Updating subtasks...").start();
|
|
3356
3751
|
try {
|
|
3357
|
-
const authManager = new
|
|
3752
|
+
const authManager = new AuthManager17();
|
|
3358
3753
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3359
3754
|
const shortIdManager = new ShortIdManager();
|
|
3360
|
-
let
|
|
3361
|
-
|
|
3362
|
-
const allTasks = await taskOps.getTasks("all");
|
|
3363
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
|
|
3364
|
-
if (!matchingTask) {
|
|
3365
|
-
spinner.fail(chalk19.red("Task not found"));
|
|
3366
|
-
console.error(chalk19.gray(`
|
|
3367
|
-
No task found with ID: ${taskIdArg}
|
|
3368
|
-
`));
|
|
3369
|
-
process.exit(1);
|
|
3370
|
-
}
|
|
3371
|
-
taskId = matchingTask.id;
|
|
3372
|
-
}
|
|
3373
|
-
const task = await taskOps.getTask(taskId);
|
|
3755
|
+
let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
|
|
3756
|
+
const task = await taskOps.getTask(taskId2);
|
|
3374
3757
|
const subtasks = [...task.subtasks_json || []];
|
|
3375
3758
|
if (subtasks.length === 0) {
|
|
3376
3759
|
spinner.fail(chalk19.yellow("No subtasks to update"));
|
|
@@ -3404,7 +3787,7 @@ Valid range: 1-${subtasks.length}
|
|
|
3404
3787
|
spinner.succeed(chalk19.yellow("All specified subtasks were already done"));
|
|
3405
3788
|
process.exit(0);
|
|
3406
3789
|
}
|
|
3407
|
-
await taskOps.updateTask(
|
|
3790
|
+
await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
|
|
3408
3791
|
const doneCount = subtasks.filter((s) => s.done).length;
|
|
3409
3792
|
spinner.succeed(chalk19.green(`Marked ${updated.length} subtask(s) as done`));
|
|
3410
3793
|
console.log();
|
|
@@ -3415,7 +3798,7 @@ Valid range: 1-${subtasks.length}
|
|
|
3415
3798
|
console.log(chalk19.gray(`Progress: ${doneCount}/${subtasks.length} completed`));
|
|
3416
3799
|
if (doneCount === subtasks.length) {
|
|
3417
3800
|
console.log();
|
|
3418
|
-
console.log(chalk19.cyan(`All subtasks complete! Run: vibetasks done ${
|
|
3801
|
+
console.log(chalk19.cyan(`All subtasks complete! Run: vibetasks done ${taskId2.substring(0, 8)}`));
|
|
3419
3802
|
}
|
|
3420
3803
|
console.log();
|
|
3421
3804
|
process.exit(0);
|
|
@@ -3430,23 +3813,11 @@ Error: ${error.message}
|
|
|
3430
3813
|
subtaskCommand.command("undo").description("Mark subtasks as incomplete").argument("<task-id>", "Task ID, short # (1-99), or first 8 characters").argument("<indices...>", 'Subtask indices (1-based): "1 2 3", "1-5", "1,3,5", or partial title match').action(async (taskIdArg, indicesArgs) => {
|
|
3431
3814
|
const spinner = ora13("Updating subtasks...").start();
|
|
3432
3815
|
try {
|
|
3433
|
-
const authManager = new
|
|
3816
|
+
const authManager = new AuthManager17();
|
|
3434
3817
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3435
3818
|
const shortIdManager = new ShortIdManager();
|
|
3436
|
-
let
|
|
3437
|
-
|
|
3438
|
-
const allTasks = await taskOps.getTasks("all");
|
|
3439
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
|
|
3440
|
-
if (!matchingTask) {
|
|
3441
|
-
spinner.fail(chalk19.red("Task not found"));
|
|
3442
|
-
console.error(chalk19.gray(`
|
|
3443
|
-
No task found with ID: ${taskIdArg}
|
|
3444
|
-
`));
|
|
3445
|
-
process.exit(1);
|
|
3446
|
-
}
|
|
3447
|
-
taskId = matchingTask.id;
|
|
3448
|
-
}
|
|
3449
|
-
const task = await taskOps.getTask(taskId);
|
|
3819
|
+
let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
|
|
3820
|
+
const task = await taskOps.getTask(taskId2);
|
|
3450
3821
|
const subtasks = [...task.subtasks_json || []];
|
|
3451
3822
|
if (subtasks.length === 0) {
|
|
3452
3823
|
spinner.fail(chalk19.yellow("No subtasks to update"));
|
|
@@ -3480,7 +3851,7 @@ Valid range: 1-${subtasks.length}
|
|
|
3480
3851
|
spinner.succeed(chalk19.yellow("All specified subtasks were already incomplete"));
|
|
3481
3852
|
process.exit(0);
|
|
3482
3853
|
}
|
|
3483
|
-
await taskOps.updateTask(
|
|
3854
|
+
await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
|
|
3484
3855
|
const doneCount = subtasks.filter((s) => s.done).length;
|
|
3485
3856
|
spinner.succeed(chalk19.yellow(`Marked ${updated.length} subtask(s) as incomplete`));
|
|
3486
3857
|
console.log();
|
|
@@ -3504,7 +3875,7 @@ Error: ${error.message}
|
|
|
3504
3875
|
import { Command as Command19 } from "commander";
|
|
3505
3876
|
import chalk20 from "chalk";
|
|
3506
3877
|
import ora14 from "ora";
|
|
3507
|
-
import { AuthManager as
|
|
3878
|
+
import { AuthManager as AuthManager18, TaskOperations as TaskOperations14 } from "@vibetasks/core";
|
|
3508
3879
|
import {
|
|
3509
3880
|
parseError,
|
|
3510
3881
|
generateErrorTaskTitle as generateErrorTaskTitle3,
|
|
@@ -3602,7 +3973,7 @@ var errorCommand = new Command19("error").description("Capture an error and crea
|
|
|
3602
3973
|
process.exit(1);
|
|
3603
3974
|
}
|
|
3604
3975
|
spinner.start("Creating error task...");
|
|
3605
|
-
const authManager = new
|
|
3976
|
+
const authManager = new AuthManager18();
|
|
3606
3977
|
const taskOps = await TaskOperations14.fromAuthManager(authManager);
|
|
3607
3978
|
const errorContext = parseError(errorText, "terminal");
|
|
3608
3979
|
const title = errorContext ? generateErrorTaskTitle3(errorContext) : `Error: ${errorText.split("\n")[0].slice(0, 80)}`;
|
|
@@ -3642,7 +4013,7 @@ ${errorText}
|
|
|
3642
4013
|
import { Command as Command20 } from "commander";
|
|
3643
4014
|
import chalk21 from "chalk";
|
|
3644
4015
|
import Table3 from "cli-table3";
|
|
3645
|
-
import { AuthManager as
|
|
4016
|
+
import { AuthManager as AuthManager19, TaskOperations as TaskOperations15 } from "@vibetasks/core";
|
|
3646
4017
|
var sourceDisplay = {
|
|
3647
4018
|
sentry: { label: "Sentry", color: chalk21.red },
|
|
3648
4019
|
ci: { label: "CI", color: chalk21.yellow },
|
|
@@ -3669,7 +4040,7 @@ function formatSource(sourceType) {
|
|
|
3669
4040
|
}
|
|
3670
4041
|
var inboxCommand = new Command20("inbox").description("View inbox items from all sources (Sentry, GitHub, Email, Slack, CI)").option("-s, --source <source>", "Filter by source: sentry, ci, github, email, slack, errors, feedback").option("-p, --priority <priority>", "Filter by priority: high, medium, low").option("-a, --all", "Include acknowledged items").option("-l, --limit <number>", "Maximum items to show", "20").action(async (options) => {
|
|
3671
4042
|
try {
|
|
3672
|
-
const authManager = new
|
|
4043
|
+
const authManager = new AuthManager19();
|
|
3673
4044
|
const taskOps = await TaskOperations15.fromAuthManager(authManager);
|
|
3674
4045
|
let sourceTypes;
|
|
3675
4046
|
if (options.source) {
|
|
@@ -3778,7 +4149,7 @@ Error: ${error.message}
|
|
|
3778
4149
|
});
|
|
3779
4150
|
var inboxStatsCommand = new Command20("stats").description("Show inbox statistics").action(async () => {
|
|
3780
4151
|
try {
|
|
3781
|
-
const authManager = new
|
|
4152
|
+
const authManager = new AuthManager19();
|
|
3782
4153
|
const taskOps = await TaskOperations15.fromAuthManager(authManager);
|
|
3783
4154
|
const stats = await taskOps.getInboxStats();
|
|
3784
4155
|
console.log("\n" + chalk21.bold("Inbox Statistics") + "\n");
|
|
@@ -3834,10 +4205,2650 @@ Error: ${error.message}
|
|
|
3834
4205
|
});
|
|
3835
4206
|
inboxCommand.addCommand(inboxStatsCommand);
|
|
3836
4207
|
|
|
4208
|
+
// src/commands/handoff.ts
|
|
4209
|
+
import { Command as Command21 } from "commander";
|
|
4210
|
+
import chalk22 from "chalk";
|
|
4211
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4212
|
+
import { AuthManager as AuthManager20, TaskOperations as TaskOperations16 } from "@vibetasks/core";
|
|
4213
|
+
function extractDecisions(session) {
|
|
4214
|
+
if (!session?.actions) return [];
|
|
4215
|
+
const decisions = [];
|
|
4216
|
+
const decisionKeywords = ["decided", "choosing", "using", "approach", "instead of", "because", "opted for"];
|
|
4217
|
+
for (const action of session.actions) {
|
|
4218
|
+
const desc = action.description.toLowerCase();
|
|
4219
|
+
const hasDecisionKeyword = decisionKeywords.some((kw) => desc.includes(kw));
|
|
4220
|
+
if (hasDecisionKeyword || action.type === "note") {
|
|
4221
|
+
const sentences = action.description.split(/[.!?]\s+/);
|
|
4222
|
+
const decision = sentences[0];
|
|
4223
|
+
const rationale = sentences.slice(1).join(". ");
|
|
4224
|
+
decisions.push({
|
|
4225
|
+
decision,
|
|
4226
|
+
rationale: rationale || "See session history for context",
|
|
4227
|
+
timestamp: action.timestamp
|
|
4228
|
+
});
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
return decisions.slice(-10);
|
|
4232
|
+
}
|
|
4233
|
+
function extractModifiedFiles(session) {
|
|
4234
|
+
if (!session?.actions) return [];
|
|
4235
|
+
const fileMap = /* @__PURE__ */ new Map();
|
|
4236
|
+
for (const action of session.actions) {
|
|
4237
|
+
if (action.type === "file_modified" && action.filePath) {
|
|
4238
|
+
fileMap.set(action.filePath, (fileMap.get(action.filePath) || 0) + 1);
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
const files = Array.from(fileMap.entries()).sort((a, b) => b[1] - a[1]).map(([path6, count]) => ({
|
|
4242
|
+
path: path6,
|
|
4243
|
+
relevance: count >= 5 ? "critical" : count >= 2 ? "important" : "reference"
|
|
4244
|
+
}));
|
|
4245
|
+
return files.slice(0, 20);
|
|
4246
|
+
}
|
|
4247
|
+
async function generateStructuredHandoff(sessionManager2, includeFull) {
|
|
4248
|
+
const session = await sessionManager2.getCurrentSession();
|
|
4249
|
+
const authManager = new AuthManager20();
|
|
4250
|
+
const taskOps = await TaskOperations16.fromAuthManager(authManager);
|
|
4251
|
+
const allTasks = await taskOps.getTasks("all");
|
|
4252
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
4253
|
+
const completed = [];
|
|
4254
|
+
const nextSteps = [];
|
|
4255
|
+
const blockers = [];
|
|
4256
|
+
const taskIds = [];
|
|
4257
|
+
let intent = "Continue working on vibing tasks";
|
|
4258
|
+
let inProgress = "";
|
|
4259
|
+
for (const task of vibingTasks) {
|
|
4260
|
+
taskIds.push(task.id);
|
|
4261
|
+
const contextNotes = task.context_notes || "";
|
|
4262
|
+
const intentMatch = contextNotes.match(/INTENT:\s*(.+?)(?:\n|$)/i);
|
|
4263
|
+
if (intentMatch) {
|
|
4264
|
+
intent = intentMatch[1].trim();
|
|
4265
|
+
} else {
|
|
4266
|
+
intent = task.title;
|
|
4267
|
+
}
|
|
4268
|
+
const doneMatch = contextNotes.match(/DONE:\s*(.+?)(?:\n(?:CURRENT|NEXT|BLOCKED)|$)/is);
|
|
4269
|
+
if (doneMatch) {
|
|
4270
|
+
completed.push(doneMatch[1].trim());
|
|
4271
|
+
}
|
|
4272
|
+
const currentMatch = contextNotes.match(/CURRENT:\s*(.+?)(?:\n(?:NEXT|BLOCKED)|$)/is);
|
|
4273
|
+
if (currentMatch) {
|
|
4274
|
+
inProgress = currentMatch[1].trim();
|
|
4275
|
+
}
|
|
4276
|
+
const nextMatch = contextNotes.match(/NEXT:\s*(.+?)(?:\n(?:BLOCKED)|$)/is);
|
|
4277
|
+
if (nextMatch) {
|
|
4278
|
+
nextSteps.push(nextMatch[1].trim());
|
|
4279
|
+
}
|
|
4280
|
+
const blockedMatch = contextNotes.match(/BLOCKED:\s*(.+?)$/is);
|
|
4281
|
+
if (blockedMatch && blockedMatch[1].trim() !== "None") {
|
|
4282
|
+
blockers.push({
|
|
4283
|
+
type: "external",
|
|
4284
|
+
description: blockedMatch[1].trim(),
|
|
4285
|
+
context: `Task: ${task.title}`
|
|
4286
|
+
});
|
|
4287
|
+
}
|
|
4288
|
+
const subtasks = task.subtasks_json || task.subtasks || [];
|
|
4289
|
+
subtasks.forEach((s) => {
|
|
4290
|
+
if (s.done) {
|
|
4291
|
+
completed.push(s.title);
|
|
4292
|
+
} else if (!inProgress) {
|
|
4293
|
+
inProgress = s.title;
|
|
4294
|
+
} else {
|
|
4295
|
+
nextSteps.push(s.title);
|
|
4296
|
+
}
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4299
|
+
let gitBranch;
|
|
4300
|
+
let uncommittedFiles = [];
|
|
4301
|
+
try {
|
|
4302
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
4303
|
+
gitBranch = execSync3("git branch --show-current", { encoding: "utf-8", timeout: 2e3 }).trim();
|
|
4304
|
+
const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 2e3 }).trim();
|
|
4305
|
+
uncommittedFiles = gitStatus.split("\n").filter(Boolean);
|
|
4306
|
+
} catch {
|
|
4307
|
+
}
|
|
4308
|
+
const handoff = {
|
|
4309
|
+
intent,
|
|
4310
|
+
progress: {
|
|
4311
|
+
completed: completed.slice(0, 10),
|
|
4312
|
+
// Limit to 10 for token efficiency
|
|
4313
|
+
in_progress: inProgress || "No current work in progress",
|
|
4314
|
+
next_steps: nextSteps.slice(0, 5)
|
|
4315
|
+
// Limit to 5 next steps
|
|
4316
|
+
},
|
|
4317
|
+
state: {
|
|
4318
|
+
git_branch: gitBranch,
|
|
4319
|
+
uncommitted_files: uncommittedFiles.slice(0, 20)
|
|
4320
|
+
// Limit to 20 files
|
|
4321
|
+
},
|
|
4322
|
+
decisions: extractDecisions(session),
|
|
4323
|
+
blockers,
|
|
4324
|
+
files: extractModifiedFiles(session),
|
|
4325
|
+
sessionId: session?.id || "unknown",
|
|
4326
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4327
|
+
taskIds
|
|
4328
|
+
};
|
|
4329
|
+
return handoff;
|
|
4330
|
+
}
|
|
4331
|
+
function estimateTokens(text) {
|
|
4332
|
+
return Math.ceil(text.length / 4);
|
|
4333
|
+
}
|
|
4334
|
+
async function generateA2AHandoff(sessionManager2, options = {}) {
|
|
4335
|
+
const maxTokens = options.maxTokens || 1e4;
|
|
4336
|
+
let estimatedTokens = 0;
|
|
4337
|
+
const authManager = new AuthManager20();
|
|
4338
|
+
const taskOps = await TaskOperations16.fromAuthManager(authManager);
|
|
4339
|
+
const allTasks = await taskOps.getTasks("all");
|
|
4340
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
4341
|
+
const tier1Tasks = vibingTasks.slice(0, 3).map((task) => ({
|
|
4342
|
+
id: task.id,
|
|
4343
|
+
a2a_task_id: task.a2a_task_id,
|
|
4344
|
+
title: task.title,
|
|
4345
|
+
status: task.status || "vibing",
|
|
4346
|
+
semantic_vector_id: task.semantic_vector_id,
|
|
4347
|
+
last_update: task.updated_at || task.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
4348
|
+
priority: task.priority,
|
|
4349
|
+
subtasks: task.subtasks_json || task.subtasks || []
|
|
4350
|
+
}));
|
|
4351
|
+
const tier1Json = JSON.stringify(tier1Tasks);
|
|
4352
|
+
estimatedTokens += estimateTokens(tier1Json);
|
|
4353
|
+
let handoff_notes = "";
|
|
4354
|
+
const tools_used = [];
|
|
4355
|
+
const decision_tree = [];
|
|
4356
|
+
const blockers = [];
|
|
4357
|
+
const next_actions = [];
|
|
4358
|
+
for (const task of vibingTasks.slice(0, 3)) {
|
|
4359
|
+
const contextNotes = task.context_notes || "";
|
|
4360
|
+
const intentMatch = contextNotes.match(/INTENT:\s*(.+?)(?:\n|$)/i);
|
|
4361
|
+
if (intentMatch) {
|
|
4362
|
+
handoff_notes = intentMatch[1].trim();
|
|
4363
|
+
}
|
|
4364
|
+
const doneMatch = contextNotes.match(/DONE:\s*(.+?)(?:\n(?:CURRENT|NEXT|BLOCKED)|$)/is);
|
|
4365
|
+
if (doneMatch) {
|
|
4366
|
+
decision_tree.push({
|
|
4367
|
+
decision: "Completed",
|
|
4368
|
+
reasoning: doneMatch[1].trim()
|
|
4369
|
+
});
|
|
4370
|
+
}
|
|
4371
|
+
const nextMatch = contextNotes.match(/NEXT:\s*(.+?)(?:\n(?:BLOCKED)|$)/is);
|
|
4372
|
+
if (nextMatch) {
|
|
4373
|
+
next_actions.push(nextMatch[1].trim());
|
|
4374
|
+
}
|
|
4375
|
+
const blockedMatch = contextNotes.match(/BLOCKED:\s*(.+?)$/is);
|
|
4376
|
+
if (blockedMatch && blockedMatch[1].trim() !== "None") {
|
|
4377
|
+
blockers.push({
|
|
4378
|
+
type: "external",
|
|
4379
|
+
description: blockedMatch[1].trim(),
|
|
4380
|
+
context: `Task: ${task.title}`
|
|
4381
|
+
});
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
handoff_notes = handoff_notes || "Continue working on vibing tasks";
|
|
4385
|
+
const context_summary = handoff_notes;
|
|
4386
|
+
estimatedTokens += estimateTokens(JSON.stringify({ handoff_notes, context_summary, decision_tree, blockers, next_actions }));
|
|
4387
|
+
let uncommittedFiles = [];
|
|
4388
|
+
let gitBranch;
|
|
4389
|
+
if (estimatedTokens < maxTokens * 0.7) {
|
|
4390
|
+
try {
|
|
4391
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
4392
|
+
gitBranch = execSync3("git branch --show-current", { encoding: "utf-8", timeout: 2e3 }).trim();
|
|
4393
|
+
const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 2e3 }).trim();
|
|
4394
|
+
uncommittedFiles = gitStatus.split("\n").filter(Boolean).slice(0, 20);
|
|
4395
|
+
estimatedTokens += estimateTokens(JSON.stringify({ git_branch: gitBranch, uncommitted_files: uncommittedFiles }));
|
|
4396
|
+
} catch {
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
const session = await sessionManager2.getCurrentSession();
|
|
4400
|
+
const sessionId = session?.id || "unknown";
|
|
4401
|
+
const executionContext = {
|
|
4402
|
+
tools_used,
|
|
4403
|
+
decision_tree,
|
|
4404
|
+
blockers,
|
|
4405
|
+
next_actions
|
|
4406
|
+
};
|
|
4407
|
+
const resourceRequirements = {
|
|
4408
|
+
capabilities: ["code_generation", "task_management"],
|
|
4409
|
+
estimated_time_minutes: vibingTasks.length * 15,
|
|
4410
|
+
// Rough estimate: 15 min per task
|
|
4411
|
+
priority: vibingTasks.some((t) => t.priority === "high") ? "high" : "medium"
|
|
4412
|
+
};
|
|
4413
|
+
const handoffId = randomUUID3();
|
|
4414
|
+
const a2aRequest = {
|
|
4415
|
+
jsonrpc: "2.0",
|
|
4416
|
+
method: "vibetasks.handoff",
|
|
4417
|
+
params: {
|
|
4418
|
+
handoff_type: "continue",
|
|
4419
|
+
tasks: tier1Tasks,
|
|
4420
|
+
handoff_notes,
|
|
4421
|
+
context_summary,
|
|
4422
|
+
execution_context: executionContext,
|
|
4423
|
+
resource_requirements: resourceRequirements,
|
|
4424
|
+
semantic_context_ids: tier1Tasks.map((t) => t.semantic_vector_id).filter(Boolean),
|
|
4425
|
+
workspace_id: options.workspaceId,
|
|
4426
|
+
source_session_id: sessionId
|
|
4427
|
+
},
|
|
4428
|
+
id: handoffId
|
|
4429
|
+
};
|
|
4430
|
+
estimatedTokens = estimateTokens(JSON.stringify(a2aRequest));
|
|
4431
|
+
return {
|
|
4432
|
+
request: a2aRequest,
|
|
4433
|
+
estimatedTokens
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
var handoffCommand = new Command21("handoff").description("Generate comprehensive handoff for the next AI (vibing tasks, context notes, git status)").argument("[session-id]", "Session ID (shows that session only)").option("--full", "Include full action history from session").option("--json", "Output as structured JSON (HandoffContext format)").option("--a2a", "Output as A2A JSON-RPC 2.0 format (for agent-to-agent handoffs)").option("--max-tokens <number>", "Maximum tokens for A2A handoff (default: 10000)", parseInt).option("--workspace-id <id>", "Workspace ID for A2A task IDs").option("--save", "Save handoff to .vibebot/handoffs/").option("--auto-update-context", "Automatically update current vibing task context notes").action(async (sessionId, options) => {
|
|
4437
|
+
const sessionManager2 = getSessionManager();
|
|
4438
|
+
if (sessionId && !options?.json) {
|
|
4439
|
+
const handoff = await sessionManager2.getSessionHandoff(sessionId);
|
|
4440
|
+
console.log(handoff);
|
|
4441
|
+
return;
|
|
4442
|
+
}
|
|
4443
|
+
if (options?.a2a) {
|
|
4444
|
+
try {
|
|
4445
|
+
const { request, estimatedTokens } = await generateA2AHandoff(sessionManager2, {
|
|
4446
|
+
maxTokens: options?.maxTokens,
|
|
4447
|
+
workspaceId: options?.workspaceId
|
|
4448
|
+
});
|
|
4449
|
+
if (options?.save) {
|
|
4450
|
+
const fs4 = await import("fs/promises");
|
|
4451
|
+
const path6 = await import("path");
|
|
4452
|
+
const handoffDir = path6.join(process.cwd(), ".vibebot", "handoffs");
|
|
4453
|
+
await fs4.mkdir(handoffDir, { recursive: true });
|
|
4454
|
+
const filename = `a2a_${request.id}.json`;
|
|
4455
|
+
const filepath = path6.join(handoffDir, filename);
|
|
4456
|
+
await fs4.writeFile(filepath, JSON.stringify(request, null, 2));
|
|
4457
|
+
console.error(chalk22.green("\u2713 A2A handoff saved:"), chalk22.gray(filepath));
|
|
4458
|
+
console.error(chalk22.gray(` Estimated tokens: ${estimatedTokens}`));
|
|
4459
|
+
}
|
|
4460
|
+
console.log(JSON.stringify(request, null, 2));
|
|
4461
|
+
if (!options?.save) {
|
|
4462
|
+
console.error(chalk22.gray(`
|
|
4463
|
+
# Estimated tokens: ${estimatedTokens}`));
|
|
4464
|
+
}
|
|
4465
|
+
return;
|
|
4466
|
+
} catch (error) {
|
|
4467
|
+
console.error(chalk22.red("Error generating A2A handoff:"), error);
|
|
4468
|
+
process.exit(1);
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
if (options?.json || options?.save) {
|
|
4472
|
+
try {
|
|
4473
|
+
const handoffData = await generateStructuredHandoff(sessionManager2, options?.full);
|
|
4474
|
+
if (options?.save) {
|
|
4475
|
+
const fs4 = await import("fs/promises");
|
|
4476
|
+
const path6 = await import("path");
|
|
4477
|
+
const handoffDir = path6.join(process.cwd(), ".vibebot", "handoffs");
|
|
4478
|
+
await fs4.mkdir(handoffDir, { recursive: true });
|
|
4479
|
+
const filename = `${handoffData.sessionId}_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}.json`;
|
|
4480
|
+
const filepath = path6.join(handoffDir, filename);
|
|
4481
|
+
await fs4.writeFile(filepath, JSON.stringify(handoffData, null, 2));
|
|
4482
|
+
console.log(chalk22.green("\u2713 Handoff saved:"), chalk22.gray(filepath));
|
|
4483
|
+
}
|
|
4484
|
+
if (options?.json) {
|
|
4485
|
+
console.log(JSON.stringify(handoffData, null, 2));
|
|
4486
|
+
}
|
|
4487
|
+
return;
|
|
4488
|
+
} catch (error) {
|
|
4489
|
+
console.error(chalk22.red("Error generating handoff:"), error);
|
|
4490
|
+
process.exit(1);
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
console.log(chalk22.cyan("# AI Handoff"));
|
|
4494
|
+
console.log(chalk22.gray(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`));
|
|
4495
|
+
console.log();
|
|
4496
|
+
const currentSession = await sessionManager2.getCurrentSession();
|
|
4497
|
+
if (currentSession) {
|
|
4498
|
+
console.log(chalk22.yellow("## Session"));
|
|
4499
|
+
console.log(`ID: ${chalk22.bold(currentSession.id)}`);
|
|
4500
|
+
console.log(`Project: ${currentSession.project || "unknown"}`);
|
|
4501
|
+
console.log(`Started: ${new Date(currentSession.startedAt).toLocaleString()}`);
|
|
4502
|
+
console.log(`Actions: ${currentSession.actions.length}`);
|
|
4503
|
+
if (currentSession.summary) {
|
|
4504
|
+
console.log(`Summary: ${currentSession.summary}`);
|
|
4505
|
+
}
|
|
4506
|
+
console.log();
|
|
4507
|
+
}
|
|
4508
|
+
try {
|
|
4509
|
+
const authManager = new AuthManager20();
|
|
4510
|
+
const taskOps = await TaskOperations16.fromAuthManager(authManager);
|
|
4511
|
+
const allTasks = await taskOps.getTasks("all");
|
|
4512
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
4513
|
+
if (vibingTasks.length > 0) {
|
|
4514
|
+
console.log(chalk22.yellow("## Vibing Tasks (Active Work)"));
|
|
4515
|
+
console.log();
|
|
4516
|
+
for (const task of vibingTasks) {
|
|
4517
|
+
const shortId = task.short_id || task.id.slice(0, 8);
|
|
4518
|
+
console.log(chalk22.green(`### #${shortId}: ${task.title}`));
|
|
4519
|
+
if (task.project_tag) {
|
|
4520
|
+
console.log(chalk22.gray(`Project: ${task.project_tag}`));
|
|
4521
|
+
}
|
|
4522
|
+
const subtasks = task.subtasks_json || task.subtasks || [];
|
|
4523
|
+
if (subtasks.length > 0) {
|
|
4524
|
+
const done = subtasks.filter((s) => s.done).length;
|
|
4525
|
+
console.log(`Progress: ${done}/${subtasks.length} subtasks`);
|
|
4526
|
+
subtasks.forEach((s, i) => {
|
|
4527
|
+
const status = s.done ? chalk22.green("\u2713") : chalk22.gray("\u25CB");
|
|
4528
|
+
const title = s.done ? chalk22.gray(s.title) : s.title;
|
|
4529
|
+
console.log(` ${status} ${i + 1}. ${title}`);
|
|
4530
|
+
});
|
|
4531
|
+
}
|
|
4532
|
+
if (task.context_notes) {
|
|
4533
|
+
console.log();
|
|
4534
|
+
console.log(chalk22.cyan("Where I left off:"));
|
|
4535
|
+
console.log(task.context_notes);
|
|
4536
|
+
} else {
|
|
4537
|
+
console.log();
|
|
4538
|
+
console.log(chalk22.yellow("\u26A0 No context notes - update with:"));
|
|
4539
|
+
console.log(chalk22.gray(` vibetasks update ${shortId} --context "..."`));
|
|
4540
|
+
}
|
|
4541
|
+
console.log();
|
|
4542
|
+
}
|
|
4543
|
+
} else {
|
|
4544
|
+
console.log(chalk22.yellow("## No Vibing Tasks"));
|
|
4545
|
+
console.log(chalk22.gray("All clear - ready for new work."));
|
|
4546
|
+
console.log();
|
|
4547
|
+
}
|
|
4548
|
+
} catch (error) {
|
|
4549
|
+
console.log(chalk22.gray("(Could not fetch tasks - may need to login)"));
|
|
4550
|
+
console.log();
|
|
4551
|
+
}
|
|
4552
|
+
try {
|
|
4553
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
4554
|
+
const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
4555
|
+
if (gitStatus) {
|
|
4556
|
+
const files = gitStatus.split("\n");
|
|
4557
|
+
console.log(chalk22.yellow(`## Uncommitted Changes (${files.length} files)`));
|
|
4558
|
+
files.slice(0, 15).forEach((f) => console.log(chalk22.gray(` ${f}`)));
|
|
4559
|
+
if (files.length > 15) {
|
|
4560
|
+
console.log(chalk22.gray(` ... and ${files.length - 15} more`));
|
|
4561
|
+
}
|
|
4562
|
+
console.log();
|
|
4563
|
+
}
|
|
4564
|
+
const commits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
4565
|
+
if (commits) {
|
|
4566
|
+
console.log(chalk22.yellow("## Recent Commits"));
|
|
4567
|
+
commits.split("\n").forEach((c) => console.log(chalk22.gray(` ${c}`)));
|
|
4568
|
+
console.log();
|
|
4569
|
+
}
|
|
4570
|
+
} catch {
|
|
4571
|
+
}
|
|
4572
|
+
console.log(chalk22.cyan("\u2500".repeat(60)));
|
|
4573
|
+
console.log(chalk22.yellow("## For Next AI"));
|
|
4574
|
+
console.log();
|
|
4575
|
+
console.log("1. Run: `vibetasks session current || vibetasks session start`");
|
|
4576
|
+
console.log("2. Run: `vibetasks list --status vibing --short-ids`");
|
|
4577
|
+
console.log('3. Read context_notes above for "where I left off"');
|
|
4578
|
+
console.log("4. Continue the vibing task or ask user what to do");
|
|
4579
|
+
if (currentSession) {
|
|
4580
|
+
console.log();
|
|
4581
|
+
console.log(chalk22.gray(`To reference this session: "Continue from session ${currentSession.id}"`));
|
|
4582
|
+
}
|
|
4583
|
+
});
|
|
4584
|
+
|
|
4585
|
+
// src/commands/parse-prd.ts
|
|
4586
|
+
import { Command as Command22 } from "commander";
|
|
4587
|
+
import ora15 from "ora";
|
|
4588
|
+
import chalk23 from "chalk";
|
|
4589
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
4590
|
+
import { existsSync } from "fs";
|
|
4591
|
+
import { AuthManager as AuthManager21, TaskOperations as TaskOperations17 } from "@vibetasks/core";
|
|
4592
|
+
import { detectProject as detectProject6 } from "@vibetasks/shared/utils/project-detector";
|
|
4593
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4594
|
+
var parsePrdCommand = new Command22("parse-prd").description("Parse a PRD/requirements document and create tasks").argument("<file>", "Path to PRD/requirements file (markdown)").option("-t, --title <title>", "Override task title (auto-generated if not provided)").option("-p, --priority <level>", "Priority: low, medium, high", "high").option("--project <name>", "Override auto-detected project").option("--dry-run", "Show what would be created without creating").option("-v, --verbose", "Show detailed parsing output").action(async (filePath, options) => {
|
|
4595
|
+
const spinner = ora15("Parsing PRD...").start();
|
|
4596
|
+
try {
|
|
4597
|
+
if (!existsSync(filePath)) {
|
|
4598
|
+
throw new Error(`File not found: ${filePath}`);
|
|
4599
|
+
}
|
|
4600
|
+
const content = await readFile3(filePath, "utf-8");
|
|
4601
|
+
spinner.text = "Analyzing document structure...";
|
|
4602
|
+
const parsed = parsePrdContent(content, options.verbose);
|
|
4603
|
+
if (options.verbose) {
|
|
4604
|
+
spinner.info("Parsed structure:");
|
|
4605
|
+
console.log(chalk23.gray(` Title: ${parsed.title}`));
|
|
4606
|
+
console.log(chalk23.gray(` Summary: ${parsed.summary?.substring(0, 100)}...`));
|
|
4607
|
+
console.log(chalk23.gray(` Sections: ${parsed.sections.length}`));
|
|
4608
|
+
console.log(chalk23.gray(` Tasks: ${parsed.tasks.length}`));
|
|
4609
|
+
spinner.start("Continuing...");
|
|
4610
|
+
}
|
|
4611
|
+
const finalTitle = options.title || parsed.title || "Implement PRD";
|
|
4612
|
+
const subtasksJson = parsed.tasks.map((task2, i) => ({
|
|
4613
|
+
id: randomUUID4(),
|
|
4614
|
+
title: task2,
|
|
4615
|
+
done: false
|
|
4616
|
+
}));
|
|
4617
|
+
const acceptanceCriteria = parsed.acceptanceCriteria || [];
|
|
4618
|
+
let notes = "";
|
|
4619
|
+
if (parsed.summary) {
|
|
4620
|
+
notes += `## Summary
|
|
4621
|
+
${parsed.summary}
|
|
4622
|
+
|
|
4623
|
+
`;
|
|
4624
|
+
}
|
|
4625
|
+
if (acceptanceCriteria.length > 0) {
|
|
4626
|
+
notes += `DONE WHEN:
|
|
4627
|
+
${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}
|
|
4628
|
+
|
|
4629
|
+
`;
|
|
4630
|
+
}
|
|
4631
|
+
if (parsed.nonGoals && parsed.nonGoals.length > 0) {
|
|
4632
|
+
notes += `## Out of Scope
|
|
4633
|
+
${parsed.nonGoals.map((n) => `- ${n}`).join("\n")}
|
|
4634
|
+
`;
|
|
4635
|
+
}
|
|
4636
|
+
if (options.dryRun) {
|
|
4637
|
+
spinner.succeed(chalk23.yellow("Dry run - no task created"));
|
|
4638
|
+
console.log();
|
|
4639
|
+
console.log(chalk23.white.bold(`Task: ${finalTitle}`));
|
|
4640
|
+
console.log(chalk23.gray(`Priority: ${options.priority}`));
|
|
4641
|
+
console.log();
|
|
4642
|
+
if (subtasksJson.length > 0) {
|
|
4643
|
+
console.log(chalk23.cyan("Subtasks:"));
|
|
4644
|
+
subtasksJson.forEach((st, i) => {
|
|
4645
|
+
console.log(chalk23.gray(` ${i + 1}. ${st.title}`));
|
|
4646
|
+
});
|
|
4647
|
+
console.log();
|
|
4648
|
+
}
|
|
4649
|
+
if (acceptanceCriteria.length > 0) {
|
|
4650
|
+
console.log(chalk23.green("Acceptance Criteria:"));
|
|
4651
|
+
acceptanceCriteria.forEach((c, i) => {
|
|
4652
|
+
console.log(chalk23.gray(` \u2713 ${c}`));
|
|
4653
|
+
});
|
|
4654
|
+
console.log();
|
|
4655
|
+
}
|
|
4656
|
+
if (parsed.nonGoals && parsed.nonGoals.length > 0) {
|
|
4657
|
+
console.log(chalk23.red("Non-Goals (out of scope):"));
|
|
4658
|
+
parsed.nonGoals.forEach((n) => {
|
|
4659
|
+
console.log(chalk23.gray(` \u2717 ${n}`));
|
|
4660
|
+
});
|
|
4661
|
+
}
|
|
4662
|
+
process.exit(0);
|
|
4663
|
+
return;
|
|
4664
|
+
}
|
|
4665
|
+
spinner.text = "Creating task...";
|
|
4666
|
+
const authManager = new AuthManager21();
|
|
4667
|
+
const taskOps = await TaskOperations17.fromAuthManager(authManager);
|
|
4668
|
+
let projectTag;
|
|
4669
|
+
if (options.project) {
|
|
4670
|
+
projectTag = options.project;
|
|
4671
|
+
} else {
|
|
4672
|
+
try {
|
|
4673
|
+
const detected = await detectProject6(process.cwd());
|
|
4674
|
+
projectTag = detected.fullName || detected.name;
|
|
4675
|
+
} catch {
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
let sessionId;
|
|
4679
|
+
let sessionHistory = [];
|
|
4680
|
+
const sessionManager2 = getSessionManager();
|
|
4681
|
+
const currentSession = await sessionManager2.getCurrentSession();
|
|
4682
|
+
if (currentSession) {
|
|
4683
|
+
sessionId = currentSession.id;
|
|
4684
|
+
sessionHistory = [{
|
|
4685
|
+
session_id: currentSession.id,
|
|
4686
|
+
action: "created",
|
|
4687
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4688
|
+
}];
|
|
4689
|
+
}
|
|
4690
|
+
const task = await taskOps.createTask({
|
|
4691
|
+
title: finalTitle,
|
|
4692
|
+
notes,
|
|
4693
|
+
notes_format: "markdown",
|
|
4694
|
+
priority: options.priority,
|
|
4695
|
+
project_tag: projectTag,
|
|
4696
|
+
created_by: "ai",
|
|
4697
|
+
status: "todo",
|
|
4698
|
+
subtasks_json: subtasksJson,
|
|
4699
|
+
session_id: sessionId,
|
|
4700
|
+
session_history: sessionHistory,
|
|
4701
|
+
is_plan: true
|
|
4702
|
+
// Mark as a plan task
|
|
4703
|
+
});
|
|
4704
|
+
try {
|
|
4705
|
+
const shortIdManager = new ShortIdManager();
|
|
4706
|
+
const allTasks = await taskOps.getTasks("all");
|
|
4707
|
+
await shortIdManager.saveMappings(allTasks);
|
|
4708
|
+
} catch {
|
|
4709
|
+
}
|
|
4710
|
+
if (currentSession) {
|
|
4711
|
+
await sessionManager2.logAction({
|
|
4712
|
+
type: "task_created",
|
|
4713
|
+
description: `Created task from PRD: "${finalTitle}"`,
|
|
4714
|
+
taskId: task.id,
|
|
4715
|
+
taskTitle: finalTitle
|
|
4716
|
+
});
|
|
4717
|
+
}
|
|
4718
|
+
spinner.succeed(chalk23.green("Task created from PRD!"));
|
|
4719
|
+
console.log();
|
|
4720
|
+
console.log(chalk23.white.bold(finalTitle));
|
|
4721
|
+
if (projectTag) {
|
|
4722
|
+
console.log(chalk23.gray(`\u{1F4C1} Project: ${chalk23.white(projectTag)}`));
|
|
4723
|
+
}
|
|
4724
|
+
console.log(chalk23.gray(`ID: ${task.id.substring(0, 8)}...`));
|
|
4725
|
+
console.log();
|
|
4726
|
+
if (subtasksJson.length > 0) {
|
|
4727
|
+
console.log(chalk23.cyan(`\u{1F4CB} ${subtasksJson.length} subtasks created:`));
|
|
4728
|
+
subtasksJson.slice(0, 10).forEach((st, i) => {
|
|
4729
|
+
console.log(chalk23.gray(` ${i + 1}. ${st.title}`));
|
|
4730
|
+
});
|
|
4731
|
+
if (subtasksJson.length > 10) {
|
|
4732
|
+
console.log(chalk23.gray(` ... and ${subtasksJson.length - 10} more`));
|
|
4733
|
+
}
|
|
4734
|
+
console.log();
|
|
4735
|
+
}
|
|
4736
|
+
if (acceptanceCriteria.length > 0) {
|
|
4737
|
+
console.log(chalk23.green(`\u2713 ${acceptanceCriteria.length} acceptance criteria defined`));
|
|
4738
|
+
}
|
|
4739
|
+
console.log();
|
|
4740
|
+
console.log(chalk23.gray("Start working with:"));
|
|
4741
|
+
console.log(chalk23.white(` vibetasks vibing ${task.short_id || task.id.substring(0, 8)}`));
|
|
4742
|
+
console.log();
|
|
4743
|
+
process.exit(0);
|
|
4744
|
+
} catch (error) {
|
|
4745
|
+
spinner.fail(chalk23.red("Failed to parse PRD"));
|
|
4746
|
+
if (error.message.includes("Not authenticated")) {
|
|
4747
|
+
console.error(chalk23.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
4748
|
+
console.error(chalk23.gray("Run: vibetasks login\n"));
|
|
4749
|
+
} else {
|
|
4750
|
+
console.error(chalk23.red(`
|
|
4751
|
+
Error: ${error.message}
|
|
4752
|
+
`));
|
|
4753
|
+
}
|
|
4754
|
+
process.exit(1);
|
|
4755
|
+
}
|
|
4756
|
+
});
|
|
4757
|
+
function parsePrdContent(content, verbose = false) {
|
|
4758
|
+
const lines = content.split("\n");
|
|
4759
|
+
const result = {
|
|
4760
|
+
title: "",
|
|
4761
|
+
sections: [],
|
|
4762
|
+
tasks: [],
|
|
4763
|
+
acceptanceCriteria: [],
|
|
4764
|
+
nonGoals: []
|
|
4765
|
+
};
|
|
4766
|
+
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
4767
|
+
if (h1Match) {
|
|
4768
|
+
result.title = h1Match[1].trim();
|
|
4769
|
+
}
|
|
4770
|
+
const summaryMatch = content.match(/^#\s+.+\n\n([\s\S]*?)(?=\n##|$)/);
|
|
4771
|
+
if (summaryMatch) {
|
|
4772
|
+
result.summary = summaryMatch[1].trim().split("\n\n")[0];
|
|
4773
|
+
}
|
|
4774
|
+
const sectionPattern = /^##\s+(.+)$/gm;
|
|
4775
|
+
let match;
|
|
4776
|
+
while ((match = sectionPattern.exec(content)) !== null) {
|
|
4777
|
+
result.sections.push(match[1].trim());
|
|
4778
|
+
}
|
|
4779
|
+
const taskPatterns = [
|
|
4780
|
+
/^[-*]\s+\[[ x]\]\s+(.+)$/gm,
|
|
4781
|
+
// Checkbox items
|
|
4782
|
+
/^[-*]\s+(.+)$/gm,
|
|
4783
|
+
// Regular bullet points under task-like headers
|
|
4784
|
+
/^\d+\.\s+(.+)$/gm
|
|
4785
|
+
// Numbered items
|
|
4786
|
+
];
|
|
4787
|
+
const taskSectionPatterns = [
|
|
4788
|
+
/##\s*(?:tasks?|implementation|steps?|work breakdown|subtasks?)\s*\n([\s\S]*?)(?=\n##|$)/gi,
|
|
4789
|
+
/##\s*(?:requirements?|deliverables?|milestones?)\s*\n([\s\S]*?)(?=\n##|$)/gi
|
|
4790
|
+
];
|
|
4791
|
+
for (const pattern of taskSectionPatterns) {
|
|
4792
|
+
let sectionMatch;
|
|
4793
|
+
while ((sectionMatch = pattern.exec(content)) !== null) {
|
|
4794
|
+
const sectionContent = sectionMatch[1];
|
|
4795
|
+
const bulletPattern = /^[-*]\s+(.+)$/gm;
|
|
4796
|
+
let bulletMatch;
|
|
4797
|
+
while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
|
|
4798
|
+
const task = bulletMatch[1].trim();
|
|
4799
|
+
const cleanTask = task.replace(/^\[[ x]\]\s*/, "").trim();
|
|
4800
|
+
if (cleanTask && !result.tasks.includes(cleanTask)) {
|
|
4801
|
+
result.tasks.push(cleanTask);
|
|
4802
|
+
}
|
|
4803
|
+
}
|
|
4804
|
+
const numberedPattern = /^\d+\.\s+(.+)$/gm;
|
|
4805
|
+
while ((bulletMatch = numberedPattern.exec(sectionContent)) !== null) {
|
|
4806
|
+
const task = bulletMatch[1].trim();
|
|
4807
|
+
if (task && !result.tasks.includes(task)) {
|
|
4808
|
+
result.tasks.push(task);
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
if (result.tasks.length === 0) {
|
|
4814
|
+
const bulletPattern = /^[-*]\s+(.+)$/gm;
|
|
4815
|
+
while ((match = bulletPattern.exec(content)) !== null) {
|
|
4816
|
+
const task = match[1].trim();
|
|
4817
|
+
if (!task.toLowerCase().includes("done when") && !task.toLowerCase().includes("success") && !task.toLowerCase().includes("criterion")) {
|
|
4818
|
+
result.tasks.push(task);
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
const criteriaPatterns = [
|
|
4823
|
+
/##\s*(?:goals?|success criteria|acceptance criteria|done when|definition of done)\s*\n([\s\S]*?)(?=\n##|$)/gi
|
|
4824
|
+
];
|
|
4825
|
+
for (const pattern of criteriaPatterns) {
|
|
4826
|
+
let criteriaMatch;
|
|
4827
|
+
while ((criteriaMatch = pattern.exec(content)) !== null) {
|
|
4828
|
+
const sectionContent = criteriaMatch[1];
|
|
4829
|
+
const bulletPattern = /^[-*]\s+(.+)$/gm;
|
|
4830
|
+
let bulletMatch;
|
|
4831
|
+
while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
|
|
4832
|
+
const criterion = bulletMatch[1].trim();
|
|
4833
|
+
if (criterion && !result.acceptanceCriteria.includes(criterion)) {
|
|
4834
|
+
result.acceptanceCriteria.push(criterion);
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
const nonGoalPatterns = [
|
|
4840
|
+
/##\s*(?:non-goals?|out of scope|not in scope|exclusions?)\s*\n([\s\S]*?)(?=\n##|$)/gi
|
|
4841
|
+
];
|
|
4842
|
+
for (const pattern of nonGoalPatterns) {
|
|
4843
|
+
let nonGoalMatch;
|
|
4844
|
+
while ((nonGoalMatch = pattern.exec(content)) !== null) {
|
|
4845
|
+
const sectionContent = nonGoalMatch[1];
|
|
4846
|
+
const bulletPattern = /^[-*]\s+(.+)$/gm;
|
|
4847
|
+
let bulletMatch;
|
|
4848
|
+
while ((bulletMatch = bulletPattern.exec(sectionContent)) !== null) {
|
|
4849
|
+
const nonGoal = bulletMatch[1].trim();
|
|
4850
|
+
if (nonGoal && !result.nonGoals.includes(nonGoal)) {
|
|
4851
|
+
result.nonGoals.push(nonGoal);
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
if (result.tasks.length > 20) {
|
|
4857
|
+
result.tasks = result.tasks.slice(0, 20);
|
|
4858
|
+
}
|
|
4859
|
+
return result;
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4862
|
+
// src/commands/checkpoint.ts
|
|
4863
|
+
import { Command as Command23 } from "commander";
|
|
4864
|
+
import chalk24 from "chalk";
|
|
4865
|
+
import { AuthManager as AuthManager22, TaskOperations as TaskOperations18 } from "@vibetasks/core";
|
|
4866
|
+
import { execSync as execSync2 } from "child_process";
|
|
4867
|
+
import * as readline from "readline";
|
|
4868
|
+
var checkpointCommand = new Command23("checkpoint").alias("cp").description("Quick context save for current work (interactive)").argument("[task-id]", "Task ID (defaults to your vibing task)").option("-q, --quick", "Quick mode: just git diff + timestamp, no prompts").option("-m, --message <msg>", "Context message (skip prompt)").option("--no-git", "Skip git diff attachment").action(async (taskId2, options) => {
|
|
4869
|
+
try {
|
|
4870
|
+
const authManager = new AuthManager22();
|
|
4871
|
+
const taskOps = await TaskOperations18.fromAuthManager(authManager);
|
|
4872
|
+
const sessionManager2 = getSessionManager();
|
|
4873
|
+
let task;
|
|
4874
|
+
if (taskId2) {
|
|
4875
|
+
const allTasks = await taskOps.getTasks("all");
|
|
4876
|
+
task = allTasks.find(
|
|
4877
|
+
(t) => t.id.startsWith(taskId2) || t.short_id?.toString() === taskId2
|
|
4878
|
+
);
|
|
4879
|
+
if (!task) {
|
|
4880
|
+
console.error(chalk24.red(`Task not found: ${taskId2}`));
|
|
4881
|
+
process.exit(1);
|
|
4882
|
+
}
|
|
4883
|
+
} else {
|
|
4884
|
+
const currentSession2 = await sessionManager2.getCurrentSession();
|
|
4885
|
+
const allTasks = await taskOps.getTasks("all");
|
|
4886
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing");
|
|
4887
|
+
if (vibingTasks.length === 0) {
|
|
4888
|
+
console.error(chalk24.yellow("No vibing tasks found"));
|
|
4889
|
+
console.error(chalk24.gray("Start one with: vibetasks vibing <task-id>"));
|
|
4890
|
+
process.exit(1);
|
|
4891
|
+
}
|
|
4892
|
+
if (vibingTasks.length === 1) {
|
|
4893
|
+
task = vibingTasks[0];
|
|
4894
|
+
} else {
|
|
4895
|
+
const sessionTask = currentSession2 ? vibingTasks.find((t) => t.session_id === currentSession2.fullId) : null;
|
|
4896
|
+
if (sessionTask) {
|
|
4897
|
+
task = sessionTask;
|
|
4898
|
+
} else {
|
|
4899
|
+
console.log(chalk24.yellow("Multiple vibing tasks found:"));
|
|
4900
|
+
vibingTasks.forEach((t, i) => {
|
|
4901
|
+
console.log(chalk24.gray(` ${i + 1}. [${t.short_id || t.id.slice(0, 8)}] ${t.title}`));
|
|
4902
|
+
});
|
|
4903
|
+
console.log(chalk24.gray("\nSpecify task: vibetasks checkpoint <task-id>"));
|
|
4904
|
+
process.exit(1);
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
}
|
|
4908
|
+
console.log(chalk24.cyan("Checkpointing:"), chalk24.bold(task.title));
|
|
4909
|
+
console.log(chalk24.gray(`ID: ${task.short_id || task.id.slice(0, 8)}`));
|
|
4910
|
+
console.log();
|
|
4911
|
+
let contextParts = [];
|
|
4912
|
+
let gitDiff = "";
|
|
4913
|
+
if (options.git !== false) {
|
|
4914
|
+
try {
|
|
4915
|
+
gitDiff = getGitContext();
|
|
4916
|
+
if (gitDiff) {
|
|
4917
|
+
contextParts.push(`## Git Changes
|
|
4918
|
+
\`\`\`
|
|
4919
|
+
${gitDiff}
|
|
4920
|
+
\`\`\``);
|
|
4921
|
+
console.log(chalk24.green("\u2713"), chalk24.gray("Captured git changes"));
|
|
4922
|
+
}
|
|
4923
|
+
} catch {
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4926
|
+
if (options.quick) {
|
|
4927
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4928
|
+
contextParts.unshift(`Checkpoint at ${timestamp}`);
|
|
4929
|
+
const contextNotes2 = contextParts.join("\n\n");
|
|
4930
|
+
await taskOps.updateTask(task.id, { context_notes: contextNotes2 });
|
|
4931
|
+
console.log(chalk24.green("\u2713 Quick checkpoint saved"));
|
|
4932
|
+
process.exit(0);
|
|
4933
|
+
}
|
|
4934
|
+
if (options.message) {
|
|
4935
|
+
contextParts.unshift(`## Progress
|
|
4936
|
+
${options.message}`);
|
|
4937
|
+
const contextNotes2 = contextParts.join("\n\n");
|
|
4938
|
+
await taskOps.updateTask(task.id, { context_notes: contextNotes2 });
|
|
4939
|
+
console.log(chalk24.green("\u2713 Checkpoint saved"));
|
|
4940
|
+
process.exit(0);
|
|
4941
|
+
}
|
|
4942
|
+
const rl = readline.createInterface({
|
|
4943
|
+
input: process.stdin,
|
|
4944
|
+
output: process.stdout
|
|
4945
|
+
});
|
|
4946
|
+
const prompt = (question) => {
|
|
4947
|
+
return new Promise((resolve) => {
|
|
4948
|
+
rl.question(chalk24.cyan(question), (answer) => {
|
|
4949
|
+
resolve(answer.trim());
|
|
4950
|
+
});
|
|
4951
|
+
});
|
|
4952
|
+
};
|
|
4953
|
+
console.log(chalk24.gray("Press Enter to skip any question\n"));
|
|
4954
|
+
const completed = await prompt("What did you just complete? ");
|
|
4955
|
+
if (completed) {
|
|
4956
|
+
contextParts.unshift(`## Completed
|
|
4957
|
+
${completed}`);
|
|
4958
|
+
}
|
|
4959
|
+
const nextStep = await prompt("What's the next step? ");
|
|
4960
|
+
if (nextStep) {
|
|
4961
|
+
contextParts.push(`## Next
|
|
4962
|
+
${nextStep}`);
|
|
4963
|
+
}
|
|
4964
|
+
const blockers = await prompt("Any blockers? (or press Enter) ");
|
|
4965
|
+
if (blockers) {
|
|
4966
|
+
contextParts.push(`## Blockers
|
|
4967
|
+
${blockers}`);
|
|
4968
|
+
}
|
|
4969
|
+
rl.close();
|
|
4970
|
+
if (contextParts.length === 0) {
|
|
4971
|
+
console.log(chalk24.yellow("Nothing to save (all fields empty)"));
|
|
4972
|
+
process.exit(0);
|
|
4973
|
+
}
|
|
4974
|
+
const contextNotes = contextParts.join("\n\n");
|
|
4975
|
+
await taskOps.updateTask(task.id, { context_notes: contextNotes });
|
|
4976
|
+
const currentSession = await sessionManager2.getCurrentSession();
|
|
4977
|
+
if (currentSession) {
|
|
4978
|
+
await sessionManager2.logAction({
|
|
4979
|
+
type: "task_updated",
|
|
4980
|
+
description: `Checkpoint: ${task.title}`,
|
|
4981
|
+
taskId: task.id,
|
|
4982
|
+
taskTitle: task.title
|
|
4983
|
+
});
|
|
4984
|
+
}
|
|
4985
|
+
console.log();
|
|
4986
|
+
console.log(chalk24.green("\u2713 Checkpoint saved"));
|
|
4987
|
+
console.log(chalk24.gray("Context will be available for the next AI session"));
|
|
4988
|
+
process.exit(0);
|
|
4989
|
+
} catch (error) {
|
|
4990
|
+
if (error.message.includes("Not authenticated")) {
|
|
4991
|
+
console.error(chalk24.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
4992
|
+
console.error(chalk24.gray("Run: vibetasks login\n"));
|
|
4993
|
+
} else {
|
|
4994
|
+
console.error(chalk24.red(`
|
|
4995
|
+
Error: ${error.message}
|
|
4996
|
+
`));
|
|
4997
|
+
}
|
|
4998
|
+
process.exit(1);
|
|
4999
|
+
}
|
|
5000
|
+
});
|
|
5001
|
+
function getGitContext() {
|
|
5002
|
+
const parts = [];
|
|
5003
|
+
try {
|
|
5004
|
+
const branch = execSync2("git branch --show-current", { encoding: "utf-8" }).trim();
|
|
5005
|
+
parts.push(`Branch: ${branch}`);
|
|
5006
|
+
const status = execSync2("git status --porcelain", { encoding: "utf-8" }).trim();
|
|
5007
|
+
if (status) {
|
|
5008
|
+
const lines = status.split("\n").slice(0, 20);
|
|
5009
|
+
parts.push(`
|
|
5010
|
+
Changed files:
|
|
5011
|
+
${lines.join("\n")}`);
|
|
5012
|
+
if (status.split("\n").length > 20) {
|
|
5013
|
+
parts.push(`... and ${status.split("\n").length - 20} more`);
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
const log = execSync2("git log --oneline -3", { encoding: "utf-8" }).trim();
|
|
5017
|
+
if (log) {
|
|
5018
|
+
parts.push(`
|
|
5019
|
+
Recent commits:
|
|
5020
|
+
${log}`);
|
|
5021
|
+
}
|
|
5022
|
+
} catch {
|
|
5023
|
+
return "";
|
|
5024
|
+
}
|
|
5025
|
+
return parts.join("\n");
|
|
5026
|
+
}
|
|
5027
|
+
|
|
5028
|
+
// src/commands/log.ts
|
|
5029
|
+
import { Command as Command24 } from "commander";
|
|
5030
|
+
import chalk25 from "chalk";
|
|
5031
|
+
import { AuthManager as AuthManager23, TaskOperations as TaskOperations19 } from "@vibetasks/core";
|
|
5032
|
+
var logCommand = new Command24("log").description("Show change history for a task (like git log)").argument("[task]", "Task ID or short number").option("-n, --limit <number>", "Maximum number of changes to show", "20").option("--oneline", "Compact one-line format").option("--since <date>", "Show changes after date (YYYY-MM-DD)").option("--until <date>", "Show changes before date (YYYY-MM-DD)").option("--type <types>", "Filter by change type (comma-separated)").option("--grep <query>", "Search change messages (ignores task arg)").option("--session <id>", "Show changes from specific session").option("--source <name>", "Filter by source: sentry, github, slack, email, ai, human").action(async (taskArg, options) => {
|
|
5033
|
+
try {
|
|
5034
|
+
const authManager = new AuthManager23();
|
|
5035
|
+
const taskOps = await TaskOperations19.fromAuthManager(authManager);
|
|
5036
|
+
if (options.grep) {
|
|
5037
|
+
const changes2 = await taskOps.searchChanges(options.grep, parseInt(options.limit, 10));
|
|
5038
|
+
if (changes2.length === 0) {
|
|
5039
|
+
console.log(chalk25.gray(`
|
|
5040
|
+
No changes found matching: "${options.grep}"
|
|
5041
|
+
`));
|
|
5042
|
+
process.exit(0);
|
|
5043
|
+
}
|
|
5044
|
+
console.log(chalk25.gray(`
|
|
5045
|
+
Found ${changes2.length} change(s) matching "${options.grep}":
|
|
5046
|
+
`));
|
|
5047
|
+
for (const change of changes2) {
|
|
5048
|
+
printChange(change, options.oneline);
|
|
5049
|
+
}
|
|
5050
|
+
process.exit(0);
|
|
5051
|
+
}
|
|
5052
|
+
if (!taskArg) {
|
|
5053
|
+
console.error(chalk25.red("Error: Task ID or number required"));
|
|
5054
|
+
console.error(chalk25.gray('Usage: vibetasks log <task-id> or vibetasks log --grep "query"'));
|
|
5055
|
+
process.exit(1);
|
|
5056
|
+
}
|
|
5057
|
+
let taskId2 = taskArg;
|
|
5058
|
+
if (/^\d+$/.test(taskArg)) {
|
|
5059
|
+
const shortIdManager = new ShortIdManager();
|
|
5060
|
+
const resolved = await shortIdManager.resolveId(taskArg);
|
|
5061
|
+
if (resolved) {
|
|
5062
|
+
taskId2 = resolved;
|
|
5063
|
+
} else {
|
|
5064
|
+
console.error(chalk25.red(`Task #${taskArg} not found`));
|
|
5065
|
+
console.error(chalk25.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
5066
|
+
process.exit(1);
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
const task = await taskOps.getTask(taskId2);
|
|
5070
|
+
const queryOptions = {
|
|
5071
|
+
limit: parseInt(options.limit, 10)
|
|
5072
|
+
};
|
|
5073
|
+
if (options.since) {
|
|
5074
|
+
queryOptions.since = new Date(options.since).toISOString();
|
|
5075
|
+
}
|
|
5076
|
+
if (options.until) {
|
|
5077
|
+
queryOptions.until = new Date(options.until).toISOString();
|
|
5078
|
+
}
|
|
5079
|
+
if (options.type) {
|
|
5080
|
+
queryOptions.changeTypes = options.type.split(",").map((t) => t.trim());
|
|
5081
|
+
}
|
|
5082
|
+
let changes = await taskOps.getTaskChanges(taskId2, queryOptions);
|
|
5083
|
+
if (options.source) {
|
|
5084
|
+
const source = options.source.toLowerCase();
|
|
5085
|
+
changes = changes.filter((c) => {
|
|
5086
|
+
const msg = c.message || "";
|
|
5087
|
+
switch (source) {
|
|
5088
|
+
case "sentry":
|
|
5089
|
+
return msg.startsWith("[Sentry]");
|
|
5090
|
+
case "github":
|
|
5091
|
+
return msg.startsWith("[GitHub]");
|
|
5092
|
+
case "slack":
|
|
5093
|
+
return msg.startsWith("[Slack]");
|
|
5094
|
+
case "email":
|
|
5095
|
+
return msg.startsWith("[Email]");
|
|
5096
|
+
case "ai":
|
|
5097
|
+
return c.actor_type === "ai";
|
|
5098
|
+
case "human":
|
|
5099
|
+
return c.actor_type === "human";
|
|
5100
|
+
case "integration":
|
|
5101
|
+
case "integrations":
|
|
5102
|
+
return msg.startsWith("[Sentry]") || msg.startsWith("[GitHub]") || msg.startsWith("[Slack]") || msg.startsWith("[Email]");
|
|
5103
|
+
default:
|
|
5104
|
+
return true;
|
|
5105
|
+
}
|
|
5106
|
+
});
|
|
5107
|
+
}
|
|
5108
|
+
if (changes.length === 0) {
|
|
5109
|
+
console.log(chalk25.gray(`
|
|
5110
|
+
No change history found for task: ${task.title}
|
|
5111
|
+
`));
|
|
5112
|
+
process.exit(0);
|
|
5113
|
+
}
|
|
5114
|
+
console.log();
|
|
5115
|
+
console.log(chalk25.cyan.bold(`History for: ${task.title}`));
|
|
5116
|
+
console.log(chalk25.gray(`Task ID: ${taskId2}`));
|
|
5117
|
+
console.log(chalk25.gray(`Status: ${task.status} | Changes: ${changes.length}`));
|
|
5118
|
+
console.log();
|
|
5119
|
+
for (const change of changes) {
|
|
5120
|
+
printChange(change, options.oneline, task.title);
|
|
5121
|
+
}
|
|
5122
|
+
console.log();
|
|
5123
|
+
process.exit(0);
|
|
5124
|
+
} catch (error) {
|
|
5125
|
+
if (error.message.includes("Not authenticated")) {
|
|
5126
|
+
console.error(chalk25.yellow("\nYou need to login first"));
|
|
5127
|
+
console.error(chalk25.gray("Run: vibetasks login\n"));
|
|
5128
|
+
} else {
|
|
5129
|
+
console.error(chalk25.red(`
|
|
5130
|
+
Error: ${error.message}
|
|
5131
|
+
`));
|
|
5132
|
+
}
|
|
5133
|
+
process.exit(1);
|
|
5134
|
+
}
|
|
5135
|
+
});
|
|
5136
|
+
function printChange(change, oneline = false, taskTitle) {
|
|
5137
|
+
const changeId = change.id.substring(0, 8);
|
|
5138
|
+
const date = new Date(change.created_at);
|
|
5139
|
+
const dateStr = date.toLocaleDateString();
|
|
5140
|
+
const timeStr = date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
5141
|
+
const typeColors = {
|
|
5142
|
+
created: chalk25.green,
|
|
5143
|
+
status_changed: chalk25.cyan,
|
|
5144
|
+
title_changed: chalk25.yellow,
|
|
5145
|
+
context_updated: chalk25.blue,
|
|
5146
|
+
priority_changed: chalk25.magenta,
|
|
5147
|
+
subtask_done: chalk25.green,
|
|
5148
|
+
subtask_undone: chalk25.red,
|
|
5149
|
+
subtask_added: chalk25.cyan,
|
|
5150
|
+
archived: chalk25.gray,
|
|
5151
|
+
restored: chalk25.green,
|
|
5152
|
+
revert: chalk25.red,
|
|
5153
|
+
handoff: chalk25.magenta
|
|
5154
|
+
};
|
|
5155
|
+
const colorFn = typeColors[change.change_type] || chalk25.white;
|
|
5156
|
+
let actorIcon;
|
|
5157
|
+
const message = change.message || "";
|
|
5158
|
+
if (message.startsWith("[Sentry]")) {
|
|
5159
|
+
actorIcon = "\u{1F534}";
|
|
5160
|
+
} else if (message.startsWith("[GitHub]")) {
|
|
5161
|
+
actorIcon = "\u{1F419}";
|
|
5162
|
+
} else if (message.startsWith("[Slack]")) {
|
|
5163
|
+
actorIcon = "\u{1F4AC}";
|
|
5164
|
+
} else if (message.startsWith("[Email]")) {
|
|
5165
|
+
actorIcon = "\u{1F4E7}";
|
|
5166
|
+
} else if (change.actor_type === "ai") {
|
|
5167
|
+
actorIcon = "\u{1F916}";
|
|
5168
|
+
} else if (change.actor_type === "human") {
|
|
5169
|
+
actorIcon = "\u{1F464}";
|
|
5170
|
+
} else {
|
|
5171
|
+
actorIcon = "\u2699\uFE0F";
|
|
5172
|
+
}
|
|
5173
|
+
if (oneline) {
|
|
5174
|
+
const message2 = change.message || change.change_type;
|
|
5175
|
+
console.log(
|
|
5176
|
+
`${chalk25.yellow(changeId)} ${chalk25.gray(dateStr)} ${colorFn(change.change_type.padEnd(16))} ${actorIcon} ${message2}`
|
|
5177
|
+
);
|
|
5178
|
+
} else {
|
|
5179
|
+
console.log(chalk25.yellow(`change ${change.id}`));
|
|
5180
|
+
let actorDisplay = `${actorIcon} ${change.actor_type}`;
|
|
5181
|
+
if (message.startsWith("[Sentry]")) {
|
|
5182
|
+
actorDisplay = `${actorIcon} sentry`;
|
|
5183
|
+
} else if (message.startsWith("[GitHub]")) {
|
|
5184
|
+
actorDisplay = `${actorIcon} github`;
|
|
5185
|
+
} else if (message.startsWith("[Slack]")) {
|
|
5186
|
+
actorDisplay = `${actorIcon} slack`;
|
|
5187
|
+
} else if (message.startsWith("[Email]")) {
|
|
5188
|
+
actorDisplay = `${actorIcon} email`;
|
|
5189
|
+
}
|
|
5190
|
+
console.log(`Actor: ${actorDisplay}`);
|
|
5191
|
+
console.log(`Date: ${dateStr} ${timeStr}`);
|
|
5192
|
+
if (change.session_id) {
|
|
5193
|
+
console.log(chalk25.gray(`Session: ${change.session_id.substring(0, 8)}`));
|
|
5194
|
+
}
|
|
5195
|
+
console.log();
|
|
5196
|
+
console.log(` ${colorFn(change.change_type)}: ${change.message || ""}`);
|
|
5197
|
+
if (change.old_value !== void 0 || change.new_value !== void 0) {
|
|
5198
|
+
if (change.field_changed === "status") {
|
|
5199
|
+
console.log(chalk25.gray(` ${chalk25.red(`- ${change.old_value}`)} ${chalk25.green(`+ ${change.new_value}`)}`));
|
|
5200
|
+
} else if (change.field_changed === "context_notes") {
|
|
5201
|
+
const oldVal = truncate(String(change.old_value || ""), 60);
|
|
5202
|
+
const newVal = truncate(String(change.new_value || ""), 60);
|
|
5203
|
+
if (oldVal) console.log(chalk25.red(` - ${oldVal}`));
|
|
5204
|
+
if (newVal) console.log(chalk25.green(` + ${newVal}`));
|
|
5205
|
+
} else if (change.field_changed === "subtasks") {
|
|
5206
|
+
const newSub = change.new_value;
|
|
5207
|
+
if (newSub?.title) {
|
|
5208
|
+
console.log(chalk25.gray(` Subtask: ${newSub.title}`));
|
|
5209
|
+
}
|
|
5210
|
+
} else if (change.old_value !== void 0 && change.new_value !== void 0) {
|
|
5211
|
+
console.log(chalk25.red(` - ${change.old_value}`));
|
|
5212
|
+
console.log(chalk25.green(` + ${change.new_value}`));
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
console.log();
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
function truncate(str, maxLength) {
|
|
5219
|
+
if (!str) return "";
|
|
5220
|
+
if (str.length <= maxLength) return str;
|
|
5221
|
+
return str.substring(0, maxLength - 3) + "...";
|
|
5222
|
+
}
|
|
5223
|
+
|
|
5224
|
+
// src/commands/show.ts
|
|
5225
|
+
import { Command as Command25 } from "commander";
|
|
5226
|
+
import chalk26 from "chalk";
|
|
5227
|
+
import { AuthManager as AuthManager24, TaskOperations as TaskOperations20 } from "@vibetasks/core";
|
|
5228
|
+
var showCommand = new Command25("show").description("Show details of a specific change (like git show)").argument("<change-id>", "Change ID (full or partial)").action(async (changeId, options) => {
|
|
5229
|
+
try {
|
|
5230
|
+
const authManager = new AuthManager24();
|
|
5231
|
+
const taskOps = await TaskOperations20.fromAuthManager(authManager);
|
|
5232
|
+
const change = await taskOps.getChange(changeId);
|
|
5233
|
+
const task = await taskOps.getTask(change.task_id);
|
|
5234
|
+
const date = new Date(change.created_at);
|
|
5235
|
+
const dateStr = date.toLocaleDateString();
|
|
5236
|
+
const timeStr = date.toLocaleTimeString();
|
|
5237
|
+
const actorIcon = change.actor_type === "ai" ? "\u{1F916}" : change.actor_type === "human" ? "\u{1F464}" : "\u2699\uFE0F";
|
|
5238
|
+
console.log();
|
|
5239
|
+
console.log(chalk26.yellow(`change ${change.id}`));
|
|
5240
|
+
console.log(`Task: ${chalk26.cyan(task.title)}`);
|
|
5241
|
+
console.log(` ${chalk26.gray(change.task_id)}`);
|
|
5242
|
+
console.log(`Actor: ${actorIcon} ${change.actor_type}`);
|
|
5243
|
+
console.log(`Date: ${dateStr} ${timeStr}`);
|
|
5244
|
+
if (change.session_id) {
|
|
5245
|
+
console.log(`Session: ${chalk26.gray(change.session_id)}`);
|
|
5246
|
+
}
|
|
5247
|
+
console.log();
|
|
5248
|
+
console.log(chalk26.bold(`${change.change_type}`));
|
|
5249
|
+
if (change.message) {
|
|
5250
|
+
console.log(` ${change.message}`);
|
|
5251
|
+
}
|
|
5252
|
+
console.log();
|
|
5253
|
+
if (change.field_changed) {
|
|
5254
|
+
console.log(chalk26.gray(`Field: ${change.field_changed}`));
|
|
5255
|
+
console.log();
|
|
5256
|
+
if (change.old_value !== void 0) {
|
|
5257
|
+
const oldVal = formatValue(change.old_value);
|
|
5258
|
+
console.log(chalk26.red(`- ${oldVal}`));
|
|
5259
|
+
}
|
|
5260
|
+
if (change.new_value !== void 0) {
|
|
5261
|
+
const newVal = formatValue(change.new_value);
|
|
5262
|
+
console.log(chalk26.green(`+ ${newVal}`));
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
if (change.reverted_change_id) {
|
|
5266
|
+
console.log();
|
|
5267
|
+
console.log(chalk26.gray(`Reverted change: ${change.reverted_change_id}`));
|
|
5268
|
+
}
|
|
5269
|
+
console.log();
|
|
5270
|
+
process.exit(0);
|
|
5271
|
+
} catch (error) {
|
|
5272
|
+
if (error.message.includes("Not authenticated")) {
|
|
5273
|
+
console.error(chalk26.yellow("\nYou need to login first"));
|
|
5274
|
+
console.error(chalk26.gray("Run: vibetasks login\n"));
|
|
5275
|
+
} else if (error.code === "PGRST116") {
|
|
5276
|
+
console.error(chalk26.red(`
|
|
5277
|
+
Change not found: ${changeId}
|
|
5278
|
+
`));
|
|
5279
|
+
} else {
|
|
5280
|
+
console.error(chalk26.red(`
|
|
5281
|
+
Error: ${error.message}
|
|
5282
|
+
`));
|
|
5283
|
+
}
|
|
5284
|
+
process.exit(1);
|
|
5285
|
+
}
|
|
5286
|
+
});
|
|
5287
|
+
function formatValue(value) {
|
|
5288
|
+
if (value === null || value === void 0) {
|
|
5289
|
+
return "(empty)";
|
|
5290
|
+
}
|
|
5291
|
+
if (typeof value === "object") {
|
|
5292
|
+
return JSON.stringify(value, null, 2);
|
|
5293
|
+
}
|
|
5294
|
+
return String(value);
|
|
5295
|
+
}
|
|
5296
|
+
|
|
5297
|
+
// src/commands/pause.ts
|
|
5298
|
+
import { Command as Command26 } from "commander";
|
|
5299
|
+
import chalk27 from "chalk";
|
|
5300
|
+
import ora16 from "ora";
|
|
5301
|
+
import { AuthManager as AuthManager25, TaskOperations as TaskOperations21 } from "@vibetasks/core";
|
|
5302
|
+
var pauseCommand = new Command26("pause").description("Pause a task (like git stash) - can be resumed later").argument("[task]", "Task ID or short number to pause").option("-m, --message <reason>", "Reason for pausing").option("-l, --list", "List all paused tasks").action(async (taskArg, options) => {
|
|
5303
|
+
const spinner = ora16();
|
|
5304
|
+
try {
|
|
5305
|
+
const authManager = new AuthManager25();
|
|
5306
|
+
const taskOps = await TaskOperations21.fromAuthManager(authManager);
|
|
5307
|
+
if (options.list) {
|
|
5308
|
+
spinner.start("Fetching paused tasks...");
|
|
5309
|
+
const allTasks = await taskOps.getTasks("all");
|
|
5310
|
+
const pausedTasks = allTasks.filter((t) => t.status === "paused");
|
|
5311
|
+
spinner.stop();
|
|
5312
|
+
if (pausedTasks.length === 0) {
|
|
5313
|
+
console.log(chalk27.yellow("\nNo paused tasks found.\n"));
|
|
5314
|
+
console.log(chalk27.gray('Pause a task with: vibetasks pause <task-id> -m "reason"\n'));
|
|
5315
|
+
process.exit(0);
|
|
5316
|
+
}
|
|
5317
|
+
console.log(chalk27.white("\nPaused Tasks:\n"));
|
|
5318
|
+
pausedTasks.forEach((task2, index) => {
|
|
5319
|
+
console.log(
|
|
5320
|
+
chalk27.gray(`${index + 1}. `) + chalk27.yellow("\u23F8 ") + chalk27.white(task2.title) + chalk27.gray(` [${task2.id.slice(0, 8)}]`)
|
|
5321
|
+
);
|
|
5322
|
+
if (task2.context_notes) {
|
|
5323
|
+
const lastLine = task2.context_notes.split("\n").pop();
|
|
5324
|
+
if (lastLine) {
|
|
5325
|
+
console.log(chalk27.gray(` \u2514\u2500 ${lastLine}`));
|
|
5326
|
+
}
|
|
5327
|
+
}
|
|
5328
|
+
});
|
|
5329
|
+
console.log(chalk27.gray(`
|
|
5330
|
+
Total: ${pausedTasks.length} paused task(s)
|
|
5331
|
+
`));
|
|
5332
|
+
console.log(chalk27.gray("Resume with: vibetasks resume <task-id>\n"));
|
|
5333
|
+
process.exit(0);
|
|
5334
|
+
}
|
|
5335
|
+
if (!taskArg) {
|
|
5336
|
+
console.log(chalk27.yellow("\nUsage:"));
|
|
5337
|
+
console.log(chalk27.gray(" vibetasks pause <task-id> # Pause a specific task"));
|
|
5338
|
+
console.log(chalk27.gray(" vibetasks pause --list # List paused tasks\n"));
|
|
5339
|
+
process.exit(1);
|
|
5340
|
+
}
|
|
5341
|
+
let taskId2 = taskArg;
|
|
5342
|
+
if (/^\d+$/.test(taskArg)) {
|
|
5343
|
+
const shortIdManager = new ShortIdManager();
|
|
5344
|
+
const resolved = await shortIdManager.resolveId(taskArg);
|
|
5345
|
+
if (resolved) {
|
|
5346
|
+
taskId2 = resolved;
|
|
5347
|
+
} else {
|
|
5348
|
+
console.error(chalk27.red(`Task #${taskArg} not found`));
|
|
5349
|
+
console.error(chalk27.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
5350
|
+
process.exit(1);
|
|
5351
|
+
}
|
|
5352
|
+
} else if (taskArg.length < 32) {
|
|
5353
|
+
const allTasks = await taskOps.getTasks("all");
|
|
5354
|
+
const matchingTask = allTasks.find((t) => t.id.startsWith(taskArg));
|
|
5355
|
+
if (!matchingTask) {
|
|
5356
|
+
console.error(chalk27.red(`Task not found: ${taskArg}`));
|
|
5357
|
+
process.exit(1);
|
|
5358
|
+
}
|
|
5359
|
+
taskId2 = matchingTask.id;
|
|
5360
|
+
}
|
|
5361
|
+
const task = await taskOps.getTask(taskId2);
|
|
5362
|
+
if (task.status === "paused") {
|
|
5363
|
+
console.log(chalk27.yellow(`
|
|
5364
|
+
Task is already paused: ${task.title}
|
|
5365
|
+
`));
|
|
5366
|
+
console.log(chalk27.gray("Resume with: vibetasks resume " + taskId2.slice(0, 8) + "\n"));
|
|
5367
|
+
process.exit(0);
|
|
5368
|
+
}
|
|
5369
|
+
if (task.status === "done" || task.status === "archived") {
|
|
5370
|
+
console.error(chalk27.red(`
|
|
5371
|
+
Cannot pause a ${task.status} task.
|
|
5372
|
+
`));
|
|
5373
|
+
process.exit(1);
|
|
5374
|
+
}
|
|
5375
|
+
spinner.start("Pausing task...");
|
|
5376
|
+
const pausedTask = await taskOps.pauseTask(taskId2, options.message);
|
|
5377
|
+
spinner.succeed(chalk27.green("Task paused!"));
|
|
5378
|
+
console.log();
|
|
5379
|
+
console.log(chalk27.yellow("\u23F8 ") + chalk27.white(pausedTask.title));
|
|
5380
|
+
if (options.message) {
|
|
5381
|
+
console.log(chalk27.gray(` Reason: ${options.message}`));
|
|
5382
|
+
}
|
|
5383
|
+
console.log();
|
|
5384
|
+
console.log(chalk27.gray(`Resume with: vibetasks resume ${taskId2.slice(0, 8)}`));
|
|
5385
|
+
console.log(chalk27.gray(`View paused: vibetasks pause --list`));
|
|
5386
|
+
console.log();
|
|
5387
|
+
process.exit(0);
|
|
5388
|
+
} catch (error) {
|
|
5389
|
+
spinner.fail(chalk27.red("Failed to pause task"));
|
|
5390
|
+
if (error.message.includes("Not authenticated")) {
|
|
5391
|
+
console.error(chalk27.yellow("\nYou need to login first"));
|
|
5392
|
+
console.error(chalk27.gray("Run: vibetasks login\n"));
|
|
5393
|
+
} else {
|
|
5394
|
+
console.error(chalk27.red(`
|
|
5395
|
+
Error: ${error.message}
|
|
5396
|
+
`));
|
|
5397
|
+
}
|
|
5398
|
+
process.exit(1);
|
|
5399
|
+
}
|
|
5400
|
+
});
|
|
5401
|
+
|
|
5402
|
+
// src/commands/resume.ts
|
|
5403
|
+
import { Command as Command27 } from "commander";
|
|
5404
|
+
import chalk28 from "chalk";
|
|
5405
|
+
import ora17 from "ora";
|
|
5406
|
+
import inquirer7 from "inquirer";
|
|
5407
|
+
import { AuthManager as AuthManager26, TaskOperations as TaskOperations22 } from "@vibetasks/core";
|
|
5408
|
+
var resumeCommand = new Command27("resume").description("Resume a paused task (like git stash pop)").argument("[task]", "Task ID or short number to resume (or pick from list)").action(async (taskArg) => {
|
|
5409
|
+
const spinner = ora17();
|
|
5410
|
+
try {
|
|
5411
|
+
const authManager = new AuthManager26();
|
|
5412
|
+
const taskOps = await TaskOperations22.fromAuthManager(authManager);
|
|
5413
|
+
const allTasks = await taskOps.getTasks("all");
|
|
5414
|
+
const pausedTasks = allTasks.filter((t) => t.status === "paused");
|
|
5415
|
+
if (pausedTasks.length === 0) {
|
|
5416
|
+
console.log(chalk28.yellow("\nNo paused tasks to resume.\n"));
|
|
5417
|
+
console.log(chalk28.gray("Pause a task with: vibetasks pause <task-id>\n"));
|
|
5418
|
+
process.exit(0);
|
|
5419
|
+
}
|
|
5420
|
+
let taskId2;
|
|
5421
|
+
if (!taskArg) {
|
|
5422
|
+
if (pausedTasks.length === 1) {
|
|
5423
|
+
taskId2 = pausedTasks[0].id;
|
|
5424
|
+
console.log(chalk28.gray(`
|
|
5425
|
+
Auto-selecting the only paused task...
|
|
5426
|
+
`));
|
|
5427
|
+
} else {
|
|
5428
|
+
const { selectedTask } = await inquirer7.prompt([{
|
|
5429
|
+
type: "list",
|
|
5430
|
+
name: "selectedTask",
|
|
5431
|
+
message: "Pick a paused task to resume:",
|
|
5432
|
+
choices: pausedTasks.map((t) => ({
|
|
5433
|
+
name: `${chalk28.yellow("\u23F8")} ${t.title}${t.context_notes ? chalk28.gray(` - ${t.context_notes.split("\n").pop()?.slice(0, 40)}...`) : ""}`,
|
|
5434
|
+
value: t.id
|
|
5435
|
+
}))
|
|
5436
|
+
}]);
|
|
5437
|
+
taskId2 = selectedTask;
|
|
5438
|
+
}
|
|
5439
|
+
} else {
|
|
5440
|
+
taskId2 = taskArg;
|
|
5441
|
+
if (/^\d+$/.test(taskArg)) {
|
|
5442
|
+
const shortIdManager = new ShortIdManager();
|
|
5443
|
+
const resolved = await shortIdManager.resolveId(taskArg);
|
|
5444
|
+
if (resolved) {
|
|
5445
|
+
taskId2 = resolved;
|
|
5446
|
+
} else {
|
|
5447
|
+
console.error(chalk28.red(`Task #${taskArg} not found`));
|
|
5448
|
+
console.error(chalk28.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
5449
|
+
process.exit(1);
|
|
5450
|
+
}
|
|
5451
|
+
} else if (taskArg.length < 32) {
|
|
5452
|
+
const matchingTask = pausedTasks.find((t) => t.id.startsWith(taskArg)) || allTasks.find((t) => t.id.startsWith(taskArg));
|
|
5453
|
+
if (!matchingTask) {
|
|
5454
|
+
console.error(chalk28.red(`Task not found: ${taskArg}`));
|
|
5455
|
+
process.exit(1);
|
|
5456
|
+
}
|
|
5457
|
+
taskId2 = matchingTask.id;
|
|
5458
|
+
}
|
|
5459
|
+
}
|
|
5460
|
+
const task = await taskOps.getTask(taskId2);
|
|
5461
|
+
if (task.status !== "paused") {
|
|
5462
|
+
console.log(chalk28.yellow(`
|
|
5463
|
+
Task is not paused (status: ${task.status}): ${task.title}
|
|
5464
|
+
`));
|
|
5465
|
+
if (task.status === "todo") {
|
|
5466
|
+
console.log(chalk28.gray("Start vibing with: vibetasks vibing " + taskId2.slice(0, 8) + "\n"));
|
|
5467
|
+
}
|
|
5468
|
+
process.exit(0);
|
|
5469
|
+
}
|
|
5470
|
+
spinner.start("Resuming task...");
|
|
5471
|
+
const resumedTask = await taskOps.resumeTask(taskId2);
|
|
5472
|
+
spinner.succeed(chalk28.green("Task resumed!"));
|
|
5473
|
+
console.log();
|
|
5474
|
+
console.log(chalk28.magenta("\u26A1 ") + chalk28.white(resumedTask.title));
|
|
5475
|
+
console.log(chalk28.gray(` Status: vibing`));
|
|
5476
|
+
if (task.context_notes) {
|
|
5477
|
+
console.log(chalk28.gray(` Context: ${task.context_notes.split("\n").pop()?.slice(0, 60)}...`));
|
|
5478
|
+
}
|
|
5479
|
+
console.log();
|
|
5480
|
+
console.log(chalk28.gray(`Complete with: vibetasks done ${taskId2.slice(0, 8)}`));
|
|
5481
|
+
console.log(chalk28.gray(`Pause again: vibetasks pause ${taskId2.slice(0, 8)}`));
|
|
5482
|
+
console.log();
|
|
5483
|
+
process.exit(0);
|
|
5484
|
+
} catch (error) {
|
|
5485
|
+
spinner.fail(chalk28.red("Failed to resume task"));
|
|
5486
|
+
if (error.message.includes("Not authenticated")) {
|
|
5487
|
+
console.error(chalk28.yellow("\nYou need to login first"));
|
|
5488
|
+
console.error(chalk28.gray("Run: vibetasks login\n"));
|
|
5489
|
+
} else {
|
|
5490
|
+
console.error(chalk28.red(`
|
|
5491
|
+
Error: ${error.message}
|
|
5492
|
+
`));
|
|
5493
|
+
}
|
|
5494
|
+
process.exit(1);
|
|
5495
|
+
}
|
|
5496
|
+
});
|
|
5497
|
+
|
|
5498
|
+
// src/commands/revert.ts
|
|
5499
|
+
import { Command as Command28 } from "commander";
|
|
5500
|
+
import chalk29 from "chalk";
|
|
5501
|
+
import ora18 from "ora";
|
|
5502
|
+
import inquirer8 from "inquirer";
|
|
5503
|
+
import { AuthManager as AuthManager27, TaskOperations as TaskOperations23 } from "@vibetasks/core";
|
|
5504
|
+
var revertCommand = new Command28("revert").description("Revert a specific change (like git revert) - non-destructive").argument("[change-id]", "Change ID to revert (or pick from recent changes)").option("-t, --task <task>", "Task ID or number to show changes for").option("-m, --message <message>", "Custom revert message").action(async (changeId, options) => {
|
|
5505
|
+
const spinner = ora18();
|
|
5506
|
+
try {
|
|
5507
|
+
const authManager = new AuthManager27();
|
|
5508
|
+
const taskOps = await TaskOperations23.fromAuthManager(authManager);
|
|
5509
|
+
if (options.task || !changeId) {
|
|
5510
|
+
let taskId2;
|
|
5511
|
+
if (options.task) {
|
|
5512
|
+
if (/^\d+$/.test(options.task)) {
|
|
5513
|
+
const shortIdManager = new ShortIdManager();
|
|
5514
|
+
const resolved = await shortIdManager.resolveId(options.task);
|
|
5515
|
+
if (resolved) {
|
|
5516
|
+
taskId2 = resolved;
|
|
5517
|
+
} else {
|
|
5518
|
+
console.error(chalk29.red(`Task #${options.task} not found`));
|
|
5519
|
+
process.exit(1);
|
|
5520
|
+
}
|
|
5521
|
+
} else if (options.task.length < 32) {
|
|
5522
|
+
const allTasks = await taskOps.getTasks("all");
|
|
5523
|
+
const matchingTask = allTasks.find((t) => t.id.startsWith(options.task));
|
|
5524
|
+
if (matchingTask) {
|
|
5525
|
+
taskId2 = matchingTask.id;
|
|
5526
|
+
}
|
|
5527
|
+
} else {
|
|
5528
|
+
taskId2 = options.task;
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
if (!taskId2) {
|
|
5532
|
+
console.log(chalk29.yellow("\nUsage:"));
|
|
5533
|
+
console.log(chalk29.gray(" vibetasks revert <change-id> # Revert specific change"));
|
|
5534
|
+
console.log(chalk29.gray(" vibetasks revert --task <task-id> # Pick from task changes"));
|
|
5535
|
+
console.log(chalk29.gray(" vibetasks log <task-id> # View changes first\n"));
|
|
5536
|
+
process.exit(1);
|
|
5537
|
+
}
|
|
5538
|
+
const changes = await taskOps.getTaskChanges(taskId2, { limit: 20 });
|
|
5539
|
+
const task2 = await taskOps.getTask(taskId2);
|
|
5540
|
+
if (changes.length === 0) {
|
|
5541
|
+
console.log(chalk29.yellow(`
|
|
5542
|
+
No changes found for task: ${task2.title}
|
|
5543
|
+
`));
|
|
5544
|
+
process.exit(0);
|
|
5545
|
+
}
|
|
5546
|
+
const revertableChanges = changes.filter((c) => c.change_type !== "created" && c.change_type !== "revert");
|
|
5547
|
+
if (revertableChanges.length === 0) {
|
|
5548
|
+
console.log(chalk29.yellow(`
|
|
5549
|
+
No revertable changes for task: ${task2.title}`));
|
|
5550
|
+
console.log(chalk29.gray("Only the initial creation exists.\n"));
|
|
5551
|
+
process.exit(0);
|
|
5552
|
+
}
|
|
5553
|
+
console.log(chalk29.cyan(`
|
|
5554
|
+
Recent changes for: ${task2.title}
|
|
5555
|
+
`));
|
|
5556
|
+
const { selectedChange } = await inquirer8.prompt([{
|
|
5557
|
+
type: "list",
|
|
5558
|
+
name: "selectedChange",
|
|
5559
|
+
message: "Select a change to revert:",
|
|
5560
|
+
choices: revertableChanges.map((c) => {
|
|
5561
|
+
const date = new Date(c.created_at).toLocaleDateString();
|
|
5562
|
+
const typeColor = getTypeColor(c.change_type);
|
|
5563
|
+
return {
|
|
5564
|
+
name: `${chalk29.gray(c.id.slice(0, 8))} ${chalk29.gray(date)} ${typeColor(c.change_type)} - ${c.message || "No message"}`,
|
|
5565
|
+
value: c.id
|
|
5566
|
+
};
|
|
5567
|
+
})
|
|
5568
|
+
}]);
|
|
5569
|
+
changeId = selectedChange;
|
|
5570
|
+
}
|
|
5571
|
+
spinner.start("Loading change...");
|
|
5572
|
+
const change = await taskOps.getChange(changeId);
|
|
5573
|
+
const task = await taskOps.getTask(change.task_id);
|
|
5574
|
+
spinner.stop();
|
|
5575
|
+
console.log();
|
|
5576
|
+
console.log(chalk29.cyan("Change to revert:"));
|
|
5577
|
+
console.log(chalk29.gray(` ID: ${change.id}`));
|
|
5578
|
+
console.log(chalk29.gray(` Task: ${task.title}`));
|
|
5579
|
+
console.log(chalk29.gray(` Type: ${change.change_type}`));
|
|
5580
|
+
console.log(chalk29.gray(` Message: ${change.message || "No message"}`));
|
|
5581
|
+
if (change.old_value !== void 0 || change.new_value !== void 0) {
|
|
5582
|
+
console.log(chalk29.gray(` Field: ${change.field_changed}`));
|
|
5583
|
+
if (change.old_value !== void 0) {
|
|
5584
|
+
console.log(chalk29.red(` Old: ${formatValue2(change.old_value)}`));
|
|
5585
|
+
}
|
|
5586
|
+
if (change.new_value !== void 0) {
|
|
5587
|
+
console.log(chalk29.green(` New: ${formatValue2(change.new_value)}`));
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5590
|
+
console.log();
|
|
5591
|
+
const { confirm } = await inquirer8.prompt([{
|
|
5592
|
+
type: "confirm",
|
|
5593
|
+
name: "confirm",
|
|
5594
|
+
message: "Revert this change?",
|
|
5595
|
+
default: true
|
|
5596
|
+
}]);
|
|
5597
|
+
if (!confirm) {
|
|
5598
|
+
console.log(chalk29.gray("\nRevert cancelled.\n"));
|
|
5599
|
+
process.exit(0);
|
|
5600
|
+
}
|
|
5601
|
+
spinner.start("Reverting change...");
|
|
5602
|
+
const revertChange = await taskOps.revertChange(changeId, options.message);
|
|
5603
|
+
spinner.succeed(chalk29.green("Change reverted!"));
|
|
5604
|
+
console.log();
|
|
5605
|
+
console.log(chalk29.yellow("\u21A9 ") + chalk29.white(`Reverted: ${change.message || change.change_type}`));
|
|
5606
|
+
console.log(chalk29.gray(` Revert ID: ${revertChange.id.slice(0, 8)}`));
|
|
5607
|
+
console.log();
|
|
5608
|
+
console.log(chalk29.gray(`View history: vibetasks log ${task.id.slice(0, 8)}`));
|
|
5609
|
+
console.log();
|
|
5610
|
+
process.exit(0);
|
|
5611
|
+
} catch (error) {
|
|
5612
|
+
spinner.fail(chalk29.red("Failed to revert change"));
|
|
5613
|
+
if (error.message.includes("Not authenticated")) {
|
|
5614
|
+
console.error(chalk29.yellow("\nYou need to login first"));
|
|
5615
|
+
console.error(chalk29.gray("Run: vibetasks login\n"));
|
|
5616
|
+
} else if (error.code === "PGRST116") {
|
|
5617
|
+
console.error(chalk29.red(`
|
|
5618
|
+
Change not found: ${changeId}
|
|
5619
|
+
`));
|
|
5620
|
+
console.error(chalk29.gray("Use: vibetasks log <task-id> to see valid change IDs\n"));
|
|
5621
|
+
} else {
|
|
5622
|
+
console.error(chalk29.red(`
|
|
5623
|
+
Error: ${error.message}
|
|
5624
|
+
`));
|
|
5625
|
+
}
|
|
5626
|
+
process.exit(1);
|
|
5627
|
+
}
|
|
5628
|
+
});
|
|
5629
|
+
function getTypeColor(changeType) {
|
|
5630
|
+
const colors = {
|
|
5631
|
+
created: chalk29.green,
|
|
5632
|
+
status_changed: chalk29.cyan,
|
|
5633
|
+
title_changed: chalk29.yellow,
|
|
5634
|
+
context_updated: chalk29.blue,
|
|
5635
|
+
priority_changed: chalk29.magenta,
|
|
5636
|
+
subtask_done: chalk29.green,
|
|
5637
|
+
subtask_undone: chalk29.red,
|
|
5638
|
+
subtask_added: chalk29.cyan,
|
|
5639
|
+
archived: chalk29.gray,
|
|
5640
|
+
restored: chalk29.green,
|
|
5641
|
+
revert: chalk29.red
|
|
5642
|
+
};
|
|
5643
|
+
return colors[changeType] || chalk29.white;
|
|
5644
|
+
}
|
|
5645
|
+
function formatValue2(value) {
|
|
5646
|
+
if (value === null || value === void 0) {
|
|
5647
|
+
return "(empty)";
|
|
5648
|
+
}
|
|
5649
|
+
if (typeof value === "object") {
|
|
5650
|
+
return JSON.stringify(value);
|
|
5651
|
+
}
|
|
5652
|
+
const str = String(value);
|
|
5653
|
+
return str.length > 50 ? str.slice(0, 47) + "..." : str;
|
|
5654
|
+
}
|
|
5655
|
+
|
|
5656
|
+
// src/commands/blame.ts
|
|
5657
|
+
import { Command as Command29 } from "commander";
|
|
5658
|
+
import chalk30 from "chalk";
|
|
5659
|
+
import { AuthManager as AuthManager28, TaskOperations as TaskOperations24 } from "@vibetasks/core";
|
|
5660
|
+
var blameCommand = new Command29("blame").description("Show who changed what on a task (like git blame)").argument("<task>", "Task ID or short number").option("-f, --field <field>", "Focus on specific field (status, title, context_notes, priority)").action(async (taskArg, options) => {
|
|
5661
|
+
try {
|
|
5662
|
+
const authManager = new AuthManager28();
|
|
5663
|
+
const taskOps = await TaskOperations24.fromAuthManager(authManager);
|
|
5664
|
+
let taskId2 = taskArg;
|
|
5665
|
+
if (/^\d+$/.test(taskArg)) {
|
|
5666
|
+
const shortIdManager = new ShortIdManager();
|
|
5667
|
+
const resolved = await shortIdManager.resolveId(taskArg);
|
|
5668
|
+
if (resolved) {
|
|
5669
|
+
taskId2 = resolved;
|
|
5670
|
+
} else {
|
|
5671
|
+
console.error(chalk30.red(`Task #${taskArg} not found`));
|
|
5672
|
+
console.error(chalk30.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
5673
|
+
process.exit(1);
|
|
5674
|
+
}
|
|
5675
|
+
} else if (taskArg.length < 32) {
|
|
5676
|
+
const allTasks = await taskOps.getTasks("all", true);
|
|
5677
|
+
const matchingTask = allTasks.find((t) => t.id.startsWith(taskArg));
|
|
5678
|
+
if (!matchingTask) {
|
|
5679
|
+
console.error(chalk30.red(`Task not found: ${taskArg}`));
|
|
5680
|
+
process.exit(1);
|
|
5681
|
+
}
|
|
5682
|
+
taskId2 = matchingTask.id;
|
|
5683
|
+
}
|
|
5684
|
+
const task = await taskOps.getTask(taskId2);
|
|
5685
|
+
const changes = await taskOps.getTaskChanges(taskId2, { limit: 100 });
|
|
5686
|
+
if (changes.length === 0) {
|
|
5687
|
+
console.log(chalk30.yellow(`
|
|
5688
|
+
No change history for task: ${task.title}
|
|
5689
|
+
`));
|
|
5690
|
+
process.exit(0);
|
|
5691
|
+
}
|
|
5692
|
+
const blameMap = {};
|
|
5693
|
+
const currentValues = {
|
|
5694
|
+
status: task.status,
|
|
5695
|
+
title: task.title,
|
|
5696
|
+
priority: task.priority,
|
|
5697
|
+
context_notes: task.context_notes,
|
|
5698
|
+
description: task.description
|
|
5699
|
+
};
|
|
5700
|
+
for (const change of changes) {
|
|
5701
|
+
if (!change.field_changed) continue;
|
|
5702
|
+
if (options.field && change.field_changed !== options.field) continue;
|
|
5703
|
+
if (!blameMap[change.field_changed]) {
|
|
5704
|
+
blameMap[change.field_changed] = {
|
|
5705
|
+
currentValue: currentValues[change.field_changed],
|
|
5706
|
+
changes: []
|
|
5707
|
+
};
|
|
5708
|
+
}
|
|
5709
|
+
blameMap[change.field_changed].changes.push({
|
|
5710
|
+
changeId: change.id,
|
|
5711
|
+
actor: change.session_id?.slice(0, 8) || "unknown",
|
|
5712
|
+
actorType: change.actor_type,
|
|
5713
|
+
date: new Date(change.created_at),
|
|
5714
|
+
oldValue: change.old_value,
|
|
5715
|
+
newValue: change.new_value,
|
|
5716
|
+
message: change.message || change.change_type
|
|
5717
|
+
});
|
|
5718
|
+
}
|
|
5719
|
+
console.log();
|
|
5720
|
+
console.log(chalk30.cyan.bold(`Blame for: ${task.title}`));
|
|
5721
|
+
console.log(chalk30.gray(`Task ID: ${taskId2}`));
|
|
5722
|
+
console.log();
|
|
5723
|
+
const fields = options.field ? [options.field] : Object.keys(blameMap).sort();
|
|
5724
|
+
if (fields.length === 0) {
|
|
5725
|
+
console.log(chalk30.yellow("No field changes tracked."));
|
|
5726
|
+
console.log(chalk30.gray("Only status, title, priority, context_notes changes are tracked.\n"));
|
|
5727
|
+
process.exit(0);
|
|
5728
|
+
}
|
|
5729
|
+
for (const field of fields) {
|
|
5730
|
+
const blame = blameMap[field];
|
|
5731
|
+
if (!blame) continue;
|
|
5732
|
+
console.log(chalk30.white.bold(`${field}:`));
|
|
5733
|
+
console.log(chalk30.gray(` Current: ${formatValue3(blame.currentValue)}`));
|
|
5734
|
+
console.log();
|
|
5735
|
+
for (const change of blame.changes.slice(0, 5)) {
|
|
5736
|
+
const actorIcon = change.actorType === "ai" ? "\u{1F916}" : change.actorType === "human" ? "\u{1F464}" : "\u2699\uFE0F";
|
|
5737
|
+
const dateStr = change.date.toLocaleDateString();
|
|
5738
|
+
const timeStr = change.date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
5739
|
+
console.log(
|
|
5740
|
+
chalk30.gray(` ${chalk30.yellow(change.changeId.slice(0, 8))} `) + chalk30.gray(`${dateStr} ${timeStr} `) + `${actorIcon} ` + chalk30.white(change.message)
|
|
5741
|
+
);
|
|
5742
|
+
if (change.oldValue !== void 0 || change.newValue !== void 0) {
|
|
5743
|
+
if (field === "status") {
|
|
5744
|
+
console.log(chalk30.gray(` ${chalk30.red(String(change.oldValue))} \u2192 ${chalk30.green(String(change.newValue))}`));
|
|
5745
|
+
} else {
|
|
5746
|
+
const oldStr = formatValue3(change.oldValue);
|
|
5747
|
+
const newStr = formatValue3(change.newValue);
|
|
5748
|
+
if (oldStr) console.log(chalk30.red(` - ${oldStr}`));
|
|
5749
|
+
if (newStr) console.log(chalk30.green(` + ${newStr}`));
|
|
5750
|
+
}
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
if (blame.changes.length > 5) {
|
|
5754
|
+
console.log(chalk30.gray(` ... and ${blame.changes.length - 5} more changes`));
|
|
5755
|
+
}
|
|
5756
|
+
console.log();
|
|
5757
|
+
}
|
|
5758
|
+
const totalChanges = Object.values(blameMap).reduce((sum, b) => sum + b.changes.length, 0);
|
|
5759
|
+
const aiChanges = changes.filter((c) => c.actor_type === "ai").length;
|
|
5760
|
+
const humanChanges = changes.filter((c) => c.actor_type === "human").length;
|
|
5761
|
+
console.log(chalk30.gray("\u2500".repeat(50)));
|
|
5762
|
+
console.log(chalk30.gray(`Total changes: ${totalChanges} | \u{1F916} AI: ${aiChanges} | \u{1F464} Human: ${humanChanges}`));
|
|
5763
|
+
console.log(chalk30.gray(`
|
|
5764
|
+
Full history: vibetasks log ${taskId2.slice(0, 8)}`));
|
|
5765
|
+
console.log();
|
|
5766
|
+
process.exit(0);
|
|
5767
|
+
} catch (error) {
|
|
5768
|
+
if (error.message.includes("Not authenticated")) {
|
|
5769
|
+
console.error(chalk30.yellow("\nYou need to login first"));
|
|
5770
|
+
console.error(chalk30.gray("Run: vibetasks login\n"));
|
|
5771
|
+
} else {
|
|
5772
|
+
console.error(chalk30.red(`
|
|
5773
|
+
Error: ${error.message}
|
|
5774
|
+
`));
|
|
5775
|
+
}
|
|
5776
|
+
process.exit(1);
|
|
5777
|
+
}
|
|
5778
|
+
});
|
|
5779
|
+
function formatValue3(value) {
|
|
5780
|
+
if (value === null || value === void 0) {
|
|
5781
|
+
return "(empty)";
|
|
5782
|
+
}
|
|
5783
|
+
if (typeof value === "object") {
|
|
5784
|
+
return JSON.stringify(value);
|
|
5785
|
+
}
|
|
5786
|
+
const str = String(value);
|
|
5787
|
+
return str.length > 60 ? str.slice(0, 57) + "..." : str;
|
|
5788
|
+
}
|
|
5789
|
+
|
|
5790
|
+
// src/commands/research.ts
|
|
5791
|
+
import { Command as Command30 } from "commander";
|
|
5792
|
+
import chalk31 from "chalk";
|
|
5793
|
+
import Table4 from "cli-table3";
|
|
5794
|
+
var researchCommand = new Command30("research").description("Manage research documents").action(() => {
|
|
5795
|
+
researchCommand.outputHelp();
|
|
5796
|
+
});
|
|
5797
|
+
researchCommand.command("list").alias("ls").description("List saved research documents").option("-p, --project <project>", "Filter by project").option("-s, --search <query>", "Search research docs").option("-l, --limit <number>", "Limit results", "20").action(async (options) => {
|
|
5798
|
+
try {
|
|
5799
|
+
const response = await fetch(`${process.env.VIBETASKS_API_URL || "http://localhost:3847"}/api/workspace-docs?` + new URLSearchParams({
|
|
5800
|
+
doc_type: "research",
|
|
5801
|
+
...options.project && { project: options.project },
|
|
5802
|
+
...options.search && { search: options.search },
|
|
5803
|
+
limit: options.limit
|
|
5804
|
+
}), {
|
|
5805
|
+
headers: {
|
|
5806
|
+
"Authorization": `Bearer ${process.env.VIBETASKS_AUTH_TOKEN}`
|
|
5807
|
+
}
|
|
5808
|
+
});
|
|
5809
|
+
if (!response.ok) {
|
|
5810
|
+
throw new Error("Failed to fetch research docs");
|
|
5811
|
+
}
|
|
5812
|
+
const data = await response.json();
|
|
5813
|
+
const docs = data.docs || [];
|
|
5814
|
+
if (docs.length === 0) {
|
|
5815
|
+
console.log(chalk31.yellow("\n\u{1F4DA} No research documents found.\n"));
|
|
5816
|
+
console.log(chalk31.dim("Use the MCP tool capture_research() to save research findings."));
|
|
5817
|
+
return;
|
|
5818
|
+
}
|
|
5819
|
+
console.log(chalk31.bold(`
|
|
5820
|
+
\u{1F4DA} Research Documents (${docs.length})
|
|
5821
|
+
`));
|
|
5822
|
+
const table = new Table4({
|
|
5823
|
+
head: [
|
|
5824
|
+
chalk31.cyan("#"),
|
|
5825
|
+
chalk31.cyan("Title"),
|
|
5826
|
+
chalk31.cyan("Project"),
|
|
5827
|
+
chalk31.cyan("Sources"),
|
|
5828
|
+
chalk31.cyan("Created")
|
|
5829
|
+
],
|
|
5830
|
+
colWidths: [5, 40, 20, 10, 12],
|
|
5831
|
+
wordWrap: true
|
|
5832
|
+
});
|
|
5833
|
+
docs.forEach((doc, idx) => {
|
|
5834
|
+
const date = new Date(doc.created_at).toLocaleDateString();
|
|
5835
|
+
const sourceCount = doc.source_urls?.length || 0;
|
|
5836
|
+
table.push([
|
|
5837
|
+
idx + 1,
|
|
5838
|
+
doc.title,
|
|
5839
|
+
doc.project_tag || "none",
|
|
5840
|
+
sourceCount > 0 ? chalk31.green(sourceCount) : chalk31.dim("-"),
|
|
5841
|
+
date
|
|
5842
|
+
]);
|
|
5843
|
+
});
|
|
5844
|
+
console.log(table.toString());
|
|
5845
|
+
console.log(chalk31.dim("\nUse: vibetasks research show <number> to view details\n"));
|
|
5846
|
+
} catch (error) {
|
|
5847
|
+
console.error(chalk31.red(`\u2717 Error: ${error.message}`));
|
|
5848
|
+
process.exit(1);
|
|
5849
|
+
}
|
|
5850
|
+
});
|
|
5851
|
+
researchCommand.command("show <id>").description("Show research document details").action(async (id) => {
|
|
5852
|
+
try {
|
|
5853
|
+
let docId = id;
|
|
5854
|
+
if (/^\d+$/.test(id)) {
|
|
5855
|
+
const response2 = await fetch(`${process.env.VIBETASKS_API_URL || "http://localhost:3847"}/api/workspace-docs?` + new URLSearchParams({
|
|
5856
|
+
doc_type: "research",
|
|
5857
|
+
limit: "100"
|
|
5858
|
+
}), {
|
|
5859
|
+
headers: {
|
|
5860
|
+
"Authorization": `Bearer ${process.env.VIBETASKS_AUTH_TOKEN}`
|
|
5861
|
+
}
|
|
5862
|
+
});
|
|
5863
|
+
const data2 = await response2.json();
|
|
5864
|
+
const docs = data2.docs || [];
|
|
5865
|
+
const idx = parseInt(id) - 1;
|
|
5866
|
+
if (idx < 0 || idx >= docs.length) {
|
|
5867
|
+
console.error(chalk31.red(`\u2717 Research doc #${id} not found`));
|
|
5868
|
+
process.exit(1);
|
|
5869
|
+
}
|
|
5870
|
+
docId = docs[idx].id;
|
|
5871
|
+
}
|
|
5872
|
+
const response = await fetch(`${process.env.VIBETASKS_API_URL || "http://localhost:3847"}/api/workspace-docs/${docId}`, {
|
|
5873
|
+
headers: {
|
|
5874
|
+
"Authorization": `Bearer ${process.env.VIBETASKS_AUTH_TOKEN}`
|
|
5875
|
+
}
|
|
5876
|
+
});
|
|
5877
|
+
if (!response.ok) {
|
|
5878
|
+
throw new Error("Failed to fetch research doc");
|
|
5879
|
+
}
|
|
5880
|
+
const data = await response.json();
|
|
5881
|
+
const doc = data.doc;
|
|
5882
|
+
console.log(chalk31.bold(`
|
|
5883
|
+
\u{1F4DA} ${doc.title}
|
|
5884
|
+
`));
|
|
5885
|
+
console.log(chalk31.dim("\u2500".repeat(80)));
|
|
5886
|
+
console.log(chalk31.cyan("Project:"), doc.project_tag || "none");
|
|
5887
|
+
console.log(chalk31.cyan("Created:"), new Date(doc.created_at).toLocaleString());
|
|
5888
|
+
console.log(chalk31.cyan("Created by:"), doc.created_by);
|
|
5889
|
+
if (doc.source_urls && doc.source_urls.length > 0) {
|
|
5890
|
+
console.log(chalk31.cyan("\nSources:"));
|
|
5891
|
+
doc.source_urls.forEach((url, idx) => {
|
|
5892
|
+
console.log(chalk31.dim(` ${idx + 1}. ${url}`));
|
|
5893
|
+
});
|
|
5894
|
+
}
|
|
5895
|
+
if (doc.search_queries && doc.search_queries.length > 0) {
|
|
5896
|
+
console.log(chalk31.cyan("\nSearch Queries:"));
|
|
5897
|
+
doc.search_queries.forEach((query) => {
|
|
5898
|
+
console.log(chalk31.dim(` \u2022 ${query}`));
|
|
5899
|
+
});
|
|
5900
|
+
}
|
|
5901
|
+
console.log(chalk31.cyan("\nContent:"));
|
|
5902
|
+
console.log(chalk31.dim("\u2500".repeat(80)));
|
|
5903
|
+
console.log(doc.content);
|
|
5904
|
+
console.log(chalk31.dim("\u2500".repeat(80) + "\n"));
|
|
5905
|
+
} catch (error) {
|
|
5906
|
+
console.error(chalk31.red(`\u2717 Error: ${error.message}`));
|
|
5907
|
+
process.exit(1);
|
|
5908
|
+
}
|
|
5909
|
+
});
|
|
5910
|
+
|
|
5911
|
+
// src/commands/bundle.ts
|
|
5912
|
+
import { Command as Command31 } from "commander";
|
|
5913
|
+
import chalk32 from "chalk";
|
|
5914
|
+
import ora19 from "ora";
|
|
5915
|
+
import {
|
|
5916
|
+
installBundle,
|
|
5917
|
+
removeBundle,
|
|
5918
|
+
updateBundle,
|
|
5919
|
+
activateBundle,
|
|
5920
|
+
deactivateBundle,
|
|
5921
|
+
loadInstalledBundles,
|
|
5922
|
+
loadBundleFromDirectory,
|
|
5923
|
+
getGlobalBundlesDir,
|
|
5924
|
+
getActiveBundle,
|
|
5925
|
+
getRequiredScaffoldVariables
|
|
5926
|
+
} from "@vibetasks/ralph-engine";
|
|
5927
|
+
import * as path5 from "path";
|
|
5928
|
+
import * as fs3 from "fs";
|
|
5929
|
+
var _inquirer = null;
|
|
5930
|
+
var _readline = null;
|
|
5931
|
+
async function getInquirer() {
|
|
5932
|
+
if (_inquirer === null) {
|
|
5933
|
+
try {
|
|
5934
|
+
_inquirer = (await import("inquirer")).default;
|
|
5935
|
+
} catch {
|
|
5936
|
+
_inquirer = false;
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
return _inquirer;
|
|
5940
|
+
}
|
|
5941
|
+
async function getReadline() {
|
|
5942
|
+
if (_readline === null) {
|
|
5943
|
+
_readline = await import("readline");
|
|
5944
|
+
}
|
|
5945
|
+
return _readline;
|
|
5946
|
+
}
|
|
5947
|
+
async function promptFn(message) {
|
|
5948
|
+
const inquirer9 = await getInquirer();
|
|
5949
|
+
if (inquirer9) {
|
|
5950
|
+
const answers = await inquirer9.prompt([{ type: "input", name: "value", message }]);
|
|
5951
|
+
return answers.value;
|
|
5952
|
+
}
|
|
5953
|
+
const readline2 = await getReadline();
|
|
5954
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5955
|
+
return new Promise((resolve) => rl.question(`${message} `, (answer) => {
|
|
5956
|
+
rl.close();
|
|
5957
|
+
resolve(answer);
|
|
5958
|
+
}));
|
|
5959
|
+
}
|
|
5960
|
+
async function confirmFn(message) {
|
|
5961
|
+
const inquirer9 = await getInquirer();
|
|
5962
|
+
if (inquirer9) {
|
|
5963
|
+
const answers = await inquirer9.prompt([{ type: "confirm", name: "confirm", message, default: true }]);
|
|
5964
|
+
return answers.confirm;
|
|
5965
|
+
}
|
|
5966
|
+
const answer = await promptFn(`${message} (y/n)`);
|
|
5967
|
+
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
5968
|
+
}
|
|
5969
|
+
var installCommand = new Command31("install").description("Install a bundle from GitHub or local path").argument("<source>", "Bundle source (github:owner/repo, owner/repo, or local path)").option("-f, --force", "Overwrite if already installed").option("-g, --global", "Install globally (default)", true).option("-p, --project", "Install to current project only").option("-a, --activate", "Activate bundle after installing").action(async (source, options) => {
|
|
5970
|
+
const spinner = ora19(`Installing bundle from ${source}...`).start();
|
|
5971
|
+
try {
|
|
5972
|
+
const target = options.project ? process.cwd() : "global";
|
|
5973
|
+
const result = await installBundle(source, {
|
|
5974
|
+
target,
|
|
5975
|
+
force: options.force,
|
|
5976
|
+
activate: options.activate
|
|
5977
|
+
});
|
|
5978
|
+
if (!result.success) {
|
|
5979
|
+
spinner.fail(chalk32.red(result.error || "Installation failed"));
|
|
5980
|
+
if (result.warnings?.length) {
|
|
5981
|
+
result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
|
|
5982
|
+
}
|
|
5983
|
+
process.exit(1);
|
|
5984
|
+
}
|
|
5985
|
+
spinner.succeed(chalk32.green(`Installed ${result.bundle.manifest.name} v${result.bundle.manifest.version}`));
|
|
5986
|
+
if (result.warnings?.length) {
|
|
5987
|
+
result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
|
|
5988
|
+
}
|
|
5989
|
+
console.log(chalk32.gray(`
|
|
5990
|
+
Path: ${result.installPath}`));
|
|
5991
|
+
console.log(chalk32.gray(` Blueprints: ${result.bundle.blueprints.length}`));
|
|
5992
|
+
console.log(chalk32.gray(` Has Studio config: ${result.bundle.studio ? "Yes" : "No"}`));
|
|
5993
|
+
console.log(chalk32.gray(` Has Scaffold: ${result.bundle.scaffold ? "Yes" : "No"}`));
|
|
5994
|
+
if (options.activate) {
|
|
5995
|
+
console.log(chalk32.green(`
|
|
5996
|
+
\u2713 Bundle activated on current project`));
|
|
5997
|
+
} else {
|
|
5998
|
+
console.log(chalk32.blue(`
|
|
5999
|
+
To use this bundle: vibetasks bundle use ${result.bundle.manifest.name}`));
|
|
6000
|
+
}
|
|
6001
|
+
process.exit(0);
|
|
6002
|
+
} catch (error) {
|
|
6003
|
+
spinner.fail(chalk32.red("Installation failed"));
|
|
6004
|
+
console.error(chalk32.red(`
|
|
6005
|
+
Error: ${error.message}
|
|
6006
|
+
`));
|
|
6007
|
+
process.exit(1);
|
|
6008
|
+
}
|
|
6009
|
+
});
|
|
6010
|
+
var useCommand = new Command31("use").description("Activate a bundle on current project").argument("<name>", "Bundle name").option("-s, --scaffold", "Also apply scaffold template").option("-r, --run-commands", "Run post-scaffold commands").action(async (name, options) => {
|
|
6011
|
+
const spinner = ora19(`Activating bundle ${name}...`).start();
|
|
6012
|
+
try {
|
|
6013
|
+
const projectPath = process.cwd();
|
|
6014
|
+
const bundlePath = path5.join(getGlobalBundlesDir(), name);
|
|
6015
|
+
if (!fs3.existsSync(bundlePath)) {
|
|
6016
|
+
spinner.fail(chalk32.red(`Bundle '${name}' not found`));
|
|
6017
|
+
console.log(chalk32.gray("\nRun `vibetasks bundle list` to see installed bundles"));
|
|
6018
|
+
process.exit(1);
|
|
6019
|
+
}
|
|
6020
|
+
const bundle = await loadBundleFromDirectory(bundlePath, { type: "local", path: bundlePath });
|
|
6021
|
+
let scaffoldVariables = {};
|
|
6022
|
+
if (options.scaffold && bundle.scaffold) {
|
|
6023
|
+
const requiredVars = getRequiredScaffoldVariables(bundle.scaffold);
|
|
6024
|
+
if (requiredVars.length > 0) {
|
|
6025
|
+
spinner.stop();
|
|
6026
|
+
console.log(chalk32.blue("\nScaffold requires the following values:\n"));
|
|
6027
|
+
for (const variable of requiredVars) {
|
|
6028
|
+
const value = await promptFn(`${variable.label}${variable.description ? ` (${variable.description})` : ""}: `);
|
|
6029
|
+
scaffoldVariables[variable.name] = value;
|
|
6030
|
+
}
|
|
6031
|
+
spinner.start("Activating bundle...");
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
const result = await activateBundle(name, {
|
|
6035
|
+
projectPath,
|
|
6036
|
+
applyScaffold: options.scaffold,
|
|
6037
|
+
scaffoldVariables,
|
|
6038
|
+
runPostScaffold: options.runCommands
|
|
6039
|
+
});
|
|
6040
|
+
if (!result.success) {
|
|
6041
|
+
spinner.fail(chalk32.red(result.error || "Activation failed"));
|
|
6042
|
+
if (result.warnings?.length) {
|
|
6043
|
+
result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
|
|
6044
|
+
}
|
|
6045
|
+
process.exit(1);
|
|
6046
|
+
}
|
|
6047
|
+
spinner.succeed(chalk32.green(`Activated ${name}`));
|
|
6048
|
+
if (result.files?.length) {
|
|
6049
|
+
console.log(chalk32.gray("\n Files:"));
|
|
6050
|
+
result.files.forEach((f) => {
|
|
6051
|
+
const icon = f.action === "created" ? "+" : f.action === "modified" ? "~" : "\u25CB";
|
|
6052
|
+
const color = f.action === "created" ? chalk32.green : f.action === "modified" ? chalk32.yellow : chalk32.gray;
|
|
6053
|
+
console.log(color(` ${icon} ${f.path}`));
|
|
6054
|
+
});
|
|
6055
|
+
}
|
|
6056
|
+
if (result.blueprintsLoaded) {
|
|
6057
|
+
console.log(chalk32.gray(`
|
|
6058
|
+
Blueprints loaded: ${result.blueprintsLoaded.blueprints.length}`));
|
|
6059
|
+
}
|
|
6060
|
+
if (result.commandResults?.length) {
|
|
6061
|
+
console.log(chalk32.gray("\n Post-scaffold commands:"));
|
|
6062
|
+
result.commandResults.forEach((r) => {
|
|
6063
|
+
const icon = r.success ? "\u2713" : "\u2717";
|
|
6064
|
+
const color = r.success ? chalk32.green : chalk32.red;
|
|
6065
|
+
console.log(color(` ${icon} ${r.command}`));
|
|
6066
|
+
});
|
|
6067
|
+
}
|
|
6068
|
+
if (result.warnings?.length) {
|
|
6069
|
+
console.log(chalk32.yellow("\n Warnings:"));
|
|
6070
|
+
result.warnings.forEach((w) => console.log(chalk32.yellow(` \u26A0 ${w}`)));
|
|
6071
|
+
}
|
|
6072
|
+
console.log(chalk32.blue("\n\u2713 Bundle is now active. VibBot will use its blueprints."));
|
|
6073
|
+
process.exit(0);
|
|
6074
|
+
} catch (error) {
|
|
6075
|
+
spinner.fail(chalk32.red("Activation failed"));
|
|
6076
|
+
console.error(chalk32.red(`
|
|
6077
|
+
Error: ${error.message}
|
|
6078
|
+
`));
|
|
6079
|
+
process.exit(1);
|
|
6080
|
+
}
|
|
6081
|
+
});
|
|
6082
|
+
var listCommand2 = new Command31("list").alias("ls").description("List installed bundles").option("-a, --active", "Show only active bundle for current project").action(async (options) => {
|
|
6083
|
+
try {
|
|
6084
|
+
if (options.active) {
|
|
6085
|
+
const activeBundle = await getActiveBundle(process.cwd());
|
|
6086
|
+
if (activeBundle) {
|
|
6087
|
+
console.log(chalk32.green(`
|
|
6088
|
+
Active bundle: ${activeBundle.manifest.name} v${activeBundle.manifest.version}`));
|
|
6089
|
+
console.log(chalk32.gray(` ${activeBundle.manifest.description}
|
|
6090
|
+
`));
|
|
6091
|
+
} else {
|
|
6092
|
+
console.log(chalk32.gray("\n No active bundle for current project\n"));
|
|
6093
|
+
}
|
|
6094
|
+
process.exit(0);
|
|
6095
|
+
}
|
|
6096
|
+
const { bundles, errors } = await loadInstalledBundles();
|
|
6097
|
+
if (bundles.length === 0) {
|
|
6098
|
+
console.log(chalk32.gray("\n No bundles installed\n"));
|
|
6099
|
+
console.log(chalk32.blue(" Install a bundle: vibetasks bundle install github:owner/repo\n"));
|
|
6100
|
+
process.exit(0);
|
|
6101
|
+
}
|
|
6102
|
+
console.log(chalk32.white(`
|
|
6103
|
+
Installed Bundles (${bundles.length}):
|
|
6104
|
+
`));
|
|
6105
|
+
for (const bundle of bundles) {
|
|
6106
|
+
console.log(chalk32.cyan(` \u2022 ${bundle.manifest.name}`), chalk32.gray(`v${bundle.manifest.version}`));
|
|
6107
|
+
console.log(chalk32.gray(` ${bundle.manifest.description}`));
|
|
6108
|
+
console.log(chalk32.gray(` Blueprints: ${bundle.blueprints.length} | Studio: ${bundle.studio ? "\u2713" : "\u2717"} | Scaffold: ${bundle.scaffold ? "\u2713" : "\u2717"}`));
|
|
6109
|
+
console.log();
|
|
6110
|
+
}
|
|
6111
|
+
if (errors.length > 0) {
|
|
6112
|
+
console.log(chalk32.yellow(" Errors loading some bundles:"));
|
|
6113
|
+
errors.forEach((e) => console.log(chalk32.yellow(` \u2022 ${e.message}`)));
|
|
6114
|
+
console.log();
|
|
6115
|
+
}
|
|
6116
|
+
process.exit(0);
|
|
6117
|
+
} catch (error) {
|
|
6118
|
+
console.error(chalk32.red(`
|
|
6119
|
+
Error: ${error.message}
|
|
6120
|
+
`));
|
|
6121
|
+
process.exit(1);
|
|
6122
|
+
}
|
|
6123
|
+
});
|
|
6124
|
+
var infoCommand = new Command31("info").description("Show detailed information about a bundle").argument("<name>", "Bundle name").action(async (name) => {
|
|
6125
|
+
try {
|
|
6126
|
+
const bundlePath = path5.join(getGlobalBundlesDir(), name);
|
|
6127
|
+
if (!fs3.existsSync(bundlePath)) {
|
|
6128
|
+
console.log(chalk32.red(`
|
|
6129
|
+
Bundle '${name}' not found
|
|
6130
|
+
`));
|
|
6131
|
+
process.exit(1);
|
|
6132
|
+
}
|
|
6133
|
+
const bundle = await loadBundleFromDirectory(bundlePath, { type: "local", path: bundlePath });
|
|
6134
|
+
console.log(chalk32.white(`
|
|
6135
|
+
${bundle.manifest.displayName || bundle.manifest.name}`));
|
|
6136
|
+
console.log(chalk32.gray(` v${bundle.manifest.version}`));
|
|
6137
|
+
console.log();
|
|
6138
|
+
console.log(chalk32.gray(` ${bundle.manifest.description}`));
|
|
6139
|
+
console.log();
|
|
6140
|
+
if (bundle.manifest.author) {
|
|
6141
|
+
const author = typeof bundle.manifest.author === "string" ? bundle.manifest.author : bundle.manifest.author.name;
|
|
6142
|
+
console.log(chalk32.gray(` Author: ${author}`));
|
|
6143
|
+
}
|
|
6144
|
+
if (bundle.manifest.domain) {
|
|
6145
|
+
console.log(chalk32.gray(` Domain: ${bundle.manifest.domain}`));
|
|
6146
|
+
}
|
|
6147
|
+
if (bundle.manifest.tags?.length) {
|
|
6148
|
+
console.log(chalk32.gray(` Tags: ${bundle.manifest.tags.join(", ")}`));
|
|
6149
|
+
}
|
|
6150
|
+
if (bundle.manifest.homepage) {
|
|
6151
|
+
console.log(chalk32.gray(` Homepage: ${bundle.manifest.homepage}`));
|
|
6152
|
+
}
|
|
6153
|
+
if (bundle.manifest.repository) {
|
|
6154
|
+
console.log(chalk32.gray(` Repository: ${bundle.manifest.repository}`));
|
|
6155
|
+
}
|
|
6156
|
+
console.log();
|
|
6157
|
+
console.log(chalk32.cyan(" Components:"));
|
|
6158
|
+
console.log(chalk32.gray(` \u2022 Blueprints: ${bundle.blueprints.length}`));
|
|
6159
|
+
if (bundle.blueprints.length > 0) {
|
|
6160
|
+
bundle.blueprints.slice(0, 5).forEach((b) => {
|
|
6161
|
+
console.log(chalk32.gray(` - ${b.frontmatter.name}: ${b.frontmatter.description}`));
|
|
6162
|
+
});
|
|
6163
|
+
if (bundle.blueprints.length > 5) {
|
|
6164
|
+
console.log(chalk32.gray(` ... and ${bundle.blueprints.length - 5} more`));
|
|
6165
|
+
}
|
|
6166
|
+
}
|
|
6167
|
+
console.log(chalk32.gray(` \u2022 Studio config: ${bundle.studio ? "Yes" : "No"}`));
|
|
6168
|
+
console.log(chalk32.gray(` \u2022 Scaffold: ${bundle.scaffold ? "Yes" : "No"}`));
|
|
6169
|
+
if (bundle.scaffold) {
|
|
6170
|
+
const requiredVars = getRequiredScaffoldVariables(bundle.scaffold);
|
|
6171
|
+
if (requiredVars.length > 0) {
|
|
6172
|
+
console.log(chalk32.gray(` \u2022 Required variables: ${requiredVars.map((v) => v.name).join(", ")}`));
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
console.log();
|
|
6176
|
+
console.log(chalk32.gray(` Path: ${bundlePath}`));
|
|
6177
|
+
console.log();
|
|
6178
|
+
process.exit(0);
|
|
6179
|
+
} catch (error) {
|
|
6180
|
+
console.error(chalk32.red(`
|
|
6181
|
+
Error: ${error.message}
|
|
6182
|
+
`));
|
|
6183
|
+
process.exit(1);
|
|
6184
|
+
}
|
|
6185
|
+
});
|
|
6186
|
+
var removeCommand = new Command31("remove").alias("rm").description("Remove an installed bundle").argument("<name>", "Bundle name").option("-f, --force", "Skip confirmation").action(async (name, options) => {
|
|
6187
|
+
try {
|
|
6188
|
+
if (!options.force) {
|
|
6189
|
+
const confirm = await confirmFn(`Remove bundle '${name}'?`);
|
|
6190
|
+
if (!confirm) {
|
|
6191
|
+
console.log(chalk32.gray("\n Cancelled\n"));
|
|
6192
|
+
process.exit(0);
|
|
6193
|
+
}
|
|
6194
|
+
}
|
|
6195
|
+
const spinner = ora19(`Removing bundle ${name}...`).start();
|
|
6196
|
+
const result = await removeBundle(name);
|
|
6197
|
+
if (!result.success) {
|
|
6198
|
+
spinner.fail(chalk32.red(result.error || "Remove failed"));
|
|
6199
|
+
process.exit(1);
|
|
6200
|
+
}
|
|
6201
|
+
spinner.succeed(chalk32.green(`Removed ${name}`));
|
|
6202
|
+
process.exit(0);
|
|
6203
|
+
} catch (error) {
|
|
6204
|
+
console.error(chalk32.red(`
|
|
6205
|
+
Error: ${error.message}
|
|
6206
|
+
`));
|
|
6207
|
+
process.exit(1);
|
|
6208
|
+
}
|
|
6209
|
+
});
|
|
6210
|
+
var updateCommand2 = new Command31("update").description("Update a bundle from its source").argument("<name>", "Bundle name").action(async (name) => {
|
|
6211
|
+
const spinner = ora19(`Updating bundle ${name}...`).start();
|
|
6212
|
+
try {
|
|
6213
|
+
const result = await updateBundle(name);
|
|
6214
|
+
if (!result.success) {
|
|
6215
|
+
spinner.fail(chalk32.red(result.error || "Update failed"));
|
|
6216
|
+
process.exit(1);
|
|
6217
|
+
}
|
|
6218
|
+
spinner.succeed(chalk32.green(`Updated ${result.bundle.manifest.name} to v${result.bundle.manifest.version}`));
|
|
6219
|
+
process.exit(0);
|
|
6220
|
+
} catch (error) {
|
|
6221
|
+
spinner.fail(chalk32.red("Update failed"));
|
|
6222
|
+
console.error(chalk32.red(`
|
|
6223
|
+
Error: ${error.message}
|
|
6224
|
+
`));
|
|
6225
|
+
process.exit(1);
|
|
6226
|
+
}
|
|
6227
|
+
});
|
|
6228
|
+
var deactivateCommand = new Command31("deactivate").description("Deactivate bundle from current project").argument("<name>", "Bundle name").action(async (name) => {
|
|
6229
|
+
const spinner = ora19(`Deactivating bundle ${name}...`).start();
|
|
6230
|
+
try {
|
|
6231
|
+
const result = await deactivateBundle(name, process.cwd());
|
|
6232
|
+
if (!result.success) {
|
|
6233
|
+
spinner.fail(chalk32.red(result.error || "Deactivation failed"));
|
|
6234
|
+
process.exit(1);
|
|
6235
|
+
}
|
|
6236
|
+
spinner.succeed(chalk32.green(`Deactivated ${name}`));
|
|
6237
|
+
process.exit(0);
|
|
6238
|
+
} catch (error) {
|
|
6239
|
+
spinner.fail(chalk32.red("Deactivation failed"));
|
|
6240
|
+
console.error(chalk32.red(`
|
|
6241
|
+
Error: ${error.message}
|
|
6242
|
+
`));
|
|
6243
|
+
process.exit(1);
|
|
6244
|
+
}
|
|
6245
|
+
});
|
|
6246
|
+
var bundleCommand = new Command31("bundle").description("Manage VibeTasks bundles (Studio + Scaffold + Blueprints)").addCommand(installCommand).addCommand(useCommand).addCommand(listCommand2).addCommand(infoCommand).addCommand(removeCommand).addCommand(updateCommand2).addCommand(deactivateCommand);
|
|
6247
|
+
|
|
6248
|
+
// src/commands/ready.ts
|
|
6249
|
+
import { Command as Command32 } from "commander";
|
|
6250
|
+
import chalk33 from "chalk";
|
|
6251
|
+
import Table5 from "cli-table3";
|
|
6252
|
+
import { AuthManager as AuthManager29, TaskOperations as TaskOperations25 } from "@vibetasks/core";
|
|
6253
|
+
var readyCommand = new Command32("ready").description("Show tasks ready to work on (no blockers, not deferred)").option("-l, --limit <number>", "Maximum number of tasks to show", "20").option("--status <status>", "Filter by status: todo, vibing (default: vibing)", "vibing").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").action(async (options) => {
|
|
6254
|
+
try {
|
|
6255
|
+
const authManager = new AuthManager29();
|
|
6256
|
+
const taskOps = await TaskOperations25.fromAuthManager(authManager);
|
|
6257
|
+
const tasks = await taskOps.getReadyTasks({
|
|
6258
|
+
status: options.status,
|
|
6259
|
+
limit: parseInt(options.limit, 10)
|
|
6260
|
+
});
|
|
6261
|
+
if (options.json) {
|
|
6262
|
+
console.log(JSON.stringify(tasks, null, 2));
|
|
6263
|
+
process.exit(0);
|
|
6264
|
+
}
|
|
6265
|
+
if (tasks.length === 0) {
|
|
6266
|
+
console.log(chalk33.green("\n\u2728 No ready tasks - all caught up!\n"));
|
|
6267
|
+
console.log(chalk33.gray("You might have:"));
|
|
6268
|
+
console.log(chalk33.gray(" - Tasks blocked by dependencies"));
|
|
6269
|
+
console.log(chalk33.gray(" - Tasks deferred for later (defer_until)"));
|
|
6270
|
+
console.log(chalk33.gray(" - No open tasks at all\n"));
|
|
6271
|
+
console.log(chalk33.gray("Run: vibetasks list --status todo to see all pending tasks"));
|
|
6272
|
+
console.log(chalk33.gray("Run: vibetasks blocked to see what's blocking you\n"));
|
|
6273
|
+
process.exit(0);
|
|
6274
|
+
}
|
|
6275
|
+
console.log(chalk33.green(`
|
|
6276
|
+
\u2705 ${tasks.length} task(s) ready to work on:
|
|
6277
|
+
`));
|
|
6278
|
+
const shortIdManager = new ShortIdManager();
|
|
6279
|
+
if (options.shortIds) {
|
|
6280
|
+
await shortIdManager.saveMappings(tasks.map((t) => ({
|
|
6281
|
+
id: t.id,
|
|
6282
|
+
short_id: t.short_id
|
|
6283
|
+
})));
|
|
6284
|
+
}
|
|
6285
|
+
const table = new Table5({
|
|
6286
|
+
head: options.shortIds ? [chalk33.cyan("#"), chalk33.cyan("Pri"), chalk33.cyan("Title"), chalk33.cyan("Due")] : [chalk33.cyan("ID"), chalk33.cyan("Pri"), chalk33.cyan("Title"), chalk33.cyan("Due")],
|
|
6287
|
+
colWidths: options.shortIds ? [8, 8, 45, 12] : [12, 8, 45, 12],
|
|
6288
|
+
wordWrap: true
|
|
6289
|
+
});
|
|
6290
|
+
const priorityColors = {
|
|
6291
|
+
high: chalk33.red,
|
|
6292
|
+
medium: chalk33.yellow,
|
|
6293
|
+
low: chalk33.blue,
|
|
6294
|
+
none: chalk33.gray
|
|
6295
|
+
};
|
|
6296
|
+
const priorityEmojis = {
|
|
6297
|
+
high: "\u{1F534}",
|
|
6298
|
+
medium: "\u{1F7E1}",
|
|
6299
|
+
low: "\u{1F535}",
|
|
6300
|
+
none: "\u26AA"
|
|
6301
|
+
};
|
|
6302
|
+
tasks.forEach((task, index) => {
|
|
6303
|
+
const priorityColor = priorityColors[task.priority || "none"];
|
|
6304
|
+
const priorityEmoji = priorityEmojis[task.priority || "none"];
|
|
6305
|
+
const shortId = chalk33.yellow.bold(task.short_id || `${index + 1}`);
|
|
6306
|
+
const id = task.id.substring(0, 8);
|
|
6307
|
+
const priority = priorityColor(`${priorityEmoji}`);
|
|
6308
|
+
const title = task.title.length > 42 ? task.title.substring(0, 39) + "..." : task.title;
|
|
6309
|
+
const dueDate = task.due_date ? task.due_date.split("T")[0] : chalk33.gray("-");
|
|
6310
|
+
if (options.shortIds) {
|
|
6311
|
+
table.push([shortId, priority, title, dueDate]);
|
|
6312
|
+
} else {
|
|
6313
|
+
table.push([id, priority, title, dueDate]);
|
|
6314
|
+
}
|
|
6315
|
+
});
|
|
6316
|
+
console.log(table.toString());
|
|
6317
|
+
if (options.shortIds) {
|
|
6318
|
+
console.log(chalk33.gray(`
|
|
6319
|
+
Use: vibetasks vibing <short-id> to start working`));
|
|
6320
|
+
} else {
|
|
6321
|
+
console.log(chalk33.gray(`
|
|
6322
|
+
Use: vibetasks vibing <id> to start working`));
|
|
6323
|
+
}
|
|
6324
|
+
console.log();
|
|
6325
|
+
process.exit(0);
|
|
6326
|
+
} catch (error) {
|
|
6327
|
+
if (error.message.includes("Not authenticated")) {
|
|
6328
|
+
console.error(chalk33.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
6329
|
+
console.error(chalk33.gray("Run: vibetasks login\n"));
|
|
6330
|
+
} else {
|
|
6331
|
+
console.error(chalk33.red(`
|
|
6332
|
+
Error: ${error.message}
|
|
6333
|
+
`));
|
|
6334
|
+
}
|
|
6335
|
+
process.exit(1);
|
|
6336
|
+
}
|
|
6337
|
+
});
|
|
6338
|
+
|
|
6339
|
+
// src/commands/stale.ts
|
|
6340
|
+
import { Command as Command33 } from "commander";
|
|
6341
|
+
import chalk34 from "chalk";
|
|
6342
|
+
import Table6 from "cli-table3";
|
|
6343
|
+
import { AuthManager as AuthManager30, TaskOperations as TaskOperations26 } from "@vibetasks/core";
|
|
6344
|
+
var staleCommand = new Command33("stale").description("Show stale tasks (not updated recently)").option("-d, --days <number>", "Tasks not updated in this many days", "7").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--status <status>", "Filter by status: todo, vibing").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").option("--fix", "Interactively close or update stale tasks").action(async (options) => {
|
|
6345
|
+
try {
|
|
6346
|
+
const authManager = new AuthManager30();
|
|
6347
|
+
const taskOps = await TaskOperations26.fromAuthManager(authManager);
|
|
6348
|
+
const days = parseInt(options.days, 10);
|
|
6349
|
+
const tasks = await taskOps.getStaleTasks({
|
|
6350
|
+
days,
|
|
6351
|
+
status: options.status,
|
|
6352
|
+
limit: parseInt(options.limit, 10)
|
|
6353
|
+
});
|
|
6354
|
+
if (options.json) {
|
|
6355
|
+
console.log(JSON.stringify(tasks, null, 2));
|
|
6356
|
+
process.exit(0);
|
|
6357
|
+
}
|
|
6358
|
+
if (tasks.length === 0) {
|
|
6359
|
+
console.log(chalk34.green(`
|
|
6360
|
+
\u2728 No stale tasks found (all updated within ${days} days)
|
|
6361
|
+
`));
|
|
6362
|
+
process.exit(0);
|
|
6363
|
+
}
|
|
6364
|
+
console.log(chalk34.yellow(`
|
|
6365
|
+
\u23F0 ${tasks.length} stale task(s) (not updated in ${days}+ days):
|
|
6366
|
+
`));
|
|
6367
|
+
const shortIdManager = new ShortIdManager();
|
|
6368
|
+
if (options.shortIds) {
|
|
6369
|
+
await shortIdManager.saveMappings(tasks.map((t) => ({
|
|
6370
|
+
id: t.id,
|
|
6371
|
+
short_id: t.short_id
|
|
6372
|
+
})));
|
|
6373
|
+
}
|
|
6374
|
+
const table = new Table6({
|
|
6375
|
+
head: [
|
|
6376
|
+
options.shortIds ? chalk34.cyan("#") : chalk34.cyan("ID"),
|
|
6377
|
+
chalk34.cyan("Status"),
|
|
6378
|
+
chalk34.cyan("Title"),
|
|
6379
|
+
chalk34.cyan("Days Stale"),
|
|
6380
|
+
chalk34.cyan("Session")
|
|
6381
|
+
],
|
|
6382
|
+
colWidths: options.shortIds ? [8, 10, 35, 12, 12] : [12, 10, 35, 12, 12],
|
|
6383
|
+
wordWrap: true
|
|
6384
|
+
});
|
|
6385
|
+
const statusColors = {
|
|
6386
|
+
todo: chalk34.gray,
|
|
6387
|
+
vibing: chalk34.magenta,
|
|
6388
|
+
paused: chalk34.yellow
|
|
6389
|
+
};
|
|
6390
|
+
tasks.forEach((task, index) => {
|
|
6391
|
+
const statusColor = statusColors[task.status] || chalk34.white;
|
|
6392
|
+
const daysStale = task.days_stale || Math.floor((Date.now() - new Date(task.updated_at).getTime()) / (1e3 * 60 * 60 * 24));
|
|
6393
|
+
const shortId = chalk34.yellow.bold(task.short_id || `${index + 1}`);
|
|
6394
|
+
const id = task.id.substring(0, 8);
|
|
6395
|
+
const status = statusColor(task.status);
|
|
6396
|
+
const title = task.title.length > 32 ? task.title.substring(0, 29) + "..." : task.title;
|
|
6397
|
+
const daysStaleStr = daysStale > 30 ? chalk34.red(`${daysStale}d`) : chalk34.yellow(`${daysStale}d`);
|
|
6398
|
+
const sessionId = task.session_id ? task.session_id.substring(0, 8) : chalk34.gray("none");
|
|
6399
|
+
if (options.shortIds) {
|
|
6400
|
+
table.push([shortId, status, title, daysStaleStr, sessionId]);
|
|
6401
|
+
} else {
|
|
6402
|
+
table.push([id, status, title, daysStaleStr, sessionId]);
|
|
6403
|
+
}
|
|
6404
|
+
});
|
|
6405
|
+
console.log(table.toString());
|
|
6406
|
+
console.log(chalk34.gray("\nThese tasks may be:"));
|
|
6407
|
+
console.log(chalk34.gray(" - Abandoned work from a previous session"));
|
|
6408
|
+
console.log(chalk34.gray(" - Completed but not marked done"));
|
|
6409
|
+
console.log(chalk34.gray(" - Blocked and forgotten"));
|
|
6410
|
+
console.log();
|
|
6411
|
+
if (!options.fix) {
|
|
6412
|
+
console.log(chalk34.gray("Run: vibetasks stale --fix to interactively clean up"));
|
|
6413
|
+
console.log(chalk34.gray("Run: vibetasks done <id> to mark a task complete"));
|
|
6414
|
+
console.log(chalk34.gray("Run: vibetasks archive <id> to archive a task"));
|
|
6415
|
+
} else {
|
|
6416
|
+
console.log(chalk34.cyan("\n--- Interactive Cleanup Mode ---\n"));
|
|
6417
|
+
const readline2 = await import("readline");
|
|
6418
|
+
const rl = readline2.createInterface({
|
|
6419
|
+
input: process.stdin,
|
|
6420
|
+
output: process.stdout
|
|
6421
|
+
});
|
|
6422
|
+
for (const task of tasks) {
|
|
6423
|
+
const answer = await new Promise((resolve) => {
|
|
6424
|
+
rl.question(
|
|
6425
|
+
chalk34.white(`
|
|
6426
|
+
"${task.title}" (${task.status}, ${task.days_stale || "?"}d stale)
|
|
6427
|
+
`) + chalk34.gray(" [d]one, [a]rchive, [s]kip, [q]uit: "),
|
|
6428
|
+
resolve
|
|
6429
|
+
);
|
|
6430
|
+
});
|
|
6431
|
+
const choice = answer.toLowerCase().trim();
|
|
6432
|
+
if (choice === "q" || choice === "quit") {
|
|
6433
|
+
console.log(chalk34.gray("\nExiting cleanup mode."));
|
|
6434
|
+
break;
|
|
6435
|
+
} else if (choice === "d" || choice === "done") {
|
|
6436
|
+
await taskOps.completeTask(task.id);
|
|
6437
|
+
console.log(chalk34.green(` \u2713 Marked done: ${task.title}`));
|
|
6438
|
+
} else if (choice === "a" || choice === "archive") {
|
|
6439
|
+
await taskOps.archiveTask(task.id);
|
|
6440
|
+
console.log(chalk34.blue(` \u{1F4E6} Archived: ${task.title}`));
|
|
6441
|
+
} else {
|
|
6442
|
+
console.log(chalk34.gray(" Skipped"));
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
rl.close();
|
|
6446
|
+
console.log(chalk34.green("\n\u2705 Cleanup complete!\n"));
|
|
6447
|
+
}
|
|
6448
|
+
console.log();
|
|
6449
|
+
process.exit(0);
|
|
6450
|
+
} catch (error) {
|
|
6451
|
+
if (error.message.includes("Not authenticated")) {
|
|
6452
|
+
console.error(chalk34.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
6453
|
+
console.error(chalk34.gray("Run: vibetasks login\n"));
|
|
6454
|
+
} else {
|
|
6455
|
+
console.error(chalk34.red(`
|
|
6456
|
+
Error: ${error.message}
|
|
6457
|
+
`));
|
|
6458
|
+
}
|
|
6459
|
+
process.exit(1);
|
|
6460
|
+
}
|
|
6461
|
+
});
|
|
6462
|
+
|
|
6463
|
+
// src/commands/orphans.ts
|
|
6464
|
+
import { Command as Command34 } from "commander";
|
|
6465
|
+
import chalk35 from "chalk";
|
|
6466
|
+
import Table7 from "cli-table3";
|
|
6467
|
+
import { AuthManager as AuthManager31, TaskOperations as TaskOperations27 } from "@vibetasks/core";
|
|
6468
|
+
var orphansCommand = new Command34("orphans").description("Find orphaned tasks (incomplete tasks from old AI sessions)").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").option("--fix", "Interactively close or reassign orphaned tasks").option("--adopt", "Adopt orphaned tasks into current session").action(async (options) => {
|
|
6469
|
+
try {
|
|
6470
|
+
const authManager = new AuthManager31();
|
|
6471
|
+
const taskOps = await TaskOperations27.fromAuthManager(authManager);
|
|
6472
|
+
const sessionManager2 = getSessionManager();
|
|
6473
|
+
const currentSession = await sessionManager2.getCurrentSession();
|
|
6474
|
+
const currentSessionId = currentSession?.id;
|
|
6475
|
+
const tasks = await taskOps.getOrphanedTasks({
|
|
6476
|
+
currentSessionId,
|
|
6477
|
+
limit: parseInt(options.limit, 10)
|
|
6478
|
+
});
|
|
6479
|
+
if (options.json) {
|
|
6480
|
+
console.log(JSON.stringify(tasks, null, 2));
|
|
6481
|
+
process.exit(0);
|
|
6482
|
+
}
|
|
6483
|
+
if (tasks.length === 0) {
|
|
6484
|
+
console.log(chalk35.green("\n\u2728 No orphaned tasks found!\n"));
|
|
6485
|
+
console.log(chalk35.gray("All in-progress tasks are from the current session."));
|
|
6486
|
+
process.exit(0);
|
|
6487
|
+
}
|
|
6488
|
+
console.log(chalk35.yellow(`
|
|
6489
|
+
\u{1F50D} Found ${tasks.length} orphaned task(s) from previous sessions:
|
|
6490
|
+
`));
|
|
6491
|
+
const shortIdManager = new ShortIdManager();
|
|
6492
|
+
if (options.shortIds) {
|
|
6493
|
+
await shortIdManager.saveMappings(tasks.map((t) => ({
|
|
6494
|
+
id: t.id,
|
|
6495
|
+
short_id: t.short_id
|
|
6496
|
+
})));
|
|
6497
|
+
}
|
|
6498
|
+
const table = new Table7({
|
|
6499
|
+
head: [
|
|
6500
|
+
options.shortIds ? chalk35.cyan("#") : chalk35.cyan("ID"),
|
|
6501
|
+
chalk35.cyan("Status"),
|
|
6502
|
+
chalk35.cyan("Title"),
|
|
6503
|
+
chalk35.cyan("Progress"),
|
|
6504
|
+
chalk35.cyan("Session")
|
|
6505
|
+
],
|
|
6506
|
+
colWidths: options.shortIds ? [8, 10, 35, 12, 12] : [12, 10, 35, 12, 12],
|
|
6507
|
+
wordWrap: true
|
|
6508
|
+
});
|
|
6509
|
+
const statusColors = {
|
|
6510
|
+
todo: chalk35.gray,
|
|
6511
|
+
vibing: chalk35.magenta,
|
|
6512
|
+
paused: chalk35.yellow
|
|
6513
|
+
};
|
|
6514
|
+
tasks.forEach((task, index) => {
|
|
6515
|
+
const statusColor = statusColors[task.status] || chalk35.white;
|
|
6516
|
+
const shortId = chalk35.yellow.bold(task.short_id || `${index + 1}`);
|
|
6517
|
+
const id = task.id.substring(0, 8);
|
|
6518
|
+
const status = statusColor(task.status);
|
|
6519
|
+
const title = task.title.length > 32 ? task.title.substring(0, 29) + "..." : task.title;
|
|
6520
|
+
const subtasksTotal = task.subtasks_total || 0;
|
|
6521
|
+
const subtasksDone = task.subtasks_done || 0;
|
|
6522
|
+
const progress = subtasksTotal > 0 ? subtasksDone === subtasksTotal ? chalk35.green(`${subtasksDone}/${subtasksTotal}`) : chalk35.yellow(`${subtasksDone}/${subtasksTotal}`) : chalk35.gray("0/0");
|
|
6523
|
+
const sessionId = task.session_id ? task.session_id === currentSessionId ? chalk35.green("current") : task.session_id.substring(0, 8) : chalk35.gray("none");
|
|
6524
|
+
if (options.shortIds) {
|
|
6525
|
+
table.push([shortId, status, title, progress, sessionId]);
|
|
6526
|
+
} else {
|
|
6527
|
+
table.push([id, status, title, progress, sessionId]);
|
|
6528
|
+
}
|
|
6529
|
+
});
|
|
6530
|
+
console.log(table.toString());
|
|
6531
|
+
const tasksWithContext = tasks.filter((t) => t.context_notes);
|
|
6532
|
+
if (tasksWithContext.length > 0) {
|
|
6533
|
+
console.log(chalk35.cyan("\n\u{1F4DD} Tasks with handoff context:\n"));
|
|
6534
|
+
tasksWithContext.slice(0, 3).forEach((task) => {
|
|
6535
|
+
console.log(chalk35.white(` ${task.title}:`));
|
|
6536
|
+
const preview = task.context_notes.length > 100 ? task.context_notes.substring(0, 100) + "..." : task.context_notes;
|
|
6537
|
+
console.log(chalk35.gray(` ${preview}
|
|
6538
|
+
`));
|
|
6539
|
+
});
|
|
6540
|
+
}
|
|
6541
|
+
console.log(chalk35.gray("\nThese tasks were started by a previous AI session but not completed."));
|
|
6542
|
+
console.log(chalk35.gray("They may represent:"));
|
|
6543
|
+
console.log(chalk35.gray(" - Work interrupted by session timeout/crash"));
|
|
6544
|
+
console.log(chalk35.gray(" - Tasks the AI forgot to complete"));
|
|
6545
|
+
console.log(chalk35.gray(" - Partially done work that needs finishing"));
|
|
6546
|
+
console.log();
|
|
6547
|
+
if (options.adopt && currentSessionId) {
|
|
6548
|
+
console.log(chalk35.cyan("\u{1F504} Adopting orphaned tasks into current session...\n"));
|
|
6549
|
+
for (const task of tasks) {
|
|
6550
|
+
await taskOps.updateTask(task.id, { session_id: currentSessionId });
|
|
6551
|
+
console.log(chalk35.green(` \u2713 Adopted: ${task.title}`));
|
|
6552
|
+
}
|
|
6553
|
+
console.log(chalk35.green(`
|
|
6554
|
+
\u2705 Adopted ${tasks.length} task(s) into current session.
|
|
6555
|
+
`));
|
|
6556
|
+
} else if (options.fix) {
|
|
6557
|
+
console.log(chalk35.cyan("\n--- Interactive Cleanup Mode ---\n"));
|
|
6558
|
+
const readline2 = await import("readline");
|
|
6559
|
+
const rl = readline2.createInterface({
|
|
6560
|
+
input: process.stdin,
|
|
6561
|
+
output: process.stdout
|
|
6562
|
+
});
|
|
6563
|
+
for (const task of tasks) {
|
|
6564
|
+
const progressStr = `${task.subtasks_done || 0}/${task.subtasks_total || 0}`;
|
|
6565
|
+
const answer = await new Promise((resolve) => {
|
|
6566
|
+
rl.question(
|
|
6567
|
+
chalk35.white(`
|
|
6568
|
+
"${task.title}" (${task.status}, ${progressStr} subtasks)
|
|
6569
|
+
`) + (task.context_notes ? chalk35.gray(` Context: ${task.context_notes.substring(0, 80)}...
|
|
6570
|
+
`) : "") + chalk35.gray(" [a]dopt to current session, [d]one, [r]emove, [s]kip, [q]uit: "),
|
|
6571
|
+
resolve
|
|
6572
|
+
);
|
|
6573
|
+
});
|
|
6574
|
+
const choice = answer.toLowerCase().trim();
|
|
6575
|
+
if (choice === "q" || choice === "quit") {
|
|
6576
|
+
console.log(chalk35.gray("\nExiting cleanup mode."));
|
|
6577
|
+
break;
|
|
6578
|
+
} else if (choice === "a" || choice === "adopt") {
|
|
6579
|
+
if (currentSessionId) {
|
|
6580
|
+
await taskOps.updateTask(task.id, { session_id: currentSessionId });
|
|
6581
|
+
console.log(chalk35.green(` \u2713 Adopted: ${task.title}`));
|
|
6582
|
+
} else {
|
|
6583
|
+
console.log(chalk35.yellow(" No current session. Start one with: vibetasks session start"));
|
|
6584
|
+
}
|
|
6585
|
+
} else if (choice === "d" || choice === "done") {
|
|
6586
|
+
await taskOps.completeTask(task.id);
|
|
6587
|
+
console.log(chalk35.green(` \u2713 Marked done: ${task.title}`));
|
|
6588
|
+
} else if (choice === "r" || choice === "remove") {
|
|
6589
|
+
await taskOps.archiveTask(task.id);
|
|
6590
|
+
console.log(chalk35.blue(` \u{1F4E6} Archived: ${task.title}`));
|
|
6591
|
+
} else {
|
|
6592
|
+
console.log(chalk35.gray(" Skipped"));
|
|
6593
|
+
}
|
|
6594
|
+
}
|
|
6595
|
+
rl.close();
|
|
6596
|
+
console.log(chalk35.green("\n\u2705 Cleanup complete!\n"));
|
|
6597
|
+
} else {
|
|
6598
|
+
console.log(chalk35.gray("Run: vibetasks orphans --fix to interactively clean up"));
|
|
6599
|
+
console.log(chalk35.gray("Run: vibetasks orphans --adopt to adopt all into current session"));
|
|
6600
|
+
console.log(chalk35.gray("Run: vibetasks show <id> to see full task details"));
|
|
6601
|
+
console.log();
|
|
6602
|
+
}
|
|
6603
|
+
process.exit(0);
|
|
6604
|
+
} catch (error) {
|
|
6605
|
+
if (error.message.includes("Not authenticated")) {
|
|
6606
|
+
console.error(chalk35.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
6607
|
+
console.error(chalk35.gray("Run: vibetasks login\n"));
|
|
6608
|
+
} else {
|
|
6609
|
+
console.error(chalk35.red(`
|
|
6610
|
+
Error: ${error.message}
|
|
6611
|
+
`));
|
|
6612
|
+
}
|
|
6613
|
+
process.exit(1);
|
|
6614
|
+
}
|
|
6615
|
+
});
|
|
6616
|
+
|
|
6617
|
+
// src/commands/blocked.ts
|
|
6618
|
+
import { Command as Command35 } from "commander";
|
|
6619
|
+
import chalk36 from "chalk";
|
|
6620
|
+
import Table8 from "cli-table3";
|
|
6621
|
+
import { AuthManager as AuthManager32, TaskOperations as TaskOperations28 } from "@vibetasks/core";
|
|
6622
|
+
var blockedCommand = new Command35("blocked").description("Show tasks blocked by dependencies").option("-l, --limit <number>", "Maximum number of tasks to show", "50").option("--short-ids", "Show short numeric IDs for easy reference").option("--json", "Output as JSON").action(async (options) => {
|
|
6623
|
+
try {
|
|
6624
|
+
const authManager = new AuthManager32();
|
|
6625
|
+
const taskOps = await TaskOperations28.fromAuthManager(authManager);
|
|
6626
|
+
const tasks = await taskOps.getBlockedTasks({
|
|
6627
|
+
limit: parseInt(options.limit, 10)
|
|
6628
|
+
});
|
|
6629
|
+
if (options.json) {
|
|
6630
|
+
console.log(JSON.stringify(tasks, null, 2));
|
|
6631
|
+
process.exit(0);
|
|
6632
|
+
}
|
|
6633
|
+
if (tasks.length === 0) {
|
|
6634
|
+
console.log(chalk36.green("\n\u2728 No blocked tasks - nothing is waiting!\n"));
|
|
6635
|
+
process.exit(0);
|
|
6636
|
+
}
|
|
6637
|
+
console.log(chalk36.yellow(`
|
|
6638
|
+
\u{1F6A7} ${tasks.length} blocked task(s):
|
|
6639
|
+
`));
|
|
6640
|
+
const shortIdManager = new ShortIdManager();
|
|
6641
|
+
if (options.shortIds) {
|
|
6642
|
+
await shortIdManager.saveMappings(tasks.map((t) => ({
|
|
6643
|
+
id: t.id,
|
|
6644
|
+
short_id: t.short_id
|
|
6645
|
+
})));
|
|
6646
|
+
}
|
|
6647
|
+
const table = new Table8({
|
|
6648
|
+
head: [
|
|
6649
|
+
options.shortIds ? chalk36.cyan("#") : chalk36.cyan("ID"),
|
|
6650
|
+
chalk36.cyan("Pri"),
|
|
6651
|
+
chalk36.cyan("Title"),
|
|
6652
|
+
chalk36.cyan("Blocked By")
|
|
6653
|
+
],
|
|
6654
|
+
colWidths: options.shortIds ? [8, 6, 35, 30] : [12, 6, 35, 30],
|
|
6655
|
+
wordWrap: true
|
|
6656
|
+
});
|
|
6657
|
+
const priorityColors = {
|
|
6658
|
+
high: chalk36.red,
|
|
6659
|
+
medium: chalk36.yellow,
|
|
6660
|
+
low: chalk36.blue,
|
|
6661
|
+
none: chalk36.gray
|
|
6662
|
+
};
|
|
6663
|
+
tasks.forEach((task, index) => {
|
|
6664
|
+
const priorityKey = task.priority || "none";
|
|
6665
|
+
const priorityColor = priorityColors[priorityKey];
|
|
6666
|
+
const shortId = chalk36.yellow.bold(task.short_id || `${index + 1}`);
|
|
6667
|
+
const id = task.id.substring(0, 8);
|
|
6668
|
+
const priority = priorityColor(task.priority?.[0]?.toUpperCase() || "-");
|
|
6669
|
+
const title = task.title.length > 32 ? task.title.substring(0, 29) + "..." : task.title;
|
|
6670
|
+
const blockerCount = task.blocked_by_count || 0;
|
|
6671
|
+
const blockerTitles = (task.blocker_titles || []).slice(0, 2);
|
|
6672
|
+
let blockedBy = "";
|
|
6673
|
+
if (blockerCount === 0) {
|
|
6674
|
+
blockedBy = chalk36.gray("deferred");
|
|
6675
|
+
} else if (blockerTitles.length > 0) {
|
|
6676
|
+
blockedBy = blockerTitles.map(
|
|
6677
|
+
(t) => chalk36.red(t.length > 12 ? t.substring(0, 9) + "..." : t)
|
|
6678
|
+
).join(", ");
|
|
6679
|
+
if (blockerCount > blockerTitles.length) {
|
|
6680
|
+
blockedBy += chalk36.gray(` +${blockerCount - blockerTitles.length} more`);
|
|
6681
|
+
}
|
|
6682
|
+
} else {
|
|
6683
|
+
blockedBy = chalk36.red(`${blockerCount} task(s)`);
|
|
6684
|
+
}
|
|
6685
|
+
if (options.shortIds) {
|
|
6686
|
+
table.push([shortId, priority, title, blockedBy]);
|
|
6687
|
+
} else {
|
|
6688
|
+
table.push([id, priority, title, blockedBy]);
|
|
6689
|
+
}
|
|
6690
|
+
});
|
|
6691
|
+
console.log(table.toString());
|
|
6692
|
+
console.log(chalk36.gray("\nTo unblock tasks:"));
|
|
6693
|
+
console.log(chalk36.gray(" 1. Complete the blocking tasks first"));
|
|
6694
|
+
console.log(chalk36.gray(" 2. Or remove the dependency with: vibetasks dep remove <task-id> <blocker-id>"));
|
|
6695
|
+
console.log();
|
|
6696
|
+
console.log(chalk36.gray("Run: vibetasks ready to see tasks you can work on now"));
|
|
6697
|
+
console.log();
|
|
6698
|
+
process.exit(0);
|
|
6699
|
+
} catch (error) {
|
|
6700
|
+
if (error.message.includes("Not authenticated")) {
|
|
6701
|
+
console.error(chalk36.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
6702
|
+
console.error(chalk36.gray("Run: vibetasks login\n"));
|
|
6703
|
+
} else {
|
|
6704
|
+
console.error(chalk36.red(`
|
|
6705
|
+
Error: ${error.message}
|
|
6706
|
+
`));
|
|
6707
|
+
}
|
|
6708
|
+
process.exit(1);
|
|
6709
|
+
}
|
|
6710
|
+
});
|
|
6711
|
+
|
|
6712
|
+
// src/commands/compact.ts
|
|
6713
|
+
import { Command as Command36 } from "commander";
|
|
6714
|
+
import chalk37 from "chalk";
|
|
6715
|
+
import ora20 from "ora";
|
|
6716
|
+
import { AuthManager as AuthManager33, TaskOperations as TaskOperations29, createCompactionService } from "@vibetasks/core";
|
|
6717
|
+
var compactCommand = new Command36("compact").description("Compact old completed tasks using AI summarization").option("-d, --days <number>", "Minimum days since completion (default: 7)", "7").option("-l, --limit <number>", "Maximum tasks to compact in one run", "10").option("--dry-run", "Preview what would be compacted without making changes").option("--stats", "Show compaction statistics only").option("--json", "Output as JSON").action(async (options) => {
|
|
6718
|
+
try {
|
|
6719
|
+
const authManager = new AuthManager33();
|
|
6720
|
+
const taskOps = await TaskOperations29.fromAuthManager(authManager);
|
|
6721
|
+
if (options.stats) {
|
|
6722
|
+
const stats = await taskOps.getCompactionStats();
|
|
6723
|
+
if (options.json) {
|
|
6724
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
6725
|
+
process.exit(0);
|
|
6726
|
+
}
|
|
6727
|
+
console.log(chalk37.cyan("\n\u{1F4CA} Compaction Statistics\n"));
|
|
6728
|
+
console.log(` Total completed tasks: ${chalk37.white(stats.totalCompleted)}`);
|
|
6729
|
+
console.log(` Already compacted: ${chalk37.green(stats.compacted)}`);
|
|
6730
|
+
console.log(` Eligible for compact: ${chalk37.yellow(stats.eligible)}`);
|
|
6731
|
+
console.log();
|
|
6732
|
+
if (stats.eligible > 0) {
|
|
6733
|
+
console.log(chalk37.gray(`Run: vibetasks compact to summarize ${stats.eligible} old task(s)`));
|
|
6734
|
+
} else {
|
|
6735
|
+
console.log(chalk37.green("\u2728 All eligible tasks are already compacted!"));
|
|
6736
|
+
}
|
|
6737
|
+
console.log();
|
|
6738
|
+
process.exit(0);
|
|
6739
|
+
}
|
|
6740
|
+
const minDays = parseInt(options.days, 10);
|
|
6741
|
+
const limit = parseInt(options.limit, 10);
|
|
6742
|
+
const tasks = await taskOps.getCompactableTasks({
|
|
6743
|
+
minDaysOld: minDays,
|
|
6744
|
+
limit
|
|
6745
|
+
});
|
|
6746
|
+
if (tasks.length === 0) {
|
|
6747
|
+
console.log(chalk37.green("\n\u2728 No tasks eligible for compaction\n"));
|
|
6748
|
+
console.log(chalk37.gray(`Tasks must be completed more than ${minDays} days ago to be compacted.`));
|
|
6749
|
+
console.log(chalk37.gray("Run: vibetasks compact --stats to see compaction statistics"));
|
|
6750
|
+
console.log();
|
|
6751
|
+
process.exit(0);
|
|
6752
|
+
}
|
|
6753
|
+
if (options.dryRun) {
|
|
6754
|
+
console.log(chalk37.yellow(`
|
|
6755
|
+
\u{1F50D} Dry run: ${tasks.length} task(s) would be compacted:
|
|
6756
|
+
`));
|
|
6757
|
+
tasks.forEach((task, i) => {
|
|
6758
|
+
const completedDate = task.completed_at ? new Date(task.completed_at).toLocaleDateString() : "unknown";
|
|
6759
|
+
console.log(` ${i + 1}. ${task.title}`);
|
|
6760
|
+
console.log(chalk37.gray(` Completed: ${completedDate}`));
|
|
6761
|
+
});
|
|
6762
|
+
console.log();
|
|
6763
|
+
console.log(chalk37.gray("Remove --dry-run to actually compact these tasks"));
|
|
6764
|
+
console.log();
|
|
6765
|
+
process.exit(0);
|
|
6766
|
+
}
|
|
6767
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
6768
|
+
console.error(chalk37.red("\n\u274C ANTHROPIC_API_KEY environment variable required for compaction"));
|
|
6769
|
+
console.error(chalk37.gray("Set it with: export ANTHROPIC_API_KEY=your-key"));
|
|
6770
|
+
process.exit(1);
|
|
6771
|
+
}
|
|
6772
|
+
console.log(chalk37.cyan(`
|
|
6773
|
+
\u{1F5DC}\uFE0F Compacting ${tasks.length} old task(s)...
|
|
6774
|
+
`));
|
|
6775
|
+
const compactionService = createCompactionService();
|
|
6776
|
+
const results = [];
|
|
6777
|
+
let successCount = 0;
|
|
6778
|
+
let failCount = 0;
|
|
6779
|
+
let totalSaved = 0;
|
|
6780
|
+
for (const task of tasks) {
|
|
6781
|
+
const spinner = ora20(`Compacting: ${task.title.substring(0, 40)}...`).start();
|
|
6782
|
+
try {
|
|
6783
|
+
const result = await compactionService.compactTask(task);
|
|
6784
|
+
if (result.success) {
|
|
6785
|
+
await taskOps.applyCompaction(task.id, result.summary);
|
|
6786
|
+
const saved = result.originalSize - result.compactedSize;
|
|
6787
|
+
totalSaved += saved;
|
|
6788
|
+
successCount++;
|
|
6789
|
+
spinner.succeed(
|
|
6790
|
+
`${task.title.substring(0, 30)}... ` + chalk37.green(`(${result.originalSize} \u2192 ${result.compactedSize} chars, saved ${saved})`)
|
|
6791
|
+
);
|
|
6792
|
+
results.push({
|
|
6793
|
+
taskId: task.id,
|
|
6794
|
+
title: task.title,
|
|
6795
|
+
...result
|
|
6796
|
+
});
|
|
6797
|
+
} else {
|
|
6798
|
+
failCount++;
|
|
6799
|
+
spinner.fail(`${task.title.substring(0, 30)}... ${chalk37.red(result.error)}`);
|
|
6800
|
+
results.push({
|
|
6801
|
+
taskId: task.id,
|
|
6802
|
+
title: task.title,
|
|
6803
|
+
...result
|
|
6804
|
+
});
|
|
6805
|
+
}
|
|
6806
|
+
} catch (error) {
|
|
6807
|
+
failCount++;
|
|
6808
|
+
spinner.fail(`${task.title.substring(0, 30)}... ${chalk37.red(error.message)}`);
|
|
6809
|
+
results.push({
|
|
6810
|
+
taskId: task.id,
|
|
6811
|
+
title: task.title,
|
|
6812
|
+
success: false,
|
|
6813
|
+
error: error.message
|
|
6814
|
+
});
|
|
6815
|
+
}
|
|
6816
|
+
}
|
|
6817
|
+
if (options.json) {
|
|
6818
|
+
console.log(JSON.stringify(results, null, 2));
|
|
6819
|
+
process.exit(0);
|
|
6820
|
+
}
|
|
6821
|
+
console.log();
|
|
6822
|
+
console.log(chalk37.cyan("\u{1F4CA} Summary:"));
|
|
6823
|
+
console.log(` Compacted: ${chalk37.green(successCount)} task(s)`);
|
|
6824
|
+
if (failCount > 0) {
|
|
6825
|
+
console.log(` Failed: ${chalk37.red(failCount)} task(s)`);
|
|
6826
|
+
}
|
|
6827
|
+
console.log(` Saved: ${chalk37.yellow(totalSaved)} characters`);
|
|
6828
|
+
console.log();
|
|
6829
|
+
if (successCount > 0) {
|
|
6830
|
+
console.log(chalk37.green("\u2705 Context compaction complete!"));
|
|
6831
|
+
console.log(chalk37.gray("Compacted summaries are stored in context_summary field."));
|
|
6832
|
+
}
|
|
6833
|
+
console.log();
|
|
6834
|
+
process.exit(failCount > 0 ? 1 : 0);
|
|
6835
|
+
} catch (error) {
|
|
6836
|
+
if (error.message.includes("Not authenticated")) {
|
|
6837
|
+
console.error(chalk37.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
6838
|
+
console.error(chalk37.gray("Run: vibetasks login\n"));
|
|
6839
|
+
} else {
|
|
6840
|
+
console.error(chalk37.red(`
|
|
6841
|
+
Error: ${error.message}
|
|
6842
|
+
`));
|
|
6843
|
+
}
|
|
6844
|
+
process.exit(1);
|
|
6845
|
+
}
|
|
6846
|
+
});
|
|
6847
|
+
|
|
3837
6848
|
// bin/vibetasks.ts
|
|
3838
6849
|
var require2 = createRequire(import.meta.url);
|
|
3839
6850
|
var pkg = require2("../../package.json");
|
|
3840
|
-
var program = new
|
|
6851
|
+
var program = new Command37();
|
|
3841
6852
|
program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version(pkg.version);
|
|
3842
6853
|
program.addCommand(setupCommand);
|
|
3843
6854
|
program.addCommand(loginCommand);
|
|
@@ -3860,4 +6871,20 @@ program.addCommand(sessionCommand);
|
|
|
3860
6871
|
program.addCommand(subtaskCommand);
|
|
3861
6872
|
program.addCommand(errorCommand);
|
|
3862
6873
|
program.addCommand(inboxCommand);
|
|
6874
|
+
program.addCommand(handoffCommand);
|
|
6875
|
+
program.addCommand(parsePrdCommand);
|
|
6876
|
+
program.addCommand(checkpointCommand);
|
|
6877
|
+
program.addCommand(logCommand);
|
|
6878
|
+
program.addCommand(showCommand);
|
|
6879
|
+
program.addCommand(pauseCommand);
|
|
6880
|
+
program.addCommand(resumeCommand);
|
|
6881
|
+
program.addCommand(revertCommand);
|
|
6882
|
+
program.addCommand(blameCommand);
|
|
6883
|
+
program.addCommand(researchCommand);
|
|
6884
|
+
program.addCommand(bundleCommand);
|
|
6885
|
+
program.addCommand(readyCommand);
|
|
6886
|
+
program.addCommand(staleCommand);
|
|
6887
|
+
program.addCommand(orphansCommand);
|
|
6888
|
+
program.addCommand(blockedCommand);
|
|
6889
|
+
program.addCommand(compactCommand);
|
|
3863
6890
|
program.parse();
|