@vibetasks/cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/vibetasks.js +729 -114
- package/package.json +2 -2
package/dist/vibetasks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// bin/vibetasks.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command13 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/login.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -721,59 +721,131 @@ Error: ${error.message}
|
|
|
721
721
|
}
|
|
722
722
|
});
|
|
723
723
|
|
|
724
|
-
// src/commands/
|
|
724
|
+
// src/commands/vibing.ts
|
|
725
725
|
import { Command as Command5 } from "commander";
|
|
726
726
|
import chalk6 from "chalk";
|
|
727
|
-
import
|
|
727
|
+
import ora5 from "ora";
|
|
728
|
+
import inquirer2 from "inquirer";
|
|
728
729
|
import { AuthManager as AuthManager6, TaskOperations as TaskOperations4 } from "@vibetasks/core";
|
|
729
|
-
var
|
|
730
|
+
var WIP_LIMIT = 3;
|
|
731
|
+
var vibingCommand = new Command5("vibing").alias("start").alias("v").description("Start working on a task (move to vibing status)").argument("[task-id]", "Task ID to start vibing on").option("-p, --pick", "Pick from todo tasks interactively").action(async (taskId, options) => {
|
|
732
|
+
const spinner = ora5();
|
|
730
733
|
try {
|
|
731
734
|
const authManager = new AuthManager6();
|
|
732
735
|
const taskOps = await TaskOperations4.fromAuthManager(authManager);
|
|
736
|
+
const allTasks = await taskOps.getTasks("all");
|
|
737
|
+
const vibingTasks = allTasks.filter((t) => t.status === "vibing" && !t.completed);
|
|
738
|
+
if (vibingTasks.length >= WIP_LIMIT) {
|
|
739
|
+
console.log(chalk6.yellow("\n\u26A0 WIP Limit Warning"));
|
|
740
|
+
console.log(chalk6.gray(` You already have ${vibingTasks.length} tasks in progress.`));
|
|
741
|
+
console.log(chalk6.gray(" Research shows 3+ concurrent tasks = 40% productivity loss.\n"));
|
|
742
|
+
console.log(chalk6.white("Currently vibing:"));
|
|
743
|
+
vibingTasks.forEach((t, i) => {
|
|
744
|
+
console.log(chalk6.magenta(` ${i + 1}. ${t.title}`));
|
|
745
|
+
});
|
|
746
|
+
console.log("");
|
|
747
|
+
const { proceed } = await inquirer2.prompt([{
|
|
748
|
+
type: "confirm",
|
|
749
|
+
name: "proceed",
|
|
750
|
+
message: "Start another task anyway?",
|
|
751
|
+
default: false
|
|
752
|
+
}]);
|
|
753
|
+
if (!proceed) {
|
|
754
|
+
console.log(chalk6.gray("\nFocus on finishing what you started! Run `vibetasks done <id>` when ready.\n"));
|
|
755
|
+
process.exit(0);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (!taskId || options.pick) {
|
|
759
|
+
const todoTasks = allTasks.filter((t) => t.status === "todo" && !t.completed);
|
|
760
|
+
if (todoTasks.length === 0) {
|
|
761
|
+
console.log(chalk6.yellow('\nNo todo tasks found. Add one with `vibetasks add "task title"`\n'));
|
|
762
|
+
process.exit(0);
|
|
763
|
+
}
|
|
764
|
+
const { selectedTask } = await inquirer2.prompt([{
|
|
765
|
+
type: "list",
|
|
766
|
+
name: "selectedTask",
|
|
767
|
+
message: "Pick a task to start:",
|
|
768
|
+
choices: todoTasks.map((t) => ({
|
|
769
|
+
name: `${t.title}${t.project_tag ? chalk6.gray(` [${t.project_tag}]`) : ""}`,
|
|
770
|
+
value: t.id
|
|
771
|
+
}))
|
|
772
|
+
}]);
|
|
773
|
+
taskId = selectedTask;
|
|
774
|
+
}
|
|
775
|
+
spinner.start("Starting task...");
|
|
776
|
+
await taskOps.updateTask(taskId, {
|
|
777
|
+
status: "vibing",
|
|
778
|
+
completed: false
|
|
779
|
+
});
|
|
780
|
+
const task = allTasks.find((t) => t.id === taskId);
|
|
781
|
+
spinner.succeed(chalk6.green("Now vibing on:"));
|
|
782
|
+
console.log(chalk6.white(`
|
|
783
|
+
${task?.title || taskId}
|
|
784
|
+
`));
|
|
785
|
+
console.log(chalk6.gray(" Tips:"));
|
|
786
|
+
console.log(chalk6.gray(" \u2022 Update context notes as you work"));
|
|
787
|
+
console.log(chalk6.gray(" \u2022 Run `vibetasks done " + taskId.slice(0, 8) + "` when finished"));
|
|
788
|
+
console.log(chalk6.gray(' \u2022 In Claude Code, say "show my vibing tasks"\n'));
|
|
789
|
+
} catch (error) {
|
|
790
|
+
spinner.fail(chalk6.red("Failed to start task"));
|
|
791
|
+
console.error(chalk6.red(error.message));
|
|
792
|
+
process.exit(1);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// src/commands/search.ts
|
|
797
|
+
import { Command as Command6 } from "commander";
|
|
798
|
+
import chalk7 from "chalk";
|
|
799
|
+
import Table2 from "cli-table3";
|
|
800
|
+
import { AuthManager as AuthManager7, TaskOperations as TaskOperations5 } from "@vibetasks/core";
|
|
801
|
+
var searchCommand = new Command6("search").description("Search tasks by title").argument("<query>", "Search query").option("-l, --limit <number>", "Maximum number of results", "20").action(async (query, options) => {
|
|
802
|
+
try {
|
|
803
|
+
const authManager = new AuthManager7();
|
|
804
|
+
const taskOps = await TaskOperations5.fromAuthManager(authManager);
|
|
733
805
|
const limit = parseInt(options.limit, 10);
|
|
734
806
|
const tasks = await taskOps.searchTasks(query, limit);
|
|
735
807
|
if (tasks.length === 0) {
|
|
736
|
-
console.log(
|
|
808
|
+
console.log(chalk7.gray(`
|
|
737
809
|
No tasks found matching: "${query}"
|
|
738
810
|
`));
|
|
739
811
|
process.exit(0);
|
|
740
812
|
}
|
|
741
813
|
const table = new Table2({
|
|
742
814
|
head: [
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
815
|
+
chalk7.cyan("ID"),
|
|
816
|
+
chalk7.cyan("Title"),
|
|
817
|
+
chalk7.cyan("Priority"),
|
|
818
|
+
chalk7.cyan("Due Date")
|
|
747
819
|
],
|
|
748
820
|
colWidths: [12, 50, 10, 12],
|
|
749
821
|
wordWrap: true
|
|
750
822
|
});
|
|
751
823
|
tasks.forEach((task) => {
|
|
752
824
|
const priorityColors = {
|
|
753
|
-
high:
|
|
754
|
-
medium:
|
|
755
|
-
low:
|
|
756
|
-
none:
|
|
825
|
+
high: chalk7.red,
|
|
826
|
+
medium: chalk7.yellow,
|
|
827
|
+
low: chalk7.blue,
|
|
828
|
+
none: chalk7.gray
|
|
757
829
|
};
|
|
758
830
|
const priorityColor = priorityColors[task.priority || "none"];
|
|
759
831
|
table.push([
|
|
760
832
|
task.id.substring(0, 8),
|
|
761
833
|
task.title,
|
|
762
834
|
priorityColor(task.priority || "none"),
|
|
763
|
-
task.due_date ? task.due_date.split("T")[0] :
|
|
835
|
+
task.due_date ? task.due_date.split("T")[0] : chalk7.gray("-")
|
|
764
836
|
]);
|
|
765
837
|
});
|
|
766
838
|
console.log("\n" + table.toString());
|
|
767
|
-
console.log(
|
|
839
|
+
console.log(chalk7.gray(`
|
|
768
840
|
Found ${tasks.length} task${tasks.length === 1 ? "" : "s"} matching "${query}"
|
|
769
841
|
`));
|
|
770
842
|
process.exit(0);
|
|
771
843
|
} catch (error) {
|
|
772
844
|
if (error.message.includes("Not authenticated")) {
|
|
773
|
-
console.error(
|
|
774
|
-
console.error(
|
|
845
|
+
console.error(chalk7.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
846
|
+
console.error(chalk7.gray("Run: taskflow login\n"));
|
|
775
847
|
} else {
|
|
776
|
-
console.error(
|
|
848
|
+
console.error(chalk7.red(`
|
|
777
849
|
Error: ${error.message}
|
|
778
850
|
`));
|
|
779
851
|
}
|
|
@@ -782,27 +854,27 @@ Error: ${error.message}
|
|
|
782
854
|
});
|
|
783
855
|
|
|
784
856
|
// src/commands/update.ts
|
|
785
|
-
import { Command as
|
|
786
|
-
import
|
|
787
|
-
import
|
|
788
|
-
import { AuthManager as
|
|
789
|
-
var updateCommand = new
|
|
857
|
+
import { Command as Command7 } from "commander";
|
|
858
|
+
import ora6 from "ora";
|
|
859
|
+
import chalk8 from "chalk";
|
|
860
|
+
import { AuthManager as AuthManager8, TaskOperations as TaskOperations6 } from "@vibetasks/core";
|
|
861
|
+
var updateCommand = new Command7("update").description("Update a task").argument("<id>", "Task ID (full or first 8 characters)").option("-t, --title <title>", "New title").option("-n, --notes <notes>", "New notes").option("-d, --due <date>", "New due date").option("-p, --priority <level>", "New priority: low, medium, high, none").option("-s, --status <status>", "New status: todo, vibing, done").option("--project <name>", "New project tag").option("-e, --energy <level>", "Energy required: low, medium, high").option("-c, --context <notes>", "Update context notes").action(async (id, options) => {
|
|
790
862
|
if (!options.title && !options.notes && !options.due && !options.priority && !options.status && !options.project && !options.energy && !options.context) {
|
|
791
|
-
console.error(
|
|
792
|
-
console.error(
|
|
863
|
+
console.error(chalk8.red("\nError: No updates specified"));
|
|
864
|
+
console.error(chalk8.gray("Provide at least one option: --title, --notes, --due, --priority, --status, --project, --energy, or --context\n"));
|
|
793
865
|
process.exit(1);
|
|
794
866
|
}
|
|
795
|
-
const spinner =
|
|
867
|
+
const spinner = ora6("Updating task...").start();
|
|
796
868
|
try {
|
|
797
|
-
const authManager = new
|
|
798
|
-
const taskOps = await
|
|
869
|
+
const authManager = new AuthManager8();
|
|
870
|
+
const taskOps = await TaskOperations6.fromAuthManager(authManager);
|
|
799
871
|
let taskId = id;
|
|
800
872
|
if (id.length < 32) {
|
|
801
873
|
const allTasks = await taskOps.getTasks("all");
|
|
802
874
|
const matchingTask = allTasks.find((t) => t.id.startsWith(id));
|
|
803
875
|
if (!matchingTask) {
|
|
804
|
-
spinner.fail(
|
|
805
|
-
console.error(
|
|
876
|
+
spinner.fail(chalk8.red("Task not found"));
|
|
877
|
+
console.error(chalk8.gray(`
|
|
806
878
|
No task found with ID starting with: ${id}
|
|
807
879
|
`));
|
|
808
880
|
process.exit(1);
|
|
@@ -837,18 +909,18 @@ No task found with ID starting with: ${id}
|
|
|
837
909
|
updates.energy_required = options.energy;
|
|
838
910
|
}
|
|
839
911
|
const task = await taskOps.updateTask(taskId, updates);
|
|
840
|
-
spinner.succeed(
|
|
841
|
-
console.log(
|
|
912
|
+
spinner.succeed(chalk8.green("Task updated!"));
|
|
913
|
+
console.log(chalk8.gray(`
|
|
842
914
|
\u2713 ${task.title}
|
|
843
915
|
`));
|
|
844
916
|
process.exit(0);
|
|
845
917
|
} catch (error) {
|
|
846
|
-
spinner.fail(
|
|
918
|
+
spinner.fail(chalk8.red("Failed to update task"));
|
|
847
919
|
if (error.message.includes("Not authenticated")) {
|
|
848
|
-
console.error(
|
|
849
|
-
console.error(
|
|
920
|
+
console.error(chalk8.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
921
|
+
console.error(chalk8.gray("Run: taskflow login\n"));
|
|
850
922
|
} else {
|
|
851
|
-
console.error(
|
|
923
|
+
console.error(chalk8.red(`
|
|
852
924
|
Error: ${error.message}
|
|
853
925
|
`));
|
|
854
926
|
}
|
|
@@ -857,23 +929,23 @@ Error: ${error.message}
|
|
|
857
929
|
});
|
|
858
930
|
|
|
859
931
|
// src/commands/delete.ts
|
|
860
|
-
import { Command as
|
|
861
|
-
import
|
|
862
|
-
import
|
|
863
|
-
import
|
|
864
|
-
import { AuthManager as
|
|
865
|
-
var deleteCommand = new
|
|
932
|
+
import { Command as Command8 } from "commander";
|
|
933
|
+
import ora7 from "ora";
|
|
934
|
+
import chalk9 from "chalk";
|
|
935
|
+
import inquirer3 from "inquirer";
|
|
936
|
+
import { AuthManager as AuthManager9, TaskOperations as TaskOperations7 } from "@vibetasks/core";
|
|
937
|
+
var deleteCommand = new Command8("delete").description("Delete a task").argument("<id>", "Task ID (full or first 8 characters)").option("-y, --yes", "Skip confirmation").action(async (id, options) => {
|
|
866
938
|
try {
|
|
867
|
-
const authManager = new
|
|
868
|
-
const taskOps = await
|
|
939
|
+
const authManager = new AuthManager9();
|
|
940
|
+
const taskOps = await TaskOperations7.fromAuthManager(authManager);
|
|
869
941
|
let taskId = id;
|
|
870
942
|
let taskTitle = "";
|
|
871
943
|
if (id.length < 32) {
|
|
872
944
|
const allTasks = await taskOps.getTasks("all");
|
|
873
945
|
const matchingTask = allTasks.find((t) => t.id.startsWith(id));
|
|
874
946
|
if (!matchingTask) {
|
|
875
|
-
console.error(
|
|
876
|
-
console.error(
|
|
947
|
+
console.error(chalk9.red("\nTask not found"));
|
|
948
|
+
console.error(chalk9.gray(`No task found with ID starting with: ${id}
|
|
877
949
|
`));
|
|
878
950
|
process.exit(1);
|
|
879
951
|
}
|
|
@@ -884,7 +956,7 @@ var deleteCommand = new Command7("delete").description("Delete a task").argument
|
|
|
884
956
|
taskTitle = task.title;
|
|
885
957
|
}
|
|
886
958
|
if (!options.yes) {
|
|
887
|
-
const answers = await
|
|
959
|
+
const answers = await inquirer3.prompt([
|
|
888
960
|
{
|
|
889
961
|
type: "confirm",
|
|
890
962
|
name: "confirm",
|
|
@@ -893,24 +965,24 @@ var deleteCommand = new Command7("delete").description("Delete a task").argument
|
|
|
893
965
|
}
|
|
894
966
|
]);
|
|
895
967
|
if (!answers.confirm) {
|
|
896
|
-
console.log(
|
|
968
|
+
console.log(chalk9.gray("\nDeletion cancelled.\n"));
|
|
897
969
|
process.exit(0);
|
|
898
970
|
}
|
|
899
971
|
}
|
|
900
|
-
const spinner =
|
|
972
|
+
const spinner = ora7("Deleting task...").start();
|
|
901
973
|
await taskOps.deleteTask(taskId);
|
|
902
|
-
spinner.succeed(
|
|
903
|
-
console.log(
|
|
974
|
+
spinner.succeed(chalk9.green("Task deleted!"));
|
|
975
|
+
console.log(chalk9.gray(`
|
|
904
976
|
\u2717 ${taskTitle}
|
|
905
977
|
`));
|
|
906
978
|
process.exit(0);
|
|
907
979
|
} catch (error) {
|
|
908
|
-
console.error(
|
|
980
|
+
console.error(chalk9.red("\nFailed to delete task"));
|
|
909
981
|
if (error.message.includes("Not authenticated")) {
|
|
910
|
-
console.error(
|
|
911
|
-
console.error(
|
|
982
|
+
console.error(chalk9.yellow("\n\u26A0\uFE0F You need to login first"));
|
|
983
|
+
console.error(chalk9.gray("Run: taskflow login\n"));
|
|
912
984
|
} else {
|
|
913
|
-
console.error(
|
|
985
|
+
console.error(chalk9.red(`Error: ${error.message}
|
|
914
986
|
`));
|
|
915
987
|
}
|
|
916
988
|
process.exit(1);
|
|
@@ -918,28 +990,28 @@ var deleteCommand = new Command7("delete").description("Delete a task").argument
|
|
|
918
990
|
});
|
|
919
991
|
|
|
920
992
|
// src/commands/config.ts
|
|
921
|
-
import { Command as
|
|
922
|
-
import
|
|
923
|
-
import { AuthManager as
|
|
924
|
-
var configCommand = new
|
|
993
|
+
import { Command as Command9 } from "commander";
|
|
994
|
+
import chalk10 from "chalk";
|
|
995
|
+
import { AuthManager as AuthManager10 } from "@vibetasks/core";
|
|
996
|
+
var configCommand = new Command9("config").description("View or set configuration").argument("[key]", "Configuration key").argument("[value]", "Configuration value").action(async (key, value) => {
|
|
925
997
|
try {
|
|
926
|
-
const authManager = new
|
|
998
|
+
const authManager = new AuthManager10();
|
|
927
999
|
if (!key) {
|
|
928
1000
|
const configManager = authManager["configManager"];
|
|
929
1001
|
const config = await configManager.getConfig();
|
|
930
1002
|
const configPath = configManager.getConfigPath();
|
|
931
|
-
console.log(
|
|
932
|
-
console.log(
|
|
1003
|
+
console.log(chalk10.blue.bold("\n\u{1F4CB} TaskFlow Configuration\n"));
|
|
1004
|
+
console.log(chalk10.gray(`Location: ${configPath}
|
|
933
1005
|
`));
|
|
934
1006
|
if (Object.keys(config).length === 0) {
|
|
935
|
-
console.log(
|
|
1007
|
+
console.log(chalk10.gray("No configuration set.\n"));
|
|
936
1008
|
process.exit(0);
|
|
937
1009
|
}
|
|
938
1010
|
Object.entries(config).forEach(([k, v]) => {
|
|
939
1011
|
if (k.includes("token") || k.includes("key")) {
|
|
940
|
-
console.log(`${
|
|
1012
|
+
console.log(`${chalk10.cyan(k)}: ${chalk10.gray("[hidden]")}`);
|
|
941
1013
|
} else {
|
|
942
|
-
console.log(`${
|
|
1014
|
+
console.log(`${chalk10.cyan(k)}: ${chalk10.white(String(v))}`);
|
|
943
1015
|
}
|
|
944
1016
|
});
|
|
945
1017
|
console.log();
|
|
@@ -948,16 +1020,16 @@ var configCommand = new Command8("config").description("View or set configuratio
|
|
|
948
1020
|
if (!value) {
|
|
949
1021
|
const val = await authManager.getConfig(key);
|
|
950
1022
|
if (val === void 0) {
|
|
951
|
-
console.log(
|
|
1023
|
+
console.log(chalk10.gray(`
|
|
952
1024
|
Configuration key "${key}" not set.
|
|
953
1025
|
`));
|
|
954
1026
|
} else {
|
|
955
1027
|
if (key.includes("token") || key.includes("key")) {
|
|
956
|
-
console.log(
|
|
1028
|
+
console.log(chalk10.gray(`
|
|
957
1029
|
${key}: [hidden]
|
|
958
1030
|
`));
|
|
959
1031
|
} else {
|
|
960
|
-
console.log(
|
|
1032
|
+
console.log(chalk10.white(`
|
|
961
1033
|
${key}: ${val}
|
|
962
1034
|
`));
|
|
963
1035
|
}
|
|
@@ -965,12 +1037,12 @@ ${key}: ${val}
|
|
|
965
1037
|
process.exit(0);
|
|
966
1038
|
}
|
|
967
1039
|
await authManager.setConfig(key, value);
|
|
968
|
-
console.log(
|
|
1040
|
+
console.log(chalk10.green(`
|
|
969
1041
|
\u2713 Configuration updated: ${key} = ${value}
|
|
970
1042
|
`));
|
|
971
1043
|
process.exit(0);
|
|
972
1044
|
} catch (error) {
|
|
973
|
-
console.error(
|
|
1045
|
+
console.error(chalk10.red(`
|
|
974
1046
|
Error: ${error.message}
|
|
975
1047
|
`));
|
|
976
1048
|
process.exit(1);
|
|
@@ -978,20 +1050,20 @@ Error: ${error.message}
|
|
|
978
1050
|
});
|
|
979
1051
|
|
|
980
1052
|
// src/commands/hooks.ts
|
|
981
|
-
import { Command as
|
|
982
|
-
import
|
|
983
|
-
import
|
|
1053
|
+
import { Command as Command10 } from "commander";
|
|
1054
|
+
import chalk11 from "chalk";
|
|
1055
|
+
import ora8 from "ora";
|
|
984
1056
|
import fs from "fs/promises";
|
|
985
1057
|
import path from "path";
|
|
986
|
-
var hooksCommand = new
|
|
987
|
-
const spinner =
|
|
1058
|
+
var hooksCommand = new Command10("hooks").description("Manage git hooks integration").command("install").description("Install git hooks in current repository").action(async () => {
|
|
1059
|
+
const spinner = ora8("Installing git hooks...").start();
|
|
988
1060
|
try {
|
|
989
1061
|
const gitDir = path.join(process.cwd(), ".git");
|
|
990
1062
|
try {
|
|
991
1063
|
await fs.access(gitDir);
|
|
992
1064
|
} catch {
|
|
993
|
-
spinner.fail(
|
|
994
|
-
console.error(
|
|
1065
|
+
spinner.fail(chalk11.red("Not a git repository"));
|
|
1066
|
+
console.error(chalk11.gray("\nRun this command from the root of a git repository.\n"));
|
|
995
1067
|
process.exit(1);
|
|
996
1068
|
}
|
|
997
1069
|
const hooksDir = path.join(gitDir, "hooks");
|
|
@@ -1027,19 +1099,19 @@ fi
|
|
|
1027
1099
|
exit 0
|
|
1028
1100
|
`;
|
|
1029
1101
|
await fs.writeFile(postCommitPath, hookScript, { mode: 493 });
|
|
1030
|
-
spinner.succeed(
|
|
1031
|
-
console.log(
|
|
1032
|
-
console.log(
|
|
1033
|
-
console.log(
|
|
1034
|
-
console.log(
|
|
1035
|
-
console.log(
|
|
1036
|
-
console.log(
|
|
1037
|
-
console.log(
|
|
1102
|
+
spinner.succeed(chalk11.green("Git hooks installed!"));
|
|
1103
|
+
console.log(chalk11.gray("\n\u{1F4CC} Post-commit hook created"));
|
|
1104
|
+
console.log(chalk11.gray("Location:"), postCommitPath);
|
|
1105
|
+
console.log(chalk11.blue.bold("\n\u2728 How to use:\n"));
|
|
1106
|
+
console.log(chalk11.white("1. Link commits to tasks:"));
|
|
1107
|
+
console.log(chalk11.gray(' git commit -m "feat: Add login [TASK-abc123]"'));
|
|
1108
|
+
console.log(chalk11.white("\n2. Auto-complete tasks:"));
|
|
1109
|
+
console.log(chalk11.gray(' git commit -m "feat: Complete feature [TASK-abc123] [COMPLETE]"'));
|
|
1038
1110
|
console.log();
|
|
1039
1111
|
process.exit(0);
|
|
1040
1112
|
} catch (error) {
|
|
1041
|
-
spinner.fail(
|
|
1042
|
-
console.error(
|
|
1113
|
+
spinner.fail(chalk11.red("Failed to install hooks"));
|
|
1114
|
+
console.error(chalk11.red(`
|
|
1043
1115
|
Error: ${error.message}
|
|
1044
1116
|
`));
|
|
1045
1117
|
process.exit(1);
|
|
@@ -1047,16 +1119,16 @@ Error: ${error.message}
|
|
|
1047
1119
|
});
|
|
1048
1120
|
|
|
1049
1121
|
// src/commands/init.ts
|
|
1050
|
-
import { Command as
|
|
1051
|
-
import
|
|
1052
|
-
import
|
|
1053
|
-
import
|
|
1054
|
-
import { AuthManager as
|
|
1122
|
+
import { Command as Command11 } from "commander";
|
|
1123
|
+
import inquirer4 from "inquirer";
|
|
1124
|
+
import ora9 from "ora";
|
|
1125
|
+
import chalk12 from "chalk";
|
|
1126
|
+
import { AuthManager as AuthManager11 } from "@vibetasks/core";
|
|
1055
1127
|
import { detectProject as detectProject2 } from "@vibetasks/shared/utils/project-detector";
|
|
1056
|
-
var initCommand = new
|
|
1057
|
-
const spinner =
|
|
1128
|
+
var initCommand = new Command11("init").description("Initialize TaskFlow for current project (auto-detect from git)").option("--name <name>", "Manually specify project name").action(async (options) => {
|
|
1129
|
+
const spinner = ora9("Detecting project...").start();
|
|
1058
1130
|
try {
|
|
1059
|
-
const authManager = new
|
|
1131
|
+
const authManager = new AuthManager11();
|
|
1060
1132
|
const cwd = process.cwd();
|
|
1061
1133
|
let project;
|
|
1062
1134
|
if (options.name) {
|
|
@@ -1065,17 +1137,17 @@ var initCommand = new Command10("init").description("Initialize TaskFlow for cur
|
|
|
1065
1137
|
source: "manual",
|
|
1066
1138
|
path: cwd
|
|
1067
1139
|
};
|
|
1068
|
-
spinner.succeed(
|
|
1140
|
+
spinner.succeed(chalk12.green("Using manual project name"));
|
|
1069
1141
|
} else {
|
|
1070
1142
|
project = await detectProject2(cwd);
|
|
1071
|
-
spinner.succeed(
|
|
1143
|
+
spinner.succeed(chalk12.green("Project detected!"));
|
|
1072
1144
|
}
|
|
1073
|
-
console.log(
|
|
1074
|
-
console.log(
|
|
1075
|
-
console.log(
|
|
1076
|
-
console.log(
|
|
1145
|
+
console.log(chalk12.white("\n\u{1F4C1} Project Info:"));
|
|
1146
|
+
console.log(chalk12.gray(` Name: ${chalk12.white(project.name)}`));
|
|
1147
|
+
console.log(chalk12.gray(` Source: ${chalk12.white(project.source)}`));
|
|
1148
|
+
console.log(chalk12.gray(` Path: ${chalk12.white(project.path)}
|
|
1077
1149
|
`));
|
|
1078
|
-
const answers = await
|
|
1150
|
+
const answers = await inquirer4.prompt([
|
|
1079
1151
|
{
|
|
1080
1152
|
type: "confirm",
|
|
1081
1153
|
name: "confirm",
|
|
@@ -1084,38 +1156,581 @@ var initCommand = new Command10("init").description("Initialize TaskFlow for cur
|
|
|
1084
1156
|
}
|
|
1085
1157
|
]);
|
|
1086
1158
|
if (!answers.confirm) {
|
|
1087
|
-
console.log(
|
|
1159
|
+
console.log(chalk12.gray("\nCancelled. Tasks will use default project.\n"));
|
|
1088
1160
|
process.exit(0);
|
|
1089
1161
|
}
|
|
1090
1162
|
await authManager.setConfig(`project_${cwd}`, project.name);
|
|
1091
|
-
console.log(
|
|
1163
|
+
console.log(chalk12.green("\n\u2713 Project initialized!"));
|
|
1092
1164
|
console.log(
|
|
1093
|
-
|
|
1165
|
+
chalk12.gray(
|
|
1094
1166
|
`
|
|
1095
|
-
All tasks created from ${
|
|
1167
|
+
All tasks created from ${chalk12.white(cwd)} will be tagged as ${chalk12.white(project.name)}
|
|
1096
1168
|
`
|
|
1097
1169
|
)
|
|
1098
1170
|
);
|
|
1099
|
-
console.log(
|
|
1100
|
-
console.log(
|
|
1101
|
-
console.log(
|
|
1102
|
-
console.log(
|
|
1171
|
+
console.log(chalk12.blue("Next steps:"));
|
|
1172
|
+
console.log(chalk12.gray(' \u2022 Run taskflow add "Your task" to create tasks'));
|
|
1173
|
+
console.log(chalk12.gray(" \u2022 Tasks will automatically be tagged with this project"));
|
|
1174
|
+
console.log(chalk12.gray(" \u2022 Use taskflow list --project to filter by project\n"));
|
|
1103
1175
|
process.exit(0);
|
|
1104
1176
|
} catch (error) {
|
|
1105
|
-
spinner.fail(
|
|
1106
|
-
console.error(
|
|
1177
|
+
spinner.fail(chalk12.red("Failed to initialize project"));
|
|
1178
|
+
console.error(chalk12.red(`
|
|
1107
1179
|
Error: ${error.message}
|
|
1108
1180
|
`));
|
|
1109
1181
|
process.exit(1);
|
|
1110
1182
|
}
|
|
1111
1183
|
});
|
|
1112
1184
|
|
|
1185
|
+
// src/commands/setup.ts
|
|
1186
|
+
import { Command as Command12 } from "commander";
|
|
1187
|
+
import inquirer5 from "inquirer";
|
|
1188
|
+
import ora10 from "ora";
|
|
1189
|
+
import chalk13 from "chalk";
|
|
1190
|
+
import fs2 from "fs/promises";
|
|
1191
|
+
import path2 from "path";
|
|
1192
|
+
import os from "os";
|
|
1193
|
+
import { createServer as createServer2 } from "http";
|
|
1194
|
+
import { exec as exec2 } from "child_process";
|
|
1195
|
+
import { promisify as promisify2 } from "util";
|
|
1196
|
+
import { AuthManager as AuthManager12, TaskOperations as TaskOperations8, createSupabaseClient as createSupabaseClient2 } from "@vibetasks/core";
|
|
1197
|
+
import { detectProject as detectProject3 } from "@vibetasks/shared/utils/project-detector";
|
|
1198
|
+
var execAsync2 = promisify2(exec2);
|
|
1199
|
+
var SUPABASE_URL = "https://cbkkztbcoitrfcleghfd.supabase.co";
|
|
1200
|
+
var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNia2t6dGJjb2l0cmZjbGVnaGZkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njc3NTc0MjgsImV4cCI6MjA4MzMzMzQyOH0.G7ILx-nntP0NbxO1gKt5yASb7nt7OmpJ8qtykeGYbQA";
|
|
1201
|
+
async function openBrowser2(url) {
|
|
1202
|
+
const platform = process.platform;
|
|
1203
|
+
let command;
|
|
1204
|
+
if (platform === "win32") {
|
|
1205
|
+
command = `start "" "${url}"`;
|
|
1206
|
+
} else if (platform === "darwin") {
|
|
1207
|
+
command = `open "${url}"`;
|
|
1208
|
+
} else {
|
|
1209
|
+
command = `xdg-open "${url}"`;
|
|
1210
|
+
}
|
|
1211
|
+
try {
|
|
1212
|
+
await execAsync2(command);
|
|
1213
|
+
} catch (error) {
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
async function getAvailablePort2(startPort = 3737) {
|
|
1217
|
+
return new Promise((resolve, reject) => {
|
|
1218
|
+
const server = createServer2();
|
|
1219
|
+
server.listen(startPort, () => {
|
|
1220
|
+
const port = server.address().port;
|
|
1221
|
+
server.close(() => resolve(port));
|
|
1222
|
+
});
|
|
1223
|
+
server.on("error", (error) => {
|
|
1224
|
+
if (error.code === "EADDRINUSE") {
|
|
1225
|
+
resolve(getAvailablePort2(startPort + 1));
|
|
1226
|
+
} else {
|
|
1227
|
+
reject(error);
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
function getClaudeConfigPath() {
|
|
1233
|
+
const configHome = process.env.XDG_CONFIG_HOME || path2.join(os.homedir(), ".config");
|
|
1234
|
+
return path2.join(configHome, "claude-code", "config.json");
|
|
1235
|
+
}
|
|
1236
|
+
async function claudeCodeConfigExists() {
|
|
1237
|
+
try {
|
|
1238
|
+
const configDir = path2.dirname(getClaudeConfigPath());
|
|
1239
|
+
await fs2.access(configDir);
|
|
1240
|
+
return true;
|
|
1241
|
+
} catch {
|
|
1242
|
+
return false;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
function showWelcome() {
|
|
1246
|
+
console.log("\n");
|
|
1247
|
+
console.log(chalk13.bold.magenta("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
1248
|
+
console.log(chalk13.bold.white(" VibeTasks Setup Wizard"));
|
|
1249
|
+
console.log(chalk13.bold.magenta("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
1250
|
+
console.log("");
|
|
1251
|
+
console.log(chalk13.gray(" This wizard will set up VibeTasks in about 2 minutes:"));
|
|
1252
|
+
console.log("");
|
|
1253
|
+
console.log(chalk13.white(" 1.") + chalk13.gray(" Authenticate with your account"));
|
|
1254
|
+
console.log(chalk13.white(" 2.") + chalk13.gray(" Configure Claude Code integration"));
|
|
1255
|
+
console.log(chalk13.white(" 3.") + chalk13.gray(" Initialize project (optional)"));
|
|
1256
|
+
console.log(chalk13.white(" 4.") + chalk13.gray(" Verify everything works"));
|
|
1257
|
+
console.log("");
|
|
1258
|
+
}
|
|
1259
|
+
async function checkExistingAuth() {
|
|
1260
|
+
try {
|
|
1261
|
+
const authManager = new AuthManager12();
|
|
1262
|
+
const token = await authManager.getAccessToken();
|
|
1263
|
+
if (!token) return { authenticated: false };
|
|
1264
|
+
const supabaseUrl = await authManager.getConfig("supabase_url") || SUPABASE_URL;
|
|
1265
|
+
const supabaseKey = await authManager.getConfig("supabase_key") || SUPABASE_ANON_KEY;
|
|
1266
|
+
const supabase = createSupabaseClient2({ supabaseUrl, supabaseKey, accessToken: token });
|
|
1267
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
1268
|
+
if (user?.email) {
|
|
1269
|
+
return { authenticated: true, email: user.email };
|
|
1270
|
+
}
|
|
1271
|
+
return { authenticated: false };
|
|
1272
|
+
} catch {
|
|
1273
|
+
return { authenticated: false };
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
async function runBrowserAuth() {
|
|
1277
|
+
const port = await getAvailablePort2();
|
|
1278
|
+
return new Promise((resolve) => {
|
|
1279
|
+
let timeoutId = null;
|
|
1280
|
+
const server = createServer2(async (req, res) => {
|
|
1281
|
+
if (req.method === "OPTIONS") {
|
|
1282
|
+
res.writeHead(200, {
|
|
1283
|
+
"Access-Control-Allow-Origin": "*",
|
|
1284
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
1285
|
+
"Access-Control-Allow-Headers": "Content-Type"
|
|
1286
|
+
});
|
|
1287
|
+
res.end();
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
if (req.url?.startsWith("/callback")) {
|
|
1291
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
1292
|
+
const accessToken = url.searchParams.get("access_token");
|
|
1293
|
+
const refreshToken = url.searchParams.get("refresh_token");
|
|
1294
|
+
const email = url.searchParams.get("email");
|
|
1295
|
+
const supabaseUrl = url.searchParams.get("supabase_url");
|
|
1296
|
+
const supabaseKey = url.searchParams.get("supabase_key");
|
|
1297
|
+
if (!accessToken || !refreshToken || !email) {
|
|
1298
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
1299
|
+
res.end(getErrorHtml("Missing authentication data"));
|
|
1300
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1301
|
+
server.close(() => resolve({ success: false }));
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
const authManager = new AuthManager12();
|
|
1306
|
+
await authManager.setAccessToken(accessToken);
|
|
1307
|
+
await authManager.setRefreshToken(refreshToken);
|
|
1308
|
+
if (supabaseUrl) await authManager.setConfig("supabase_url", supabaseUrl);
|
|
1309
|
+
if (supabaseKey) await authManager.setConfig("supabase_key", supabaseKey);
|
|
1310
|
+
const storageMethod = authManager.getStorageMethod();
|
|
1311
|
+
const storageLocation = storageMethod === "keychain" ? "system keychain" : authManager["configManager"].getConfigPath();
|
|
1312
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1313
|
+
res.end(getSuccessHtml(email));
|
|
1314
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1315
|
+
server.close(() => resolve({ success: true, email, storageLocation }));
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
1318
|
+
res.end(getErrorHtml("Failed to store tokens"));
|
|
1319
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1320
|
+
server.close(() => resolve({ success: false }));
|
|
1321
|
+
}
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
res.writeHead(404);
|
|
1325
|
+
res.end();
|
|
1326
|
+
});
|
|
1327
|
+
server.listen(port, async () => {
|
|
1328
|
+
const taskflowPort = process.env.TASKFLOW_WEB_PORT || "2843";
|
|
1329
|
+
const authUrl = `http://localhost:${taskflowPort}/cli-auth?port=${port}`;
|
|
1330
|
+
console.log(chalk13.gray(`
|
|
1331
|
+
If browser doesn't open, visit:`));
|
|
1332
|
+
console.log(chalk13.cyan(` ${authUrl}
|
|
1333
|
+
`));
|
|
1334
|
+
await openBrowser2(authUrl);
|
|
1335
|
+
});
|
|
1336
|
+
server.on("error", () => {
|
|
1337
|
+
resolve({ success: false });
|
|
1338
|
+
});
|
|
1339
|
+
timeoutId = setTimeout(() => {
|
|
1340
|
+
server.close(() => resolve({ success: false }));
|
|
1341
|
+
}, 3 * 60 * 1e3);
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
async function stepAuthentication() {
|
|
1345
|
+
console.log(chalk13.bold.blue("\n\u2501\u2501\u2501 Step 1: Authentication \u2501\u2501\u2501\n"));
|
|
1346
|
+
const existing = await checkExistingAuth();
|
|
1347
|
+
if (existing.authenticated) {
|
|
1348
|
+
console.log(chalk13.green(" \u2713") + chalk13.white(` Already logged in as ${chalk13.cyan(existing.email)}`));
|
|
1349
|
+
const { reauth } = await inquirer5.prompt([{
|
|
1350
|
+
type: "confirm",
|
|
1351
|
+
name: "reauth",
|
|
1352
|
+
message: "Re-authenticate with a different account?",
|
|
1353
|
+
default: false
|
|
1354
|
+
}]);
|
|
1355
|
+
if (!reauth) {
|
|
1356
|
+
return { success: true, email: existing.email, skipped: true };
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
const { authMethod } = await inquirer5.prompt([{
|
|
1360
|
+
type: "list",
|
|
1361
|
+
name: "authMethod",
|
|
1362
|
+
message: "How would you like to authenticate?",
|
|
1363
|
+
choices: [
|
|
1364
|
+
{ name: "Browser login (recommended)", value: "browser" },
|
|
1365
|
+
{ name: "Email & password", value: "terminal" }
|
|
1366
|
+
]
|
|
1367
|
+
}]);
|
|
1368
|
+
if (authMethod === "browser") {
|
|
1369
|
+
const spinner = ora10("Opening browser for authentication...").start();
|
|
1370
|
+
spinner.stop();
|
|
1371
|
+
console.log(chalk13.gray(" Waiting for browser authentication...\n"));
|
|
1372
|
+
const result = await runBrowserAuth();
|
|
1373
|
+
if (result.success) {
|
|
1374
|
+
console.log(chalk13.green("\n \u2713") + chalk13.white(` Logged in as ${chalk13.cyan(result.email)}`));
|
|
1375
|
+
console.log(chalk13.gray(` Tokens stored in: ${result.storageLocation}`));
|
|
1376
|
+
} else {
|
|
1377
|
+
console.log(chalk13.red("\n \u2717 Authentication failed or timed out"));
|
|
1378
|
+
}
|
|
1379
|
+
return result;
|
|
1380
|
+
} else {
|
|
1381
|
+
const answers = await inquirer5.prompt([
|
|
1382
|
+
{
|
|
1383
|
+
type: "input",
|
|
1384
|
+
name: "email",
|
|
1385
|
+
message: "Email:",
|
|
1386
|
+
validate: (input) => input.includes("@") || "Please enter a valid email"
|
|
1387
|
+
},
|
|
1388
|
+
{
|
|
1389
|
+
type: "password",
|
|
1390
|
+
name: "password",
|
|
1391
|
+
message: "Password:",
|
|
1392
|
+
mask: "*",
|
|
1393
|
+
validate: (input) => input.length >= 6 || "Password must be at least 6 characters"
|
|
1394
|
+
}
|
|
1395
|
+
]);
|
|
1396
|
+
const spinner = ora10("Authenticating...").start();
|
|
1397
|
+
try {
|
|
1398
|
+
const authManager = new AuthManager12();
|
|
1399
|
+
await authManager.setConfig("supabase_url", SUPABASE_URL);
|
|
1400
|
+
await authManager.setConfig("supabase_key", SUPABASE_ANON_KEY);
|
|
1401
|
+
const supabase = createSupabaseClient2({
|
|
1402
|
+
supabaseUrl: SUPABASE_URL,
|
|
1403
|
+
supabaseKey: SUPABASE_ANON_KEY
|
|
1404
|
+
});
|
|
1405
|
+
const { data, error } = await supabase.auth.signInWithPassword({
|
|
1406
|
+
email: answers.email,
|
|
1407
|
+
password: answers.password
|
|
1408
|
+
});
|
|
1409
|
+
if (error) throw error;
|
|
1410
|
+
if (!data.session) throw new Error("No session returned");
|
|
1411
|
+
await authManager.setAccessToken(data.session.access_token);
|
|
1412
|
+
await authManager.setRefreshToken(data.session.refresh_token);
|
|
1413
|
+
const storageMethod = authManager.getStorageMethod();
|
|
1414
|
+
const storageLocation = storageMethod === "keychain" ? "system keychain" : authManager["configManager"].getConfigPath();
|
|
1415
|
+
spinner.succeed(chalk13.green("Authenticated successfully"));
|
|
1416
|
+
console.log(chalk13.gray(` Tokens stored in: ${storageLocation}`));
|
|
1417
|
+
return { success: true, email: answers.email, storageLocation };
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
spinner.fail(chalk13.red("Authentication failed"));
|
|
1420
|
+
console.log(chalk13.red(` ${error.message}`));
|
|
1421
|
+
return { success: false };
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
async function stepClaudeCodeConfig() {
|
|
1426
|
+
console.log(chalk13.bold.blue("\n\u2501\u2501\u2501 Step 2: Claude Code Integration \u2501\u2501\u2501\n"));
|
|
1427
|
+
const hasClaudeCode = await claudeCodeConfigExists();
|
|
1428
|
+
if (!hasClaudeCode) {
|
|
1429
|
+
console.log(chalk13.yellow(" \u26A0") + chalk13.gray(" Claude Code config directory not found"));
|
|
1430
|
+
console.log(chalk13.gray(" Run this command again after installing Claude Code"));
|
|
1431
|
+
const { createAnyway } = await inquirer5.prompt([{
|
|
1432
|
+
type: "confirm",
|
|
1433
|
+
name: "createAnyway",
|
|
1434
|
+
message: "Create config directory anyway?",
|
|
1435
|
+
default: false
|
|
1436
|
+
}]);
|
|
1437
|
+
if (!createAnyway) {
|
|
1438
|
+
return { success: true, skipped: true };
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
const { configure } = await inquirer5.prompt([{
|
|
1442
|
+
type: "confirm",
|
|
1443
|
+
name: "configure",
|
|
1444
|
+
message: "Configure VibeTasks MCP server for Claude Code?",
|
|
1445
|
+
default: true
|
|
1446
|
+
}]);
|
|
1447
|
+
if (!configure) {
|
|
1448
|
+
return { success: true, skipped: true };
|
|
1449
|
+
}
|
|
1450
|
+
const spinner = ora10("Configuring Claude Code...").start();
|
|
1451
|
+
try {
|
|
1452
|
+
const configPath = getClaudeConfigPath();
|
|
1453
|
+
const configDir = path2.dirname(configPath);
|
|
1454
|
+
await fs2.mkdir(configDir, { recursive: true });
|
|
1455
|
+
let config = {};
|
|
1456
|
+
try {
|
|
1457
|
+
const existing = await fs2.readFile(configPath, "utf-8");
|
|
1458
|
+
config = JSON.parse(existing);
|
|
1459
|
+
} catch {
|
|
1460
|
+
}
|
|
1461
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
1462
|
+
if (!config.hooks) config.hooks = {};
|
|
1463
|
+
if (!config.hooks.SessionStart) config.hooks.SessionStart = [];
|
|
1464
|
+
if (!config.hooks.Stop) config.hooks.Stop = [];
|
|
1465
|
+
let mcpCommand = "vibetasks-mcp";
|
|
1466
|
+
let mcpArgs = [];
|
|
1467
|
+
try {
|
|
1468
|
+
if (process.platform === "win32") {
|
|
1469
|
+
await execAsync2("where vibetasks-mcp");
|
|
1470
|
+
} else {
|
|
1471
|
+
await execAsync2("which vibetasks-mcp");
|
|
1472
|
+
}
|
|
1473
|
+
} catch {
|
|
1474
|
+
mcpCommand = "npx";
|
|
1475
|
+
mcpArgs = ["@vibetasks/mcp-server"];
|
|
1476
|
+
}
|
|
1477
|
+
config.mcpServers.vibetasks = {
|
|
1478
|
+
command: mcpCommand,
|
|
1479
|
+
...mcpArgs.length > 0 && { args: mcpArgs },
|
|
1480
|
+
env: {
|
|
1481
|
+
TASKFLOW_SUPABASE_URL: SUPABASE_URL,
|
|
1482
|
+
TASKFLOW_SUPABASE_KEY: SUPABASE_ANON_KEY
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
const hasSessionStartHook = config.hooks.SessionStart.some(
|
|
1486
|
+
(h) => h.command === mcpCommand || h.command === "vibetasks-mcp"
|
|
1487
|
+
);
|
|
1488
|
+
const hasStopHook = config.hooks.Stop.some(
|
|
1489
|
+
(h) => h.command === mcpCommand || h.command === "vibetasks-mcp"
|
|
1490
|
+
);
|
|
1491
|
+
if (!hasSessionStartHook) {
|
|
1492
|
+
config.hooks.SessionStart.push({
|
|
1493
|
+
type: "command",
|
|
1494
|
+
command: mcpCommand,
|
|
1495
|
+
...mcpArgs.length > 0 && { args: mcpArgs },
|
|
1496
|
+
env: {
|
|
1497
|
+
CLAUDE_HOOK_TYPE: "SessionStart",
|
|
1498
|
+
TASKFLOW_SUPABASE_URL: SUPABASE_URL
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
if (!hasStopHook) {
|
|
1503
|
+
config.hooks.Stop.push({
|
|
1504
|
+
type: "command",
|
|
1505
|
+
command: mcpCommand,
|
|
1506
|
+
...mcpArgs.length > 0 && { args: mcpArgs },
|
|
1507
|
+
env: {
|
|
1508
|
+
CLAUDE_HOOK_TYPE: "SessionEnd",
|
|
1509
|
+
TASKFLOW_SUPABASE_URL: SUPABASE_URL
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
await fs2.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1514
|
+
spinner.succeed(chalk13.green("Claude Code configured"));
|
|
1515
|
+
console.log(chalk13.gray(` Config: ${configPath}`));
|
|
1516
|
+
console.log(chalk13.gray(" Added: MCP server, SessionStart hook, SessionEnd hook"));
|
|
1517
|
+
return { success: true, configured: true, configPath };
|
|
1518
|
+
} catch (error) {
|
|
1519
|
+
spinner.fail(chalk13.red("Failed to configure Claude Code"));
|
|
1520
|
+
console.log(chalk13.red(` ${error.message}`));
|
|
1521
|
+
return { success: false };
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
async function stepProjectInit() {
|
|
1525
|
+
console.log(chalk13.bold.blue("\n\u2501\u2501\u2501 Step 3: Project Setup (Optional) \u2501\u2501\u2501\n"));
|
|
1526
|
+
const spinner = ora10("Detecting project...").start();
|
|
1527
|
+
let project;
|
|
1528
|
+
try {
|
|
1529
|
+
project = await detectProject3(process.cwd());
|
|
1530
|
+
spinner.succeed(chalk13.green(`Detected: ${project.name}`));
|
|
1531
|
+
console.log(chalk13.gray(` Source: ${project.source}`));
|
|
1532
|
+
console.log(chalk13.gray(` Path: ${project.path}`));
|
|
1533
|
+
} catch {
|
|
1534
|
+
spinner.info(chalk13.gray("No project detected in current directory"));
|
|
1535
|
+
return { success: true, skipped: true };
|
|
1536
|
+
}
|
|
1537
|
+
const { setupProject } = await inquirer5.prompt([{
|
|
1538
|
+
type: "confirm",
|
|
1539
|
+
name: "setupProject",
|
|
1540
|
+
message: `Auto-tag tasks created here as "${project.name}"?`,
|
|
1541
|
+
default: true
|
|
1542
|
+
}]);
|
|
1543
|
+
if (!setupProject) {
|
|
1544
|
+
return { success: true, skipped: true };
|
|
1545
|
+
}
|
|
1546
|
+
try {
|
|
1547
|
+
const authManager = new AuthManager12();
|
|
1548
|
+
await authManager.setConfig(`project_${process.cwd()}`, project.name);
|
|
1549
|
+
console.log(chalk13.green("\n \u2713") + chalk13.white(` Project "${project.name}" configured`));
|
|
1550
|
+
return { success: true, projectName: project.name };
|
|
1551
|
+
} catch (error) {
|
|
1552
|
+
console.log(chalk13.yellow("\n \u26A0") + chalk13.gray(` Could not save project config: ${error.message}`));
|
|
1553
|
+
return { success: true, skipped: true };
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
async function stepVerify() {
|
|
1557
|
+
console.log(chalk13.bold.blue("\n\u2501\u2501\u2501 Step 4: Verification \u2501\u2501\u2501\n"));
|
|
1558
|
+
const result = {
|
|
1559
|
+
supabaseConnected: false,
|
|
1560
|
+
mcpConfigured: false
|
|
1561
|
+
};
|
|
1562
|
+
const supabaseSpinner = ora10("Testing Supabase connection...").start();
|
|
1563
|
+
try {
|
|
1564
|
+
const authManager = new AuthManager12();
|
|
1565
|
+
const taskOps = await TaskOperations8.fromAuthManager(authManager);
|
|
1566
|
+
const tasks = await taskOps.getTasks("all");
|
|
1567
|
+
result.supabaseConnected = true;
|
|
1568
|
+
result.taskCount = tasks.length;
|
|
1569
|
+
supabaseSpinner.succeed(chalk13.green(`Supabase connected (${tasks.length} tasks found)`));
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
supabaseSpinner.fail(chalk13.red("Supabase connection failed"));
|
|
1572
|
+
console.log(chalk13.gray(` ${error.message}`));
|
|
1573
|
+
}
|
|
1574
|
+
const mcpSpinner = ora10("Checking MCP configuration...").start();
|
|
1575
|
+
try {
|
|
1576
|
+
const configPath = getClaudeConfigPath();
|
|
1577
|
+
const configContent = await fs2.readFile(configPath, "utf-8");
|
|
1578
|
+
const config = JSON.parse(configContent);
|
|
1579
|
+
if (config.mcpServers?.vibetasks) {
|
|
1580
|
+
result.mcpConfigured = true;
|
|
1581
|
+
mcpSpinner.succeed(chalk13.green("MCP server configured"));
|
|
1582
|
+
} else {
|
|
1583
|
+
mcpSpinner.warn(chalk13.yellow("MCP server not found in config"));
|
|
1584
|
+
}
|
|
1585
|
+
} catch {
|
|
1586
|
+
mcpSpinner.warn(chalk13.yellow("Could not read Claude Code config"));
|
|
1587
|
+
}
|
|
1588
|
+
return result;
|
|
1589
|
+
}
|
|
1590
|
+
function showCompletion(auth, claude, project, verify) {
|
|
1591
|
+
console.log("\n");
|
|
1592
|
+
console.log(chalk13.bold.green("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
1593
|
+
console.log(chalk13.bold.white(" Setup Complete!"));
|
|
1594
|
+
console.log(chalk13.bold.green("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
1595
|
+
console.log("");
|
|
1596
|
+
console.log(chalk13.white(" Summary:"));
|
|
1597
|
+
console.log(` ${auth.success ? chalk13.green("\u2713") : chalk13.red("\u2717")} Authentication: ${auth.email || "Not configured"}`);
|
|
1598
|
+
console.log(` ${claude.configured ? chalk13.green("\u2713") : chalk13.yellow("\u25CB")} Claude Code: ${claude.configured ? "Configured" : "Skipped"}`);
|
|
1599
|
+
console.log(` ${project.projectName ? chalk13.green("\u2713") : chalk13.yellow("\u25CB")} Project: ${project.projectName || "None"}`);
|
|
1600
|
+
console.log(` ${verify.supabaseConnected ? chalk13.green("\u2713") : chalk13.red("\u2717")} Connection: ${verify.supabaseConnected ? `${verify.taskCount} tasks` : "Failed"}`);
|
|
1601
|
+
console.log("");
|
|
1602
|
+
if (claude.configured) {
|
|
1603
|
+
console.log(chalk13.yellow(" \u26A1 Important: Restart Claude Code to activate MCP server"));
|
|
1604
|
+
console.log("");
|
|
1605
|
+
}
|
|
1606
|
+
console.log(chalk13.white(" What you can do now:"));
|
|
1607
|
+
console.log("");
|
|
1608
|
+
console.log(chalk13.gray(" CLI Commands:"));
|
|
1609
|
+
console.log(chalk13.cyan(' $ vibetasks add "My first task"'));
|
|
1610
|
+
console.log(chalk13.cyan(" $ vibetasks list"));
|
|
1611
|
+
console.log(chalk13.cyan(" $ vibetasks done <task-id>"));
|
|
1612
|
+
console.log("");
|
|
1613
|
+
if (claude.configured) {
|
|
1614
|
+
console.log(chalk13.gray(" In Claude Code (after restart):"));
|
|
1615
|
+
console.log(chalk13.cyan(' "Create a task to fix the login bug"'));
|
|
1616
|
+
console.log(chalk13.cyan(' "Show my tasks for today"'));
|
|
1617
|
+
console.log(chalk13.cyan(' "What should I work on next?"'));
|
|
1618
|
+
}
|
|
1619
|
+
console.log("");
|
|
1620
|
+
console.log(chalk13.gray(" Need help? https://github.com/vyasapps/vibetasks"));
|
|
1621
|
+
console.log("");
|
|
1622
|
+
}
|
|
1623
|
+
function getSuccessHtml(email) {
|
|
1624
|
+
return `<!DOCTYPE html>
|
|
1625
|
+
<html>
|
|
1626
|
+
<head>
|
|
1627
|
+
<title>VibeTasks - Setup Success</title>
|
|
1628
|
+
<style>
|
|
1629
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1630
|
+
body {
|
|
1631
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1632
|
+
display: flex; align-items: center; justify-content: center;
|
|
1633
|
+
min-height: 100vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
1634
|
+
}
|
|
1635
|
+
.container {
|
|
1636
|
+
text-align: center; padding: 60px 40px; background: white;
|
|
1637
|
+
border-radius: 20px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); max-width: 450px;
|
|
1638
|
+
}
|
|
1639
|
+
.check { font-size: 64px; margin-bottom: 20px; }
|
|
1640
|
+
h1 { color: #2d3748; font-size: 24px; margin-bottom: 8px; }
|
|
1641
|
+
.email { color: #667eea; font-weight: 600; margin-bottom: 20px; }
|
|
1642
|
+
p { color: #718096; line-height: 1.6; }
|
|
1643
|
+
.badge {
|
|
1644
|
+
display: inline-block; background: #f0fdf4; color: #15803d;
|
|
1645
|
+
padding: 8px 16px; border-radius: 20px; font-size: 14px; margin-top: 20px;
|
|
1646
|
+
}
|
|
1647
|
+
</style>
|
|
1648
|
+
</head>
|
|
1649
|
+
<body>
|
|
1650
|
+
<div class="container">
|
|
1651
|
+
<div class="check">\u2713</div>
|
|
1652
|
+
<h1>Authentication Successful!</h1>
|
|
1653
|
+
<p class="email">${email}</p>
|
|
1654
|
+
<p>Return to your terminal to continue setup.</p>
|
|
1655
|
+
<div class="badge">You can close this window</div>
|
|
1656
|
+
</div>
|
|
1657
|
+
</body>
|
|
1658
|
+
</html>`;
|
|
1659
|
+
}
|
|
1660
|
+
function getErrorHtml(message) {
|
|
1661
|
+
return `<!DOCTYPE html>
|
|
1662
|
+
<html>
|
|
1663
|
+
<head>
|
|
1664
|
+
<title>VibeTasks - Error</title>
|
|
1665
|
+
<style>
|
|
1666
|
+
body {
|
|
1667
|
+
font-family: system-ui; display: flex; align-items: center;
|
|
1668
|
+
justify-content: center; height: 100vh; margin: 0; background: #f5f5f5;
|
|
1669
|
+
}
|
|
1670
|
+
.container {
|
|
1671
|
+
text-align: center; padding: 40px; background: white;
|
|
1672
|
+
border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
1673
|
+
}
|
|
1674
|
+
h1 { color: #e53e3e; margin-bottom: 16px; }
|
|
1675
|
+
</style>
|
|
1676
|
+
</head>
|
|
1677
|
+
<body>
|
|
1678
|
+
<div class="container">
|
|
1679
|
+
<h1>Authentication Failed</h1>
|
|
1680
|
+
<p>${message}</p>
|
|
1681
|
+
</div>
|
|
1682
|
+
</body>
|
|
1683
|
+
</html>`;
|
|
1684
|
+
}
|
|
1685
|
+
var setupCommand = new Command12("setup").description("Interactive setup wizard for VibeTasks").option("--skip-auth", "Skip authentication step").option("--skip-claude", "Skip Claude Code configuration").option("--skip-project", "Skip project initialization").option("--skip-verify", "Skip verification step").action(async (options) => {
|
|
1686
|
+
showWelcome();
|
|
1687
|
+
const { proceed } = await inquirer5.prompt([{
|
|
1688
|
+
type: "confirm",
|
|
1689
|
+
name: "proceed",
|
|
1690
|
+
message: "Ready to begin setup?",
|
|
1691
|
+
default: true
|
|
1692
|
+
}]);
|
|
1693
|
+
if (!proceed) {
|
|
1694
|
+
console.log(chalk13.gray("\nSetup cancelled. Run `vibetasks setup` anytime to continue.\n"));
|
|
1695
|
+
process.exit(0);
|
|
1696
|
+
}
|
|
1697
|
+
let authResult = { success: false };
|
|
1698
|
+
if (!options.skipAuth) {
|
|
1699
|
+
authResult = await stepAuthentication();
|
|
1700
|
+
if (!authResult.success) {
|
|
1701
|
+
console.log(chalk13.red("\n\u2717 Authentication is required to continue."));
|
|
1702
|
+
console.log(chalk13.gray(" Run `vibetasks setup` to try again.\n"));
|
|
1703
|
+
process.exit(1);
|
|
1704
|
+
}
|
|
1705
|
+
} else {
|
|
1706
|
+
authResult = { success: true, skipped: true };
|
|
1707
|
+
}
|
|
1708
|
+
let claudeResult = { success: true, skipped: true };
|
|
1709
|
+
if (!options.skipClaude) {
|
|
1710
|
+
claudeResult = await stepClaudeCodeConfig();
|
|
1711
|
+
}
|
|
1712
|
+
let projectResult = { success: true, skipped: true };
|
|
1713
|
+
if (!options.skipProject) {
|
|
1714
|
+
projectResult = await stepProjectInit();
|
|
1715
|
+
}
|
|
1716
|
+
let verifyResult = { supabaseConnected: false, mcpConfigured: false };
|
|
1717
|
+
if (!options.skipVerify && authResult.success && !authResult.skipped) {
|
|
1718
|
+
verifyResult = await stepVerify();
|
|
1719
|
+
} else if (authResult.skipped) {
|
|
1720
|
+
verifyResult = await stepVerify();
|
|
1721
|
+
}
|
|
1722
|
+
showCompletion(authResult, claudeResult, projectResult, verifyResult);
|
|
1723
|
+
process.exit(0);
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1113
1726
|
// bin/vibetasks.ts
|
|
1114
|
-
var program = new
|
|
1115
|
-
program.name("vibetasks").description("VibeTasks CLI - Fast task management for
|
|
1727
|
+
var program = new Command13();
|
|
1728
|
+
program.name("vibetasks").description("VibeTasks CLI - Fast task management for vibers").version("0.2.0");
|
|
1729
|
+
program.addCommand(setupCommand);
|
|
1116
1730
|
program.addCommand(loginCommand);
|
|
1117
1731
|
program.addCommand(addCommand);
|
|
1118
1732
|
program.addCommand(listCommand);
|
|
1733
|
+
program.addCommand(vibingCommand);
|
|
1119
1734
|
program.addCommand(doneCommand);
|
|
1120
1735
|
program.addCommand(searchCommand);
|
|
1121
1736
|
program.addCommand(updateCommand);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibetasks/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "VibeTasks CLI - Lightning-fast task management from your terminal. Works with Claude Code, Cursor, and all AI coding tools.",
|
|
5
5
|
"author": "Vyas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"typecheck": "tsc --noEmit"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@vibetasks/core": "^0.
|
|
46
|
+
"@vibetasks/core": "^0.2.0",
|
|
47
47
|
"@vibetasks/shared": "^1.0.0",
|
|
48
48
|
"commander": "^11.1.0",
|
|
49
49
|
"chalk": "^5.3.0",
|