@vibetasks/cli 0.6.11 → 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 +1738 -212
- 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
|
|
@@ -415,7 +415,7 @@ 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
419
|
import { AuthManager as AuthManager2 } from "@vibetasks/core";
|
|
420
420
|
var ADJECTIVES = [
|
|
421
421
|
"swift",
|
|
@@ -555,11 +555,12 @@ var SessionManager = class {
|
|
|
555
555
|
}
|
|
556
556
|
/**
|
|
557
557
|
* Generate a memorable session ID like "swift-fox-42"
|
|
558
|
+
* Uses cryptographically secure random generation
|
|
558
559
|
*/
|
|
559
560
|
generateSessionId() {
|
|
560
|
-
const adj = ADJECTIVES[
|
|
561
|
-
const noun = NOUNS[
|
|
562
|
-
const num =
|
|
561
|
+
const adj = ADJECTIVES[randomInt(ADJECTIVES.length)];
|
|
562
|
+
const noun = NOUNS[randomInt(NOUNS.length)];
|
|
563
|
+
const num = randomInt(100);
|
|
563
564
|
return `${adj}-${noun}-${num}`;
|
|
564
565
|
}
|
|
565
566
|
/**
|
|
@@ -725,7 +726,7 @@ var SessionManager = class {
|
|
|
725
726
|
* Includes: vibing tasks with context, git status, files changed, next steps
|
|
726
727
|
*/
|
|
727
728
|
async generateHandoff() {
|
|
728
|
-
const { execSync:
|
|
729
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
729
730
|
const lines = [];
|
|
730
731
|
lines.push("# AI Handoff");
|
|
731
732
|
lines.push(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
|
|
@@ -739,7 +740,7 @@ var SessionManager = class {
|
|
|
739
740
|
lines.push("");
|
|
740
741
|
}
|
|
741
742
|
try {
|
|
742
|
-
const gitStatus =
|
|
743
|
+
const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
743
744
|
if (gitStatus) {
|
|
744
745
|
const changedFiles = gitStatus.split("\n").slice(0, 20);
|
|
745
746
|
lines.push("## Uncommitted Changes");
|
|
@@ -752,7 +753,7 @@ var SessionManager = class {
|
|
|
752
753
|
} catch {
|
|
753
754
|
}
|
|
754
755
|
try {
|
|
755
|
-
const recentCommits =
|
|
756
|
+
const recentCommits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
756
757
|
if (recentCommits) {
|
|
757
758
|
lines.push("## Recent Commits");
|
|
758
759
|
recentCommits.split("\n").forEach((c) => lines.push(`- ${c}`));
|
|
@@ -787,7 +788,8 @@ import { join as join2 } from "path";
|
|
|
787
788
|
import { homedir as homedir2 } from "os";
|
|
788
789
|
var ShortIdManager = class {
|
|
789
790
|
storePath;
|
|
790
|
-
maxIds =
|
|
791
|
+
maxIds = 9999;
|
|
792
|
+
// Support up to 9999 short IDs (database assigns them)
|
|
791
793
|
constructor() {
|
|
792
794
|
this.storePath = join2(homedir2(), ".vibetasks", "short-ids.json");
|
|
793
795
|
}
|
|
@@ -837,25 +839,54 @@ var ShortIdManager = class {
|
|
|
837
839
|
}
|
|
838
840
|
}
|
|
839
841
|
/**
|
|
840
|
-
* Resolve
|
|
842
|
+
* Resolve a partial UUID (8 characters) to full UUID by searching database
|
|
841
843
|
*/
|
|
842
|
-
async
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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) {
|
|
849
851
|
throw new Error(
|
|
850
|
-
`
|
|
852
|
+
`No task found starting with "${partialId}". Run 'vibetasks list' to see all tasks.`
|
|
851
853
|
);
|
|
852
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
|
+
}
|
|
853
884
|
return idOrShortId;
|
|
854
885
|
}
|
|
855
886
|
};
|
|
856
887
|
|
|
857
888
|
// src/commands/add.ts
|
|
858
|
-
var addCommand = new Command2("add").description("Add a new task").argument("<title>", "Task title").option("-n, --notes <notes>", "Task notes (markdown supported)").option("-d, --due <date>", 'Due date (YYYY-MM-DD, "today", "tomorrow", "+3d")').option("-p, --priority <level>", "Priority: low, medium, high", "none").option("-t, --tags <tags...>", "Tags (space-separated)").option("-s, --subtasks <subtasks...>", "Subtasks (space-separated, use quotes for multi-word)").option("--project <name>", "Override auto-detected project").option("-e, --energy <level>", "Energy required: low, medium, high").option("-a, --acceptance <criteria...>", 'Acceptance criteria - "DONE WHEN" conditions (use quotes for multi-word)').option("-f, --files <files...>", "Relevant files for this task").option("--non-goals <goals...>", "Explicit non-goals / out of scope items").action(async (title, options) => {
|
|
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) => {
|
|
859
890
|
const spinner = ora2("Creating task...").start();
|
|
860
891
|
try {
|
|
861
892
|
const authManager = new AuthManager3();
|
|
@@ -893,11 +924,34 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
893
924
|
throw new Error(`Invalid energy level. Must be one of: ${validEnergy.join(", ")}`);
|
|
894
925
|
}
|
|
895
926
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
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
|
+
}
|
|
901
955
|
const acceptanceCriteria = options.acceptance?.map((text) => ({
|
|
902
956
|
id: randomUUID2(),
|
|
903
957
|
text,
|
|
@@ -917,6 +971,7 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
917
971
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
918
972
|
}];
|
|
919
973
|
}
|
|
974
|
+
const assignedTo = options.for === "human" ? "human" : options.for === "ai" ? "ai" : options.for === "both" ? "both" : "both";
|
|
920
975
|
const task = await taskOps.createTask({
|
|
921
976
|
title,
|
|
922
977
|
notes: options.notes,
|
|
@@ -927,6 +982,8 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
927
982
|
// AUTO-TAGGED!
|
|
928
983
|
created_by: createdBy,
|
|
929
984
|
// ai or human
|
|
985
|
+
assigned_to: assignedTo,
|
|
986
|
+
// who can complete this task
|
|
930
987
|
status: createdBy === "ai" ? "vibing" : "todo",
|
|
931
988
|
// AI tasks start vibing
|
|
932
989
|
energy_required: options.energy,
|
|
@@ -971,6 +1028,11 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
971
1028
|
console.log(chalk3.gray(`\u{1F916} Created by: ${chalk3.cyan("AI")}`));
|
|
972
1029
|
console.log(chalk3.gray(`\u26A1 Status: ${chalk3.magenta("vibing")}`));
|
|
973
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
|
+
}
|
|
974
1036
|
console.log(chalk3.gray(`ID: ${task.id.substring(0, 8)}...`));
|
|
975
1037
|
console.log(chalk3.white.bold(task.title));
|
|
976
1038
|
if (task.notes) {
|
|
@@ -998,6 +1060,21 @@ var addCommand = new Command2("add").description("Add a new task").argument("<ti
|
|
|
998
1060
|
console.log(chalk3.cyan(`Subtasks: ${subtasksJson.length}`));
|
|
999
1061
|
subtasksJson.forEach((st, i) => {
|
|
1000
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
|
+
}
|
|
1001
1078
|
});
|
|
1002
1079
|
}
|
|
1003
1080
|
if (acceptanceCriteria.length > 0) {
|
|
@@ -1059,9 +1136,9 @@ var listCommand = new Command3("list").description("List tasks").argument("[filt
|
|
|
1059
1136
|
tasks = tasks.filter((t) => t.created_by === options.createdBy);
|
|
1060
1137
|
}
|
|
1061
1138
|
if (options.status) {
|
|
1062
|
-
const validStatuses = ["todo", "vibing", "done"];
|
|
1139
|
+
const validStatuses = ["todo", "vibing", "done", "paused", "archived"];
|
|
1063
1140
|
if (!validStatuses.includes(options.status)) {
|
|
1064
|
-
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`));
|
|
1065
1142
|
process.exit(1);
|
|
1066
1143
|
}
|
|
1067
1144
|
tasks = tasks.filter((t) => t.status === options.status);
|
|
@@ -1157,7 +1234,13 @@ ${filterMessages[filter] || "No tasks found"}.
|
|
|
1157
1234
|
const id = task.id.substring(0, 8);
|
|
1158
1235
|
const status = statusColor(`${statusEmojis[task.status || "todo"]} ${task.status || "todo"}`);
|
|
1159
1236
|
const title = task.status === "done" ? chalk4.strikethrough(task.title) : task.title;
|
|
1160
|
-
|
|
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;
|
|
1161
1244
|
const project = task.project_tag ? chalk4.blue(task.project_tag) : chalk4.gray("-");
|
|
1162
1245
|
const priority = priorityColor(task.priority || "none");
|
|
1163
1246
|
const dueDate = task.due_date ? task.due_date.split("T")[0] : chalk4.gray("-");
|
|
@@ -1214,10 +1297,10 @@ var doneCommand = new Command4("done").description("Mark a task as complete").ar
|
|
|
1214
1297
|
const authManager = new AuthManager5();
|
|
1215
1298
|
const taskOps = await TaskOperations3.fromAuthManager(authManager);
|
|
1216
1299
|
const shortIdManager = new ShortIdManager();
|
|
1217
|
-
let
|
|
1218
|
-
if (
|
|
1300
|
+
let taskId2 = await shortIdManager.resolveId(id, taskOps);
|
|
1301
|
+
if (taskId2.length < 32) {
|
|
1219
1302
|
const allTasks = await taskOps.getTasks("all");
|
|
1220
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(
|
|
1303
|
+
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId2));
|
|
1221
1304
|
if (!matchingTask) {
|
|
1222
1305
|
spinner.fail(chalk5.red("Task not found"));
|
|
1223
1306
|
console.error(chalk5.gray(`
|
|
@@ -1225,11 +1308,11 @@ No task found with ID starting with: ${id}
|
|
|
1225
1308
|
`));
|
|
1226
1309
|
process.exit(1);
|
|
1227
1310
|
}
|
|
1228
|
-
|
|
1311
|
+
taskId2 = matchingTask.id;
|
|
1229
1312
|
}
|
|
1230
1313
|
const sessionManager2 = getSessionManager();
|
|
1231
1314
|
const currentSession = await sessionManager2.getOrCreateSession();
|
|
1232
|
-
const existingTask = await taskOps.getTask(
|
|
1315
|
+
const existingTask = await taskOps.getTask(taskId2);
|
|
1233
1316
|
const existingHistory = existingTask?.session_history || [];
|
|
1234
1317
|
const updatedHistory = [
|
|
1235
1318
|
...existingHistory,
|
|
@@ -1248,8 +1331,8 @@ No task found with ID starting with: ${id}
|
|
|
1248
1331
|
if (contextNotes) {
|
|
1249
1332
|
updatePayload.context_notes = contextNotes;
|
|
1250
1333
|
}
|
|
1251
|
-
await taskOps.updateTask(
|
|
1252
|
-
const task = await taskOps.completeTask(
|
|
1334
|
+
await taskOps.updateTask(taskId2, updatePayload);
|
|
1335
|
+
const task = await taskOps.completeTask(taskId2);
|
|
1253
1336
|
await sessionManager2.logAction({
|
|
1254
1337
|
type: "task_completed",
|
|
1255
1338
|
description: `Completed: "${task.title}"`,
|
|
@@ -1270,10 +1353,22 @@ No task found with ID starting with: ${id}
|
|
|
1270
1353
|
if (error.message.includes("Not authenticated")) {
|
|
1271
1354
|
console.error(chalk5.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
1272
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"));
|
|
1273
1364
|
} else {
|
|
1274
1365
|
console.error(chalk5.red(`
|
|
1275
|
-
Error: ${error.message}
|
|
1276
|
-
`
|
|
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();
|
|
1277
1372
|
}
|
|
1278
1373
|
process.exit(1);
|
|
1279
1374
|
}
|
|
@@ -1284,16 +1379,20 @@ import { Command as Command5 } from "commander";
|
|
|
1284
1379
|
import chalk6 from "chalk";
|
|
1285
1380
|
import ora4 from "ora";
|
|
1286
1381
|
import inquirer from "inquirer";
|
|
1287
|
-
import {
|
|
1288
|
-
|
|
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) => {
|
|
1289
1388
|
const spinner = ora4();
|
|
1290
1389
|
try {
|
|
1291
1390
|
const authManager = new AuthManager6();
|
|
1292
1391
|
const taskOps = await TaskOperations4.fromAuthManager(authManager);
|
|
1293
1392
|
const shortIdManager = new ShortIdManager();
|
|
1294
|
-
let
|
|
1393
|
+
let taskId2;
|
|
1295
1394
|
if (taskIdInput) {
|
|
1296
|
-
|
|
1395
|
+
taskId2 = await shortIdManager.resolveId(taskIdInput, taskOps);
|
|
1297
1396
|
}
|
|
1298
1397
|
const allTasks = await taskOps.getTasks("all");
|
|
1299
1398
|
const vibingTasks = allTasks.filter((t) => t.status === "vibing" && !t.completed);
|
|
@@ -1301,7 +1400,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1301
1400
|
console.log(chalk6.gray(`
|
|
1302
1401
|
Currently vibing on ${vibingTasks.length} task(s)`));
|
|
1303
1402
|
}
|
|
1304
|
-
if (!
|
|
1403
|
+
if (!taskId2 || options.pick) {
|
|
1305
1404
|
const todoTasks = allTasks.filter((t) => t.status === "todo" && !t.completed);
|
|
1306
1405
|
if (todoTasks.length === 0) {
|
|
1307
1406
|
console.log(chalk6.yellow('\nNo todo tasks found. Add one with `vibetasks add "task title"`\n'));
|
|
@@ -1316,10 +1415,10 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1316
1415
|
value: t.id
|
|
1317
1416
|
}))
|
|
1318
1417
|
}]);
|
|
1319
|
-
|
|
1418
|
+
taskId2 = selectedTask;
|
|
1320
1419
|
}
|
|
1321
1420
|
spinner.start("Starting task...");
|
|
1322
|
-
const task = allTasks.find((t) => t.id ===
|
|
1421
|
+
const task = allTasks.find((t) => t.id === taskId2);
|
|
1323
1422
|
const sessionManager2 = getSessionManager();
|
|
1324
1423
|
const currentSession = await sessionManager2.getOrCreateSession();
|
|
1325
1424
|
const existingSessionHistory = task?.session_history || [];
|
|
@@ -1335,7 +1434,7 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1335
1434
|
}
|
|
1336
1435
|
];
|
|
1337
1436
|
}
|
|
1338
|
-
await taskOps.updateTask(
|
|
1437
|
+
await taskOps.updateTask(taskId2, {
|
|
1339
1438
|
status: "vibing",
|
|
1340
1439
|
completed: false,
|
|
1341
1440
|
session_id: currentSession.id,
|
|
@@ -1344,16 +1443,88 @@ var vibingCommand = new Command5("vibing").alias("start").alias("v").description
|
|
|
1344
1443
|
});
|
|
1345
1444
|
spinner.succeed(chalk6.green("Now vibing on:"));
|
|
1346
1445
|
console.log(chalk6.white(`
|
|
1347
|
-
${task?.title ||
|
|
1446
|
+
${task?.title || taskId2}
|
|
1348
1447
|
`));
|
|
1349
1448
|
if (lastSession && lastSession.session_id !== currentSession.id) {
|
|
1350
1449
|
console.log(chalk6.cyan(` \u{1F4CB} Continuing from session: ${lastSession.session_id}`));
|
|
1351
1450
|
console.log(chalk6.gray(` This task has been worked on by ${existingSessionHistory.length} session(s)
|
|
1352
1451
|
`));
|
|
1353
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
|
+
}
|
|
1354
1523
|
console.log(chalk6.gray(" Tips:"));
|
|
1355
1524
|
console.log(chalk6.gray(" \u2022 Update context notes as you work"));
|
|
1356
|
-
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"));
|
|
1357
1528
|
console.log(chalk6.gray(' \u2022 In Claude Code, say "show my vibing tasks"\n'));
|
|
1358
1529
|
} catch (error) {
|
|
1359
1530
|
spinner.fail(chalk6.red("Failed to start task"));
|
|
@@ -1437,19 +1608,8 @@ var updateCommand = new Command7("update").description("Update a task").argument
|
|
|
1437
1608
|
try {
|
|
1438
1609
|
const authManager = new AuthManager8();
|
|
1439
1610
|
const taskOps = await TaskOperations6.fromAuthManager(authManager);
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
const allTasks = await taskOps.getTasks("all");
|
|
1443
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(id));
|
|
1444
|
-
if (!matchingTask) {
|
|
1445
|
-
spinner.fail(chalk8.red("Task not found"));
|
|
1446
|
-
console.error(chalk8.gray(`
|
|
1447
|
-
No task found with ID starting with: ${id}
|
|
1448
|
-
`));
|
|
1449
|
-
process.exit(1);
|
|
1450
|
-
}
|
|
1451
|
-
taskId = matchingTask.id;
|
|
1452
|
-
}
|
|
1611
|
+
const shortIdManager = new ShortIdManager();
|
|
1612
|
+
let taskId2 = await shortIdManager.resolveId(id, taskOps);
|
|
1453
1613
|
const updates = {};
|
|
1454
1614
|
if (options.title) updates.title = options.title;
|
|
1455
1615
|
if (options.notes) updates.notes = options.notes;
|
|
@@ -1477,7 +1637,7 @@ No task found with ID starting with: ${id}
|
|
|
1477
1637
|
}
|
|
1478
1638
|
updates.energy_required = options.energy;
|
|
1479
1639
|
}
|
|
1480
|
-
const task = await taskOps.updateTask(
|
|
1640
|
+
const task = await taskOps.updateTask(taskId2, updates);
|
|
1481
1641
|
spinner.succeed(chalk8.green("Task updated!"));
|
|
1482
1642
|
console.log(chalk8.gray(`
|
|
1483
1643
|
\u2713 ${task.title}
|
|
@@ -1507,23 +1667,10 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
|
|
|
1507
1667
|
try {
|
|
1508
1668
|
const authManager = new AuthManager9();
|
|
1509
1669
|
const taskOps = await TaskOperations7.fromAuthManager(authManager);
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(id));
|
|
1515
|
-
if (!matchingTask) {
|
|
1516
|
-
console.error(chalk9.red("\nTask not found"));
|
|
1517
|
-
console.error(chalk9.gray(`No task found with ID starting with: ${id}
|
|
1518
|
-
`));
|
|
1519
|
-
process.exit(1);
|
|
1520
|
-
}
|
|
1521
|
-
taskId = matchingTask.id;
|
|
1522
|
-
taskTitle = matchingTask.title;
|
|
1523
|
-
} else {
|
|
1524
|
-
const task = await taskOps.getTask(taskId);
|
|
1525
|
-
taskTitle = task.title;
|
|
1526
|
-
}
|
|
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;
|
|
1527
1674
|
if (!options.yes) {
|
|
1528
1675
|
const answers = await inquirer2.prompt([
|
|
1529
1676
|
{
|
|
@@ -1539,7 +1686,7 @@ var deleteCommand = new Command8("delete").description("Delete a task").argument
|
|
|
1539
1686
|
}
|
|
1540
1687
|
}
|
|
1541
1688
|
const spinner = ora6("Deleting task...").start();
|
|
1542
|
-
await taskOps.deleteTask(
|
|
1689
|
+
await taskOps.deleteTask(taskId2);
|
|
1543
1690
|
spinner.succeed(chalk9.green("Task deleted!"));
|
|
1544
1691
|
console.log(chalk9.gray(`
|
|
1545
1692
|
\u2717 ${taskTitle}
|
|
@@ -1623,17 +1770,17 @@ import { Command as Command10 } from "commander";
|
|
|
1623
1770
|
import chalk11 from "chalk";
|
|
1624
1771
|
import ora7 from "ora";
|
|
1625
1772
|
import fs from "fs/promises";
|
|
1626
|
-
import
|
|
1773
|
+
import path2 from "path";
|
|
1627
1774
|
function getVibetasksHooksPath() {
|
|
1628
|
-
const cliRoot =
|
|
1775
|
+
const cliRoot = path2.dirname(path2.dirname(path2.dirname(new URL(import.meta.url).pathname)));
|
|
1629
1776
|
const normalizedPath = process.platform === "win32" && cliRoot.startsWith("/") ? cliRoot.slice(1) : cliRoot;
|
|
1630
|
-
return
|
|
1777
|
+
return path2.join(normalizedPath, "hooks");
|
|
1631
1778
|
}
|
|
1632
1779
|
var hooksCommand = new Command10("hooks").description("Manage hooks integration (git and Claude Code)");
|
|
1633
1780
|
hooksCommand.command("install").description("Install git hooks in current repository").action(async () => {
|
|
1634
1781
|
const spinner = ora7("Installing git hooks...").start();
|
|
1635
1782
|
try {
|
|
1636
|
-
const gitDir =
|
|
1783
|
+
const gitDir = path2.join(process.cwd(), ".git");
|
|
1637
1784
|
try {
|
|
1638
1785
|
await fs.access(gitDir);
|
|
1639
1786
|
} catch {
|
|
@@ -1641,9 +1788,9 @@ hooksCommand.command("install").description("Install git hooks in current reposi
|
|
|
1641
1788
|
console.error(chalk11.gray("\nRun this command from the root of a git repository.\n"));
|
|
1642
1789
|
process.exit(1);
|
|
1643
1790
|
}
|
|
1644
|
-
const hooksDir =
|
|
1791
|
+
const hooksDir = path2.join(gitDir, "hooks");
|
|
1645
1792
|
await fs.mkdir(hooksDir, { recursive: true });
|
|
1646
|
-
const postCommitPath =
|
|
1793
|
+
const postCommitPath = path2.join(hooksDir, "post-commit");
|
|
1647
1794
|
const hookScript = `#!/bin/sh
|
|
1648
1795
|
# VibeTasks post-commit hook
|
|
1649
1796
|
# Automatically updates tasks based on commit messages
|
|
@@ -1697,7 +1844,7 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
|
|
|
1697
1844
|
try {
|
|
1698
1845
|
const projectPath = options.project || process.cwd();
|
|
1699
1846
|
const hooksPath = getVibetasksHooksPath();
|
|
1700
|
-
const syncScriptPath =
|
|
1847
|
+
const syncScriptPath = path2.join(hooksPath, "sync-todos.js");
|
|
1701
1848
|
const hookConfig = {
|
|
1702
1849
|
hooks: {
|
|
1703
1850
|
SubagentStop: [
|
|
@@ -1716,9 +1863,9 @@ hooksCommand.command("claude").description("Generate Claude Code hook configurat
|
|
|
1716
1863
|
console.log(JSON.stringify(hookConfig, null, 2));
|
|
1717
1864
|
process.exit(0);
|
|
1718
1865
|
}
|
|
1719
|
-
const claudeDir =
|
|
1866
|
+
const claudeDir = path2.join(projectPath, ".claude");
|
|
1720
1867
|
await fs.mkdir(claudeDir, { recursive: true });
|
|
1721
|
-
const settingsPath =
|
|
1868
|
+
const settingsPath = path2.join(claudeDir, "settings.json");
|
|
1722
1869
|
let existingSettings = {};
|
|
1723
1870
|
try {
|
|
1724
1871
|
const data = await fs.readFile(settingsPath, "utf-8");
|
|
@@ -1763,8 +1910,8 @@ Error: ${error.message}
|
|
|
1763
1910
|
});
|
|
1764
1911
|
hooksCommand.command("status").description("Show status of installed hooks").action(async () => {
|
|
1765
1912
|
console.log(chalk11.blue.bold("\n VibeTasks Hooks Status\n"));
|
|
1766
|
-
const gitHooksDir =
|
|
1767
|
-
const postCommitPath =
|
|
1913
|
+
const gitHooksDir = path2.join(process.cwd(), ".git", "hooks");
|
|
1914
|
+
const postCommitPath = path2.join(gitHooksDir, "post-commit");
|
|
1768
1915
|
try {
|
|
1769
1916
|
const content = await fs.readFile(postCommitPath, "utf-8");
|
|
1770
1917
|
if (content.includes("vibetasks") || content.includes("VibeTasks")) {
|
|
@@ -1775,7 +1922,7 @@ hooksCommand.command("status").description("Show status of installed hooks").act
|
|
|
1775
1922
|
} catch {
|
|
1776
1923
|
console.log(chalk11.gray(" [ ] Git post-commit hook not installed"));
|
|
1777
1924
|
}
|
|
1778
|
-
const claudeSettingsPath =
|
|
1925
|
+
const claudeSettingsPath = path2.join(process.cwd(), ".claude", "settings.json");
|
|
1779
1926
|
try {
|
|
1780
1927
|
const content = await fs.readFile(claudeSettingsPath, "utf-8");
|
|
1781
1928
|
const settings = JSON.parse(content);
|
|
@@ -1863,7 +2010,7 @@ import inquirer4 from "inquirer";
|
|
|
1863
2010
|
import ora9 from "ora";
|
|
1864
2011
|
import chalk13 from "chalk";
|
|
1865
2012
|
import fs2 from "fs/promises";
|
|
1866
|
-
import
|
|
2013
|
+
import path3 from "path";
|
|
1867
2014
|
import os from "os";
|
|
1868
2015
|
import { createServer as createServer2 } from "http";
|
|
1869
2016
|
import { exec as exec2 } from "child_process";
|
|
@@ -1905,12 +2052,12 @@ async function getAvailablePort2(startPort = 3737) {
|
|
|
1905
2052
|
});
|
|
1906
2053
|
}
|
|
1907
2054
|
function getClaudeConfigPath() {
|
|
1908
|
-
const configHome = process.env.XDG_CONFIG_HOME ||
|
|
1909
|
-
return
|
|
2055
|
+
const configHome = process.env.XDG_CONFIG_HOME || path3.join(os.homedir(), ".config");
|
|
2056
|
+
return path3.join(configHome, "claude-code", "config.json");
|
|
1910
2057
|
}
|
|
1911
2058
|
async function claudeCodeConfigExists() {
|
|
1912
2059
|
try {
|
|
1913
|
-
const configDir =
|
|
2060
|
+
const configDir = path3.dirname(getClaudeConfigPath());
|
|
1914
2061
|
await fs2.access(configDir);
|
|
1915
2062
|
return true;
|
|
1916
2063
|
} catch {
|
|
@@ -2125,7 +2272,7 @@ async function stepClaudeCodeConfig() {
|
|
|
2125
2272
|
const spinner = ora9("Configuring Claude Code...").start();
|
|
2126
2273
|
try {
|
|
2127
2274
|
const configPath = getClaudeConfigPath();
|
|
2128
|
-
const configDir =
|
|
2275
|
+
const configDir = path3.dirname(configPath);
|
|
2129
2276
|
await fs2.mkdir(configDir, { recursive: true });
|
|
2130
2277
|
let config = {};
|
|
2131
2278
|
try {
|
|
@@ -2374,7 +2521,7 @@ async function runNonInteractiveSetup() {
|
|
|
2374
2521
|
}
|
|
2375
2522
|
console.log(chalk13.gray("\nConfiguring Claude Code MCP server..."));
|
|
2376
2523
|
const configPath = getClaudeConfigPath();
|
|
2377
|
-
const configDir =
|
|
2524
|
+
const configDir = path3.dirname(configPath);
|
|
2378
2525
|
try {
|
|
2379
2526
|
await fs2.mkdir(configDir, { recursive: true });
|
|
2380
2527
|
let config = {};
|
|
@@ -2705,7 +2852,7 @@ import chalk15 from "chalk";
|
|
|
2705
2852
|
import ora11 from "ora";
|
|
2706
2853
|
import inquirer6 from "inquirer";
|
|
2707
2854
|
import { AuthManager as AuthManager14, TaskOperations as TaskOperations10 } from "@vibetasks/core";
|
|
2708
|
-
var archiveCommand = new Command14("archive").description("Archive a task or manage archived tasks").argument("[task-id]", "Task ID to archive (full or first 8 characters)").option("-l, --list", "List all archived tasks").option("-u, --unarchive <id>", "Unarchive a task by ID").option("--pick", "Pick from completed tasks to archive").action(async (
|
|
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) => {
|
|
2709
2856
|
const spinner = ora11();
|
|
2710
2857
|
try {
|
|
2711
2858
|
const authManager = new AuthManager14();
|
|
@@ -2754,7 +2901,7 @@ Restored: ${task2.title}`));
|
|
|
2754
2901
|
`));
|
|
2755
2902
|
process.exit(0);
|
|
2756
2903
|
}
|
|
2757
|
-
if (options.pick || !
|
|
2904
|
+
if (options.pick || !taskId2) {
|
|
2758
2905
|
const allTasks = await taskOps.getTasks("completed");
|
|
2759
2906
|
if (allTasks.length === 0) {
|
|
2760
2907
|
console.log(chalk15.yellow("\nNo completed tasks to archive. Complete some tasks first!\n"));
|
|
@@ -2769,16 +2916,16 @@ Restored: ${task2.title}`));
|
|
|
2769
2916
|
value: t.id
|
|
2770
2917
|
}))
|
|
2771
2918
|
}]);
|
|
2772
|
-
|
|
2919
|
+
taskId2 = selectedTask;
|
|
2773
2920
|
}
|
|
2774
|
-
if (!
|
|
2921
|
+
if (!taskId2) {
|
|
2775
2922
|
console.log(chalk15.yellow("\nUsage: vibetasks archive <task-id>"));
|
|
2776
2923
|
console.log(chalk15.gray(" or: vibetasks archive --pick"));
|
|
2777
2924
|
console.log(chalk15.gray(" or: vibetasks archive --list"));
|
|
2778
2925
|
console.log(chalk15.gray(" or: vibetasks archive --unarchive <id>\n"));
|
|
2779
2926
|
process.exit(1);
|
|
2780
2927
|
}
|
|
2781
|
-
const validTaskId =
|
|
2928
|
+
const validTaskId = taskId2;
|
|
2782
2929
|
spinner.start("Archiving task...");
|
|
2783
2930
|
let targetId = validTaskId;
|
|
2784
2931
|
if (validTaskId.length < 32) {
|
|
@@ -2787,7 +2934,7 @@ Restored: ${task2.title}`));
|
|
|
2787
2934
|
if (!matchingTask) {
|
|
2788
2935
|
spinner.fail(chalk15.red("Task not found"));
|
|
2789
2936
|
console.error(chalk15.gray(`
|
|
2790
|
-
No task found with ID starting with: ${
|
|
2937
|
+
No task found with ID starting with: ${taskId2}
|
|
2791
2938
|
`));
|
|
2792
2939
|
process.exit(1);
|
|
2793
2940
|
}
|
|
@@ -2820,13 +2967,13 @@ Error: ${error.message}
|
|
|
2820
2967
|
import { Command as Command15 } from "commander";
|
|
2821
2968
|
import chalk16 from "chalk";
|
|
2822
2969
|
import ora12 from "ora";
|
|
2823
|
-
import { spawn } from "child_process";
|
|
2824
|
-
import
|
|
2825
|
-
import { fileURLToPath } from "url";
|
|
2970
|
+
import { spawn as spawn2 } from "child_process";
|
|
2971
|
+
import path4 from "path";
|
|
2972
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2826
2973
|
import { AuthManager as AuthManager15, TaskOperations as TaskOperations11 } from "@vibetasks/core";
|
|
2827
2974
|
import { generateErrorTaskTitle as generateErrorTaskTitle2 } from "@vibetasks/shared";
|
|
2828
|
-
var __filename2 =
|
|
2829
|
-
var
|
|
2975
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2976
|
+
var __dirname3 = path4.dirname(__filename2);
|
|
2830
2977
|
var daemonCommand = new Command15("daemon").description("Manage the VibeTasks error capture daemon").addCommand(createStartCommand()).addCommand(createStopCommand()).addCommand(createStatusCommand()).addCommand(createConfigureCommand()).addCommand(createCaptureCommand());
|
|
2831
2978
|
function createStartCommand() {
|
|
2832
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) => {
|
|
@@ -3026,8 +3173,8 @@ function createCaptureCommand() {
|
|
|
3026
3173
|
});
|
|
3027
3174
|
}
|
|
3028
3175
|
async function startDaemonBackground(config) {
|
|
3029
|
-
const workerPath =
|
|
3030
|
-
const child =
|
|
3176
|
+
const workerPath = path4.resolve(__dirname3, "..", "daemon-worker.js");
|
|
3177
|
+
const child = spawn2(process.execPath, [workerPath], {
|
|
3031
3178
|
detached: true,
|
|
3032
3179
|
stdio: "ignore",
|
|
3033
3180
|
env: {
|
|
@@ -3484,6 +3631,31 @@ var sessionCommand = new Command17("session").description("Manage AI sessions fo
|
|
|
3484
3631
|
console.log(chalk18.yellow("\u26A0 Sync failed (not logged in or server unavailable)"));
|
|
3485
3632
|
}
|
|
3486
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
|
+
})
|
|
3487
3659
|
);
|
|
3488
3660
|
function formatDuration(start, end) {
|
|
3489
3661
|
const ms = new Date(end).getTime() - new Date(start).getTime();
|
|
@@ -3544,20 +3716,8 @@ subtaskCommand.command("list").description("List subtasks for a task").argument(
|
|
|
3544
3716
|
const authManager = new AuthManager17();
|
|
3545
3717
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3546
3718
|
const shortIdManager = new ShortIdManager();
|
|
3547
|
-
let
|
|
3548
|
-
|
|
3549
|
-
const allTasks = await taskOps.getTasks("all");
|
|
3550
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
|
|
3551
|
-
if (!matchingTask) {
|
|
3552
|
-
spinner.fail(chalk19.red("Task not found"));
|
|
3553
|
-
console.error(chalk19.gray(`
|
|
3554
|
-
No task found with ID: ${taskIdArg}
|
|
3555
|
-
`));
|
|
3556
|
-
process.exit(1);
|
|
3557
|
-
}
|
|
3558
|
-
taskId = matchingTask.id;
|
|
3559
|
-
}
|
|
3560
|
-
const task = await taskOps.getTask(taskId);
|
|
3719
|
+
let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
|
|
3720
|
+
const task = await taskOps.getTask(taskId2);
|
|
3561
3721
|
const subtasks = task.subtasks_json || [];
|
|
3562
3722
|
spinner.stop();
|
|
3563
3723
|
console.log();
|
|
@@ -3592,20 +3752,8 @@ subtaskCommand.command("done").description("Mark subtasks as complete").argument
|
|
|
3592
3752
|
const authManager = new AuthManager17();
|
|
3593
3753
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3594
3754
|
const shortIdManager = new ShortIdManager();
|
|
3595
|
-
let
|
|
3596
|
-
|
|
3597
|
-
const allTasks = await taskOps.getTasks("all");
|
|
3598
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
|
|
3599
|
-
if (!matchingTask) {
|
|
3600
|
-
spinner.fail(chalk19.red("Task not found"));
|
|
3601
|
-
console.error(chalk19.gray(`
|
|
3602
|
-
No task found with ID: ${taskIdArg}
|
|
3603
|
-
`));
|
|
3604
|
-
process.exit(1);
|
|
3605
|
-
}
|
|
3606
|
-
taskId = matchingTask.id;
|
|
3607
|
-
}
|
|
3608
|
-
const task = await taskOps.getTask(taskId);
|
|
3755
|
+
let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
|
|
3756
|
+
const task = await taskOps.getTask(taskId2);
|
|
3609
3757
|
const subtasks = [...task.subtasks_json || []];
|
|
3610
3758
|
if (subtasks.length === 0) {
|
|
3611
3759
|
spinner.fail(chalk19.yellow("No subtasks to update"));
|
|
@@ -3639,7 +3787,7 @@ Valid range: 1-${subtasks.length}
|
|
|
3639
3787
|
spinner.succeed(chalk19.yellow("All specified subtasks were already done"));
|
|
3640
3788
|
process.exit(0);
|
|
3641
3789
|
}
|
|
3642
|
-
await taskOps.updateTask(
|
|
3790
|
+
await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
|
|
3643
3791
|
const doneCount = subtasks.filter((s) => s.done).length;
|
|
3644
3792
|
spinner.succeed(chalk19.green(`Marked ${updated.length} subtask(s) as done`));
|
|
3645
3793
|
console.log();
|
|
@@ -3650,7 +3798,7 @@ Valid range: 1-${subtasks.length}
|
|
|
3650
3798
|
console.log(chalk19.gray(`Progress: ${doneCount}/${subtasks.length} completed`));
|
|
3651
3799
|
if (doneCount === subtasks.length) {
|
|
3652
3800
|
console.log();
|
|
3653
|
-
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)}`));
|
|
3654
3802
|
}
|
|
3655
3803
|
console.log();
|
|
3656
3804
|
process.exit(0);
|
|
@@ -3668,20 +3816,8 @@ subtaskCommand.command("undo").description("Mark subtasks as incomplete").argume
|
|
|
3668
3816
|
const authManager = new AuthManager17();
|
|
3669
3817
|
const taskOps = await TaskOperations13.fromAuthManager(authManager);
|
|
3670
3818
|
const shortIdManager = new ShortIdManager();
|
|
3671
|
-
let
|
|
3672
|
-
|
|
3673
|
-
const allTasks = await taskOps.getTasks("all");
|
|
3674
|
-
const matchingTask = allTasks.find((t) => t.id.startsWith(taskId));
|
|
3675
|
-
if (!matchingTask) {
|
|
3676
|
-
spinner.fail(chalk19.red("Task not found"));
|
|
3677
|
-
console.error(chalk19.gray(`
|
|
3678
|
-
No task found with ID: ${taskIdArg}
|
|
3679
|
-
`));
|
|
3680
|
-
process.exit(1);
|
|
3681
|
-
}
|
|
3682
|
-
taskId = matchingTask.id;
|
|
3683
|
-
}
|
|
3684
|
-
const task = await taskOps.getTask(taskId);
|
|
3819
|
+
let taskId2 = await shortIdManager.resolveId(taskIdArg, taskOps);
|
|
3820
|
+
const task = await taskOps.getTask(taskId2);
|
|
3685
3821
|
const subtasks = [...task.subtasks_json || []];
|
|
3686
3822
|
if (subtasks.length === 0) {
|
|
3687
3823
|
spinner.fail(chalk19.yellow("No subtasks to update"));
|
|
@@ -3715,7 +3851,7 @@ Valid range: 1-${subtasks.length}
|
|
|
3715
3851
|
spinner.succeed(chalk19.yellow("All specified subtasks were already incomplete"));
|
|
3716
3852
|
process.exit(0);
|
|
3717
3853
|
}
|
|
3718
|
-
await taskOps.updateTask(
|
|
3854
|
+
await taskOps.updateTask(taskId2, { subtasks_json: subtasks });
|
|
3719
3855
|
const doneCount = subtasks.filter((s) => s.done).length;
|
|
3720
3856
|
spinner.succeed(chalk19.yellow(`Marked ${updated.length} subtask(s) as incomplete`));
|
|
3721
3857
|
console.log();
|
|
@@ -4072,14 +4208,288 @@ inboxCommand.addCommand(inboxStatsCommand);
|
|
|
4072
4208
|
// src/commands/handoff.ts
|
|
4073
4209
|
import { Command as Command21 } from "commander";
|
|
4074
4210
|
import chalk22 from "chalk";
|
|
4211
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4075
4212
|
import { AuthManager as AuthManager20, TaskOperations as TaskOperations16 } from "@vibetasks/core";
|
|
4076
|
-
|
|
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) => {
|
|
4077
4437
|
const sessionManager2 = getSessionManager();
|
|
4078
|
-
if (sessionId) {
|
|
4438
|
+
if (sessionId && !options?.json) {
|
|
4079
4439
|
const handoff = await sessionManager2.getSessionHandoff(sessionId);
|
|
4080
4440
|
console.log(handoff);
|
|
4081
4441
|
return;
|
|
4082
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
|
+
}
|
|
4083
4493
|
console.log(chalk22.cyan("# AI Handoff"));
|
|
4084
4494
|
console.log(chalk22.gray(`Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}`));
|
|
4085
4495
|
console.log();
|
|
@@ -4140,8 +4550,8 @@ var handoffCommand = new Command21("handoff").description("Generate comprehensiv
|
|
|
4140
4550
|
console.log();
|
|
4141
4551
|
}
|
|
4142
4552
|
try {
|
|
4143
|
-
const { execSync:
|
|
4144
|
-
const gitStatus =
|
|
4553
|
+
const { execSync: execSync3 } = await import("child_process");
|
|
4554
|
+
const gitStatus = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
4145
4555
|
if (gitStatus) {
|
|
4146
4556
|
const files = gitStatus.split("\n");
|
|
4147
4557
|
console.log(chalk22.yellow(`## Uncommitted Changes (${files.length} files)`));
|
|
@@ -4151,7 +4561,7 @@ var handoffCommand = new Command21("handoff").description("Generate comprehensiv
|
|
|
4151
4561
|
}
|
|
4152
4562
|
console.log();
|
|
4153
4563
|
}
|
|
4154
|
-
const commits =
|
|
4564
|
+
const commits = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
4155
4565
|
if (commits) {
|
|
4156
4566
|
console.log(chalk22.yellow("## Recent Commits"));
|
|
4157
4567
|
commits.split("\n").forEach((c) => console.log(chalk22.gray(` ${c}`)));
|
|
@@ -4180,7 +4590,7 @@ import { readFile as readFile3 } from "fs/promises";
|
|
|
4180
4590
|
import { existsSync } from "fs";
|
|
4181
4591
|
import { AuthManager as AuthManager21, TaskOperations as TaskOperations17 } from "@vibetasks/core";
|
|
4182
4592
|
import { detectProject as detectProject6 } from "@vibetasks/shared/utils/project-detector";
|
|
4183
|
-
import { randomUUID as
|
|
4593
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4184
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) => {
|
|
4185
4595
|
const spinner = ora15("Parsing PRD...").start();
|
|
4186
4596
|
try {
|
|
@@ -4200,7 +4610,7 @@ var parsePrdCommand = new Command22("parse-prd").description("Parse a PRD/requir
|
|
|
4200
4610
|
}
|
|
4201
4611
|
const finalTitle = options.title || parsed.title || "Implement PRD";
|
|
4202
4612
|
const subtasksJson = parsed.tasks.map((task2, i) => ({
|
|
4203
|
-
id:
|
|
4613
|
+
id: randomUUID4(),
|
|
4204
4614
|
title: task2,
|
|
4205
4615
|
done: false
|
|
4206
4616
|
}));
|
|
@@ -4453,21 +4863,21 @@ function parsePrdContent(content, verbose = false) {
|
|
|
4453
4863
|
import { Command as Command23 } from "commander";
|
|
4454
4864
|
import chalk24 from "chalk";
|
|
4455
4865
|
import { AuthManager as AuthManager22, TaskOperations as TaskOperations18 } from "@vibetasks/core";
|
|
4456
|
-
import { execSync } from "child_process";
|
|
4866
|
+
import { execSync as execSync2 } from "child_process";
|
|
4457
4867
|
import * as readline from "readline";
|
|
4458
|
-
var checkpointCommand = new Command23("checkpoint").alias("cp").description("Quick context save for current work (interactive)").argument("[task-id]", "Task ID (defaults to your vibing task)").option("-q, --quick", "Quick mode: just git diff + timestamp, no prompts").option("-m, --message <msg>", "Context message (skip prompt)").option("--no-git", "Skip git diff attachment").action(async (
|
|
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) => {
|
|
4459
4869
|
try {
|
|
4460
4870
|
const authManager = new AuthManager22();
|
|
4461
4871
|
const taskOps = await TaskOperations18.fromAuthManager(authManager);
|
|
4462
4872
|
const sessionManager2 = getSessionManager();
|
|
4463
4873
|
let task;
|
|
4464
|
-
if (
|
|
4874
|
+
if (taskId2) {
|
|
4465
4875
|
const allTasks = await taskOps.getTasks("all");
|
|
4466
4876
|
task = allTasks.find(
|
|
4467
|
-
(t) => t.id.startsWith(
|
|
4877
|
+
(t) => t.id.startsWith(taskId2) || t.short_id?.toString() === taskId2
|
|
4468
4878
|
);
|
|
4469
4879
|
if (!task) {
|
|
4470
|
-
console.error(chalk24.red(`Task not found: ${
|
|
4880
|
+
console.error(chalk24.red(`Task not found: ${taskId2}`));
|
|
4471
4881
|
process.exit(1);
|
|
4472
4882
|
}
|
|
4473
4883
|
} else {
|
|
@@ -4591,9 +5001,9 @@ Error: ${error.message}
|
|
|
4591
5001
|
function getGitContext() {
|
|
4592
5002
|
const parts = [];
|
|
4593
5003
|
try {
|
|
4594
|
-
const branch =
|
|
5004
|
+
const branch = execSync2("git branch --show-current", { encoding: "utf-8" }).trim();
|
|
4595
5005
|
parts.push(`Branch: ${branch}`);
|
|
4596
|
-
const status =
|
|
5006
|
+
const status = execSync2("git status --porcelain", { encoding: "utf-8" }).trim();
|
|
4597
5007
|
if (status) {
|
|
4598
5008
|
const lines = status.split("\n").slice(0, 20);
|
|
4599
5009
|
parts.push(`
|
|
@@ -4603,7 +5013,7 @@ ${lines.join("\n")}`);
|
|
|
4603
5013
|
parts.push(`... and ${status.split("\n").length - 20} more`);
|
|
4604
5014
|
}
|
|
4605
5015
|
}
|
|
4606
|
-
const log =
|
|
5016
|
+
const log = execSync2("git log --oneline -3", { encoding: "utf-8" }).trim();
|
|
4607
5017
|
if (log) {
|
|
4608
5018
|
parts.push(`
|
|
4609
5019
|
Recent commits:
|
|
@@ -4619,7 +5029,7 @@ ${log}`);
|
|
|
4619
5029
|
import { Command as Command24 } from "commander";
|
|
4620
5030
|
import chalk25 from "chalk";
|
|
4621
5031
|
import { AuthManager as AuthManager23, TaskOperations as TaskOperations19 } from "@vibetasks/core";
|
|
4622
|
-
var logCommand = new Command24("log").description("Show change history for a task (like git log)").argument("[task]", "Task ID or short number").option("-n, --limit <number>", "Maximum number of changes to show", "20").option("--oneline", "Compact one-line format").option("--since <date>", "Show changes after date (YYYY-MM-DD)").option("--until <date>", "Show changes before date (YYYY-MM-DD)").option("--type <types>", "Filter by change type (comma-separated)").option("--grep <query>", "Search change messages (ignores task arg)").option("--session <id>", "Show changes from specific session").action(async (taskArg, options) => {
|
|
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) => {
|
|
4623
5033
|
try {
|
|
4624
5034
|
const authManager = new AuthManager23();
|
|
4625
5035
|
const taskOps = await TaskOperations19.fromAuthManager(authManager);
|
|
@@ -4644,19 +5054,19 @@ Found ${changes2.length} change(s) matching "${options.grep}":
|
|
|
4644
5054
|
console.error(chalk25.gray('Usage: vibetasks log <task-id> or vibetasks log --grep "query"'));
|
|
4645
5055
|
process.exit(1);
|
|
4646
5056
|
}
|
|
4647
|
-
let
|
|
5057
|
+
let taskId2 = taskArg;
|
|
4648
5058
|
if (/^\d+$/.test(taskArg)) {
|
|
4649
5059
|
const shortIdManager = new ShortIdManager();
|
|
4650
5060
|
const resolved = await shortIdManager.resolveId(taskArg);
|
|
4651
5061
|
if (resolved) {
|
|
4652
|
-
|
|
5062
|
+
taskId2 = resolved;
|
|
4653
5063
|
} else {
|
|
4654
5064
|
console.error(chalk25.red(`Task #${taskArg} not found`));
|
|
4655
5065
|
console.error(chalk25.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
4656
5066
|
process.exit(1);
|
|
4657
5067
|
}
|
|
4658
5068
|
}
|
|
4659
|
-
const task = await taskOps.getTask(
|
|
5069
|
+
const task = await taskOps.getTask(taskId2);
|
|
4660
5070
|
const queryOptions = {
|
|
4661
5071
|
limit: parseInt(options.limit, 10)
|
|
4662
5072
|
};
|
|
@@ -4669,7 +5079,32 @@ Found ${changes2.length} change(s) matching "${options.grep}":
|
|
|
4669
5079
|
if (options.type) {
|
|
4670
5080
|
queryOptions.changeTypes = options.type.split(",").map((t) => t.trim());
|
|
4671
5081
|
}
|
|
4672
|
-
|
|
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
|
+
}
|
|
4673
5108
|
if (changes.length === 0) {
|
|
4674
5109
|
console.log(chalk25.gray(`
|
|
4675
5110
|
No change history found for task: ${task.title}
|
|
@@ -4678,7 +5113,7 @@ No change history found for task: ${task.title}
|
|
|
4678
5113
|
}
|
|
4679
5114
|
console.log();
|
|
4680
5115
|
console.log(chalk25.cyan.bold(`History for: ${task.title}`));
|
|
4681
|
-
console.log(chalk25.gray(`Task ID: ${
|
|
5116
|
+
console.log(chalk25.gray(`Task ID: ${taskId2}`));
|
|
4682
5117
|
console.log(chalk25.gray(`Status: ${task.status} | Changes: ${changes.length}`));
|
|
4683
5118
|
console.log();
|
|
4684
5119
|
for (const change of changes) {
|
|
@@ -4718,15 +5153,41 @@ function printChange(change, oneline = false, taskTitle) {
|
|
|
4718
5153
|
handoff: chalk25.magenta
|
|
4719
5154
|
};
|
|
4720
5155
|
const colorFn = typeColors[change.change_type] || chalk25.white;
|
|
4721
|
-
|
|
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
|
+
}
|
|
4722
5173
|
if (oneline) {
|
|
4723
|
-
const
|
|
5174
|
+
const message2 = change.message || change.change_type;
|
|
4724
5175
|
console.log(
|
|
4725
|
-
`${chalk25.yellow(changeId)} ${chalk25.gray(dateStr)} ${colorFn(change.change_type.padEnd(16))} ${actorIcon} ${
|
|
5176
|
+
`${chalk25.yellow(changeId)} ${chalk25.gray(dateStr)} ${colorFn(change.change_type.padEnd(16))} ${actorIcon} ${message2}`
|
|
4726
5177
|
);
|
|
4727
5178
|
} else {
|
|
4728
5179
|
console.log(chalk25.yellow(`change ${change.id}`));
|
|
4729
|
-
|
|
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}`);
|
|
4730
5191
|
console.log(`Date: ${dateStr} ${timeStr}`);
|
|
4731
5192
|
if (change.session_id) {
|
|
4732
5193
|
console.log(chalk25.gray(`Session: ${change.session_id.substring(0, 8)}`));
|
|
@@ -4877,12 +5338,12 @@ Total: ${pausedTasks.length} paused task(s)
|
|
|
4877
5338
|
console.log(chalk27.gray(" vibetasks pause --list # List paused tasks\n"));
|
|
4878
5339
|
process.exit(1);
|
|
4879
5340
|
}
|
|
4880
|
-
let
|
|
5341
|
+
let taskId2 = taskArg;
|
|
4881
5342
|
if (/^\d+$/.test(taskArg)) {
|
|
4882
5343
|
const shortIdManager = new ShortIdManager();
|
|
4883
5344
|
const resolved = await shortIdManager.resolveId(taskArg);
|
|
4884
5345
|
if (resolved) {
|
|
4885
|
-
|
|
5346
|
+
taskId2 = resolved;
|
|
4886
5347
|
} else {
|
|
4887
5348
|
console.error(chalk27.red(`Task #${taskArg} not found`));
|
|
4888
5349
|
console.error(chalk27.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
@@ -4895,14 +5356,14 @@ Total: ${pausedTasks.length} paused task(s)
|
|
|
4895
5356
|
console.error(chalk27.red(`Task not found: ${taskArg}`));
|
|
4896
5357
|
process.exit(1);
|
|
4897
5358
|
}
|
|
4898
|
-
|
|
5359
|
+
taskId2 = matchingTask.id;
|
|
4899
5360
|
}
|
|
4900
|
-
const task = await taskOps.getTask(
|
|
5361
|
+
const task = await taskOps.getTask(taskId2);
|
|
4901
5362
|
if (task.status === "paused") {
|
|
4902
5363
|
console.log(chalk27.yellow(`
|
|
4903
5364
|
Task is already paused: ${task.title}
|
|
4904
5365
|
`));
|
|
4905
|
-
console.log(chalk27.gray("Resume with: vibetasks resume " +
|
|
5366
|
+
console.log(chalk27.gray("Resume with: vibetasks resume " + taskId2.slice(0, 8) + "\n"));
|
|
4906
5367
|
process.exit(0);
|
|
4907
5368
|
}
|
|
4908
5369
|
if (task.status === "done" || task.status === "archived") {
|
|
@@ -4912,7 +5373,7 @@ Cannot pause a ${task.status} task.
|
|
|
4912
5373
|
process.exit(1);
|
|
4913
5374
|
}
|
|
4914
5375
|
spinner.start("Pausing task...");
|
|
4915
|
-
const pausedTask = await taskOps.pauseTask(
|
|
5376
|
+
const pausedTask = await taskOps.pauseTask(taskId2, options.message);
|
|
4916
5377
|
spinner.succeed(chalk27.green("Task paused!"));
|
|
4917
5378
|
console.log();
|
|
4918
5379
|
console.log(chalk27.yellow("\u23F8 ") + chalk27.white(pausedTask.title));
|
|
@@ -4920,7 +5381,7 @@ Cannot pause a ${task.status} task.
|
|
|
4920
5381
|
console.log(chalk27.gray(` Reason: ${options.message}`));
|
|
4921
5382
|
}
|
|
4922
5383
|
console.log();
|
|
4923
|
-
console.log(chalk27.gray(`Resume with: vibetasks resume ${
|
|
5384
|
+
console.log(chalk27.gray(`Resume with: vibetasks resume ${taskId2.slice(0, 8)}`));
|
|
4924
5385
|
console.log(chalk27.gray(`View paused: vibetasks pause --list`));
|
|
4925
5386
|
console.log();
|
|
4926
5387
|
process.exit(0);
|
|
@@ -4956,10 +5417,10 @@ var resumeCommand = new Command27("resume").description("Resume a paused task (l
|
|
|
4956
5417
|
console.log(chalk28.gray("Pause a task with: vibetasks pause <task-id>\n"));
|
|
4957
5418
|
process.exit(0);
|
|
4958
5419
|
}
|
|
4959
|
-
let
|
|
5420
|
+
let taskId2;
|
|
4960
5421
|
if (!taskArg) {
|
|
4961
5422
|
if (pausedTasks.length === 1) {
|
|
4962
|
-
|
|
5423
|
+
taskId2 = pausedTasks[0].id;
|
|
4963
5424
|
console.log(chalk28.gray(`
|
|
4964
5425
|
Auto-selecting the only paused task...
|
|
4965
5426
|
`));
|
|
@@ -4973,15 +5434,15 @@ Auto-selecting the only paused task...
|
|
|
4973
5434
|
value: t.id
|
|
4974
5435
|
}))
|
|
4975
5436
|
}]);
|
|
4976
|
-
|
|
5437
|
+
taskId2 = selectedTask;
|
|
4977
5438
|
}
|
|
4978
5439
|
} else {
|
|
4979
|
-
|
|
5440
|
+
taskId2 = taskArg;
|
|
4980
5441
|
if (/^\d+$/.test(taskArg)) {
|
|
4981
5442
|
const shortIdManager = new ShortIdManager();
|
|
4982
5443
|
const resolved = await shortIdManager.resolveId(taskArg);
|
|
4983
5444
|
if (resolved) {
|
|
4984
|
-
|
|
5445
|
+
taskId2 = resolved;
|
|
4985
5446
|
} else {
|
|
4986
5447
|
console.error(chalk28.red(`Task #${taskArg} not found`));
|
|
4987
5448
|
console.error(chalk28.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
@@ -4993,21 +5454,21 @@ Auto-selecting the only paused task...
|
|
|
4993
5454
|
console.error(chalk28.red(`Task not found: ${taskArg}`));
|
|
4994
5455
|
process.exit(1);
|
|
4995
5456
|
}
|
|
4996
|
-
|
|
5457
|
+
taskId2 = matchingTask.id;
|
|
4997
5458
|
}
|
|
4998
5459
|
}
|
|
4999
|
-
const task = await taskOps.getTask(
|
|
5460
|
+
const task = await taskOps.getTask(taskId2);
|
|
5000
5461
|
if (task.status !== "paused") {
|
|
5001
5462
|
console.log(chalk28.yellow(`
|
|
5002
5463
|
Task is not paused (status: ${task.status}): ${task.title}
|
|
5003
5464
|
`));
|
|
5004
5465
|
if (task.status === "todo") {
|
|
5005
|
-
console.log(chalk28.gray("Start vibing with: vibetasks vibing " +
|
|
5466
|
+
console.log(chalk28.gray("Start vibing with: vibetasks vibing " + taskId2.slice(0, 8) + "\n"));
|
|
5006
5467
|
}
|
|
5007
5468
|
process.exit(0);
|
|
5008
5469
|
}
|
|
5009
5470
|
spinner.start("Resuming task...");
|
|
5010
|
-
const resumedTask = await taskOps.resumeTask(
|
|
5471
|
+
const resumedTask = await taskOps.resumeTask(taskId2);
|
|
5011
5472
|
spinner.succeed(chalk28.green("Task resumed!"));
|
|
5012
5473
|
console.log();
|
|
5013
5474
|
console.log(chalk28.magenta("\u26A1 ") + chalk28.white(resumedTask.title));
|
|
@@ -5016,8 +5477,8 @@ Task is not paused (status: ${task.status}): ${task.title}
|
|
|
5016
5477
|
console.log(chalk28.gray(` Context: ${task.context_notes.split("\n").pop()?.slice(0, 60)}...`));
|
|
5017
5478
|
}
|
|
5018
5479
|
console.log();
|
|
5019
|
-
console.log(chalk28.gray(`Complete with: vibetasks done ${
|
|
5020
|
-
console.log(chalk28.gray(`Pause again: vibetasks pause ${
|
|
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)}`));
|
|
5021
5482
|
console.log();
|
|
5022
5483
|
process.exit(0);
|
|
5023
5484
|
} catch (error) {
|
|
@@ -5046,13 +5507,13 @@ var revertCommand = new Command28("revert").description("Revert a specific chang
|
|
|
5046
5507
|
const authManager = new AuthManager27();
|
|
5047
5508
|
const taskOps = await TaskOperations23.fromAuthManager(authManager);
|
|
5048
5509
|
if (options.task || !changeId) {
|
|
5049
|
-
let
|
|
5510
|
+
let taskId2;
|
|
5050
5511
|
if (options.task) {
|
|
5051
5512
|
if (/^\d+$/.test(options.task)) {
|
|
5052
5513
|
const shortIdManager = new ShortIdManager();
|
|
5053
5514
|
const resolved = await shortIdManager.resolveId(options.task);
|
|
5054
5515
|
if (resolved) {
|
|
5055
|
-
|
|
5516
|
+
taskId2 = resolved;
|
|
5056
5517
|
} else {
|
|
5057
5518
|
console.error(chalk29.red(`Task #${options.task} not found`));
|
|
5058
5519
|
process.exit(1);
|
|
@@ -5061,21 +5522,21 @@ var revertCommand = new Command28("revert").description("Revert a specific chang
|
|
|
5061
5522
|
const allTasks = await taskOps.getTasks("all");
|
|
5062
5523
|
const matchingTask = allTasks.find((t) => t.id.startsWith(options.task));
|
|
5063
5524
|
if (matchingTask) {
|
|
5064
|
-
|
|
5525
|
+
taskId2 = matchingTask.id;
|
|
5065
5526
|
}
|
|
5066
5527
|
} else {
|
|
5067
|
-
|
|
5528
|
+
taskId2 = options.task;
|
|
5068
5529
|
}
|
|
5069
5530
|
}
|
|
5070
|
-
if (!
|
|
5531
|
+
if (!taskId2) {
|
|
5071
5532
|
console.log(chalk29.yellow("\nUsage:"));
|
|
5072
5533
|
console.log(chalk29.gray(" vibetasks revert <change-id> # Revert specific change"));
|
|
5073
5534
|
console.log(chalk29.gray(" vibetasks revert --task <task-id> # Pick from task changes"));
|
|
5074
5535
|
console.log(chalk29.gray(" vibetasks log <task-id> # View changes first\n"));
|
|
5075
5536
|
process.exit(1);
|
|
5076
5537
|
}
|
|
5077
|
-
const changes = await taskOps.getTaskChanges(
|
|
5078
|
-
const task2 = await taskOps.getTask(
|
|
5538
|
+
const changes = await taskOps.getTaskChanges(taskId2, { limit: 20 });
|
|
5539
|
+
const task2 = await taskOps.getTask(taskId2);
|
|
5079
5540
|
if (changes.length === 0) {
|
|
5080
5541
|
console.log(chalk29.yellow(`
|
|
5081
5542
|
No changes found for task: ${task2.title}
|
|
@@ -5200,12 +5661,12 @@ var blameCommand = new Command29("blame").description("Show who changed what on
|
|
|
5200
5661
|
try {
|
|
5201
5662
|
const authManager = new AuthManager28();
|
|
5202
5663
|
const taskOps = await TaskOperations24.fromAuthManager(authManager);
|
|
5203
|
-
let
|
|
5664
|
+
let taskId2 = taskArg;
|
|
5204
5665
|
if (/^\d+$/.test(taskArg)) {
|
|
5205
5666
|
const shortIdManager = new ShortIdManager();
|
|
5206
5667
|
const resolved = await shortIdManager.resolveId(taskArg);
|
|
5207
5668
|
if (resolved) {
|
|
5208
|
-
|
|
5669
|
+
taskId2 = resolved;
|
|
5209
5670
|
} else {
|
|
5210
5671
|
console.error(chalk30.red(`Task #${taskArg} not found`));
|
|
5211
5672
|
console.error(chalk30.gray("Run: vibetasks list --short-ids to see available tasks"));
|
|
@@ -5218,10 +5679,10 @@ var blameCommand = new Command29("blame").description("Show who changed what on
|
|
|
5218
5679
|
console.error(chalk30.red(`Task not found: ${taskArg}`));
|
|
5219
5680
|
process.exit(1);
|
|
5220
5681
|
}
|
|
5221
|
-
|
|
5682
|
+
taskId2 = matchingTask.id;
|
|
5222
5683
|
}
|
|
5223
|
-
const task = await taskOps.getTask(
|
|
5224
|
-
const changes = await taskOps.getTaskChanges(
|
|
5684
|
+
const task = await taskOps.getTask(taskId2);
|
|
5685
|
+
const changes = await taskOps.getTaskChanges(taskId2, { limit: 100 });
|
|
5225
5686
|
if (changes.length === 0) {
|
|
5226
5687
|
console.log(chalk30.yellow(`
|
|
5227
5688
|
No change history for task: ${task.title}
|
|
@@ -5257,7 +5718,7 @@ No change history for task: ${task.title}
|
|
|
5257
5718
|
}
|
|
5258
5719
|
console.log();
|
|
5259
5720
|
console.log(chalk30.cyan.bold(`Blame for: ${task.title}`));
|
|
5260
|
-
console.log(chalk30.gray(`Task ID: ${
|
|
5721
|
+
console.log(chalk30.gray(`Task ID: ${taskId2}`));
|
|
5261
5722
|
console.log();
|
|
5262
5723
|
const fields = options.field ? [options.field] : Object.keys(blameMap).sort();
|
|
5263
5724
|
if (fields.length === 0) {
|
|
@@ -5300,7 +5761,7 @@ No change history for task: ${task.title}
|
|
|
5300
5761
|
console.log(chalk30.gray("\u2500".repeat(50)));
|
|
5301
5762
|
console.log(chalk30.gray(`Total changes: ${totalChanges} | \u{1F916} AI: ${aiChanges} | \u{1F464} Human: ${humanChanges}`));
|
|
5302
5763
|
console.log(chalk30.gray(`
|
|
5303
|
-
Full history: vibetasks log ${
|
|
5764
|
+
Full history: vibetasks log ${taskId2.slice(0, 8)}`));
|
|
5304
5765
|
console.log();
|
|
5305
5766
|
process.exit(0);
|
|
5306
5767
|
} catch (error) {
|
|
@@ -5326,10 +5787,1068 @@ function formatValue3(value) {
|
|
|
5326
5787
|
return str.length > 60 ? str.slice(0, 57) + "..." : str;
|
|
5327
5788
|
}
|
|
5328
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
|
+
|
|
5329
6848
|
// bin/vibetasks.ts
|
|
5330
6849
|
var require2 = createRequire(import.meta.url);
|
|
5331
6850
|
var pkg = require2("../../package.json");
|
|
5332
|
-
var program = new
|
|
6851
|
+
var program = new Command37();
|
|
5333
6852
|
program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version(pkg.version);
|
|
5334
6853
|
program.addCommand(setupCommand);
|
|
5335
6854
|
program.addCommand(loginCommand);
|
|
@@ -5361,4 +6880,11 @@ program.addCommand(pauseCommand);
|
|
|
5361
6880
|
program.addCommand(resumeCommand);
|
|
5362
6881
|
program.addCommand(revertCommand);
|
|
5363
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);
|
|
5364
6890
|
program.parse();
|