@workdynamite/cli 1.0.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.
Files changed (54) hide show
  1. package/README.md +39 -0
  2. package/bin/workdynamite.js +3 -0
  3. package/dist/commands/config.d.ts +3 -0
  4. package/dist/commands/config.d.ts.map +1 -0
  5. package/dist/commands/config.js +62 -0
  6. package/dist/commands/config.js.map +1 -0
  7. package/dist/commands/login.d.ts +3 -0
  8. package/dist/commands/login.d.ts.map +1 -0
  9. package/dist/commands/login.js +99 -0
  10. package/dist/commands/login.js.map +1 -0
  11. package/dist/commands/logout.d.ts +3 -0
  12. package/dist/commands/logout.d.ts.map +1 -0
  13. package/dist/commands/logout.js +24 -0
  14. package/dist/commands/logout.js.map +1 -0
  15. package/dist/commands/pick.d.ts +3 -0
  16. package/dist/commands/pick.d.ts.map +1 -0
  17. package/dist/commands/pick.js +81 -0
  18. package/dist/commands/pick.js.map +1 -0
  19. package/dist/commands/projects.d.ts +3 -0
  20. package/dist/commands/projects.d.ts.map +1 -0
  21. package/dist/commands/projects.js +119 -0
  22. package/dist/commands/projects.js.map +1 -0
  23. package/dist/commands/status.d.ts +3 -0
  24. package/dist/commands/status.d.ts.map +1 -0
  25. package/dist/commands/status.js +80 -0
  26. package/dist/commands/status.js.map +1 -0
  27. package/dist/commands/tasks.d.ts +3 -0
  28. package/dist/commands/tasks.d.ts.map +1 -0
  29. package/dist/commands/tasks.js +102 -0
  30. package/dist/commands/tasks.js.map +1 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +51 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/lib/api.d.ts +70 -0
  36. package/dist/lib/api.d.ts.map +1 -0
  37. package/dist/lib/api.js +71 -0
  38. package/dist/lib/api.js.map +1 -0
  39. package/dist/lib/config.d.ts +20 -0
  40. package/dist/lib/config.d.ts.map +1 -0
  41. package/dist/lib/config.js +55 -0
  42. package/dist/lib/config.js.map +1 -0
  43. package/package.json +47 -0
  44. package/src/commands/config.ts +69 -0
  45. package/src/commands/login.ts +116 -0
  46. package/src/commands/logout.ts +21 -0
  47. package/src/commands/pick.ts +80 -0
  48. package/src/commands/projects.ts +139 -0
  49. package/src/commands/status.ts +87 -0
  50. package/src/commands/tasks.ts +112 -0
  51. package/src/index.ts +50 -0
  52. package/src/lib/api.ts +128 -0
  53. package/src/lib/config.ts +61 -0
  54. package/tsconfig.json +20 -0
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.tasksCommand = void 0;
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const api_js_1 = require("../lib/api.js");
11
+ const config_js_1 = require("../lib/config.js");
12
+ exports.tasksCommand = new commander_1.Command('tasks')
13
+ .description('List your assigned tasks')
14
+ .option('-p, --project <id>', 'Filter by project ID')
15
+ .option('-s, --state <state>', 'Filter by state (open, in_progress, done)')
16
+ .option('-l, --limit <number>', 'Maximum number of tasks', '20')
17
+ .action(async (options) => {
18
+ if (!(0, config_js_1.isAuthenticated)()) {
19
+ console.log(chalk_1.default.red('Not logged in. Run "workdynamite login" first.'));
20
+ return;
21
+ }
22
+ const spinner = (0, ora_1.default)('Fetching tasks...').start();
23
+ const config = (0, config_js_1.getConfig)();
24
+ const projectId = options.project || config.default_project;
25
+ const { data, error } = await (0, api_js_1.getTasks)({
26
+ project: projectId,
27
+ state: options.state,
28
+ limit: parseInt(options.limit, 10),
29
+ });
30
+ if (error) {
31
+ spinner.fail(chalk_1.default.red(`Failed to fetch tasks: ${error}`));
32
+ return;
33
+ }
34
+ spinner.stop();
35
+ const tasks = data.tasks;
36
+ if (tasks.length === 0) {
37
+ console.log(chalk_1.default.yellow('\nNo tasks assigned to you.'));
38
+ console.log(chalk_1.default.dim('Create tasks in the web app to see them here.'));
39
+ return;
40
+ }
41
+ // Group tasks by project
42
+ const tasksByProject = tasks.reduce((acc, task) => {
43
+ const project = task.project || 'No Project';
44
+ if (!acc[project])
45
+ acc[project] = [];
46
+ acc[project].push(task);
47
+ return acc;
48
+ }, {});
49
+ console.log(chalk_1.default.bold('\nšŸ“‹ Your Assigned Tasks\n'));
50
+ for (const [project, projectTasks] of Object.entries(tasksByProject)) {
51
+ console.log(chalk_1.default.bold.blue(`${project}`));
52
+ console.log(chalk_1.default.dim('─'.repeat(50)));
53
+ for (const task of projectTasks) {
54
+ const stateColor = getStateColor(task.state);
55
+ const priorityBadge = getPriorityBadge(task.priority);
56
+ console.log(` ${chalk_1.default.dim('#')}${chalk_1.default.bold(String(task.number).padEnd(5))} ` +
57
+ `${priorityBadge} ` +
58
+ `${task.title.substring(0, 40)}${task.title.length > 40 ? '...' : ''} ` +
59
+ `${stateColor(task.state)}`);
60
+ }
61
+ console.log('');
62
+ }
63
+ console.log(chalk_1.default.dim('─'.repeat(50)));
64
+ console.log(chalk_1.default.dim(`Showing ${tasks.length} task(s)`));
65
+ console.log(chalk_1.default.dim('\nCommands:'));
66
+ console.log(chalk_1.default.dim(' workdynamite pick <number> Copy task ref to clipboard'));
67
+ console.log(chalk_1.default.dim(' workdynamite pick <number> -w Copy as WIP'));
68
+ console.log('');
69
+ });
70
+ function getStateColor(state) {
71
+ switch (state.toLowerCase()) {
72
+ case 'open':
73
+ case 'todo':
74
+ return chalk_1.default.gray;
75
+ case 'in_progress':
76
+ case 'in progress':
77
+ return chalk_1.default.yellow;
78
+ case 'done':
79
+ case 'completed':
80
+ return chalk_1.default.green;
81
+ case 'blocked':
82
+ return chalk_1.default.red;
83
+ default:
84
+ return chalk_1.default.white;
85
+ }
86
+ }
87
+ function getPriorityBadge(priority) {
88
+ switch (priority?.toLowerCase()) {
89
+ case 'critical':
90
+ case 'highest':
91
+ return chalk_1.default.bgRed.white(' !! ');
92
+ case 'high':
93
+ return chalk_1.default.red('[H]');
94
+ case 'medium':
95
+ return chalk_1.default.yellow('[M]');
96
+ case 'low':
97
+ return chalk_1.default.green('[L]');
98
+ default:
99
+ return chalk_1.default.dim('[–]');
100
+ }
101
+ }
102
+ //# sourceMappingURL=tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/commands/tasks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const commander_1 = require("commander");
4
+ const login_js_1 = require("./commands/login.js");
5
+ const logout_js_1 = require("./commands/logout.js");
6
+ const tasks_js_1 = require("./commands/tasks.js");
7
+ const pick_js_1 = require("./commands/pick.js");
8
+ const projects_js_1 = require("./commands/projects.js");
9
+ const status_js_1 = require("./commands/status.js");
10
+ const config_js_1 = require("./commands/config.js");
11
+ const program = new commander_1.Command();
12
+ program
13
+ .name('workdynamite')
14
+ .description('WorkDynamite CLI - Manage tasks from your terminal')
15
+ .version('1.0.0');
16
+ // Register commands
17
+ program.addCommand(login_js_1.loginCommand);
18
+ program.addCommand(logout_js_1.logoutCommand);
19
+ program.addCommand(tasks_js_1.tasksCommand);
20
+ program.addCommand(pick_js_1.pickCommand);
21
+ program.addCommand(projects_js_1.projectsCommand);
22
+ program.addCommand(status_js_1.statusCommand);
23
+ program.addCommand(config_js_1.configCommand);
24
+ // Aliases
25
+ program
26
+ .command('ls')
27
+ .description('Alias for "tasks" - list your assigned tasks')
28
+ .action(async () => {
29
+ await tasks_js_1.tasksCommand.parseAsync(['tasks'], { from: 'user' });
30
+ });
31
+ program
32
+ .command('p <number>')
33
+ .description('Alias for "pick" - copy task reference to clipboard')
34
+ .option('-w, --wip', 'Use WIP prefix')
35
+ .option('-f, --fixes', 'Use Fixes prefix (default)')
36
+ .option('-c, --closes', 'Use Closes prefix')
37
+ .option('-r, --refs', 'Use Refs prefix')
38
+ .action(async (number, options) => {
39
+ const args = ['pick', number];
40
+ if (options.wip)
41
+ args.push('--wip');
42
+ if (options.fixes)
43
+ args.push('--fixes');
44
+ if (options.closes)
45
+ args.push('--closes');
46
+ if (options.refs)
47
+ args.push('--refs');
48
+ await pick_js_1.pickCommand.parseAsync(args, { from: 'user' });
49
+ });
50
+ program.parse();
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,kDAAmD;AACnD,oDAAqD;AACrD,kDAAmD;AACnD,gDAAiD;AACjD,wDAAyD;AACzD,oDAAqD;AACrD,oDAAqD;AAErD,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,cAAc,CAAC;KACpB,WAAW,CAAC,oDAAoD,CAAC;KACjE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,uBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,uBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,6BAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAElC,UAAU;AACV,OAAO;KACJ,OAAO,CAAC,IAAI,CAAC;KACb,WAAW,CAAC,8CAA8C,CAAC;KAC3D,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,uBAAY,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,YAAY,CAAC;KACrB,WAAW,CAAC,qDAAqD,CAAC;KAClE,MAAM,CAAC,WAAW,EAAE,gBAAgB,CAAC;KACrC,MAAM,CAAC,aAAa,EAAE,4BAA4B,CAAC;KACnD,MAAM,CAAC,cAAc,EAAE,mBAAmB,CAAC;KAC3C,MAAM,CAAC,YAAY,EAAE,iBAAiB,CAAC;KACvC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;IAChC,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,IAAI,OAAO,CAAC,GAAG;QAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,OAAO,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,OAAO,CAAC,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1C,IAAI,OAAO,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,qBAAW,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,70 @@
1
+ interface ApiResponse<T = any> {
2
+ data?: T;
3
+ error?: string;
4
+ }
5
+ export declare function initLogin(): Promise<ApiResponse<{
6
+ device_code: string;
7
+ user_code: string;
8
+ verification_uri: string;
9
+ verification_uri_complete: string;
10
+ expires_in: number;
11
+ interval: number;
12
+ }>>;
13
+ export declare function pollLogin(deviceCode: string): Promise<ApiResponse<{
14
+ access_token: string;
15
+ token_type: string;
16
+ user: {
17
+ id: string;
18
+ email: string;
19
+ name: string;
20
+ };
21
+ message?: string;
22
+ }>>;
23
+ export declare function getMe(): Promise<ApiResponse<{
24
+ user: {
25
+ id: string;
26
+ email: string;
27
+ name: string;
28
+ avatar_url?: string;
29
+ };
30
+ projects_count: number;
31
+ }>>;
32
+ export declare function getProjects(): Promise<ApiResponse<{
33
+ projects: Array<{
34
+ id: string;
35
+ name: string;
36
+ description?: string;
37
+ status: string;
38
+ progress: number;
39
+ role: string;
40
+ }>;
41
+ }>>;
42
+ export interface Task {
43
+ id: string;
44
+ number: number;
45
+ title: string;
46
+ state: string;
47
+ priority: string;
48
+ story_points?: number;
49
+ due_date?: string;
50
+ project: string;
51
+ project_id: string;
52
+ refs: {
53
+ wip: string;
54
+ fixes: string;
55
+ closes: string;
56
+ refs: string;
57
+ };
58
+ }
59
+ export declare function getTasks(options?: {
60
+ project?: string;
61
+ state?: string;
62
+ limit?: number;
63
+ }): Promise<ApiResponse<{
64
+ tasks: Task[];
65
+ }>>;
66
+ export declare function getTask(taskId: string): Promise<ApiResponse<{
67
+ task: Task;
68
+ }>>;
69
+ export {};
70
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAEA,UAAU,WAAW,CAAC,CAAC,GAAG,GAAG;IAC3B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0CD,wBAAsB,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,yBAAyB,EAAE,MAAM,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC,CAKF;AAED,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC,CAKF;AAED,wBAAsB,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC;IACjD,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAAC,CAEF;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IACvD,QAAQ,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ,CAAC,CAAC,CAEF;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,wBAAsB,QAAQ,CAAC,OAAO,GAAE;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,GAAG,OAAO,CAAC,WAAW,CAAC;IAAE,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAAC,CAAC,CAQ/C;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,CAElF"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initLogin = initLogin;
4
+ exports.pollLogin = pollLogin;
5
+ exports.getMe = getMe;
6
+ exports.getProjects = getProjects;
7
+ exports.getTasks = getTasks;
8
+ exports.getTask = getTask;
9
+ const config_js_1 = require("./config.js");
10
+ async function request(endpoint, options = {}) {
11
+ const token = (0, config_js_1.getToken)();
12
+ const apiUrl = (0, config_js_1.getApiUrl)();
13
+ if (!token && !endpoint.startsWith('login/')) {
14
+ return { error: 'Not authenticated. Run "workdynamite login" first.' };
15
+ }
16
+ const url = `${apiUrl}/${endpoint}`;
17
+ const headers = {
18
+ 'Content-Type': 'application/json',
19
+ ...(options.headers || {}),
20
+ };
21
+ if (token) {
22
+ headers['Authorization'] = `Bearer ${token}`;
23
+ }
24
+ try {
25
+ const response = await fetch(url, {
26
+ ...options,
27
+ headers,
28
+ });
29
+ const data = await response.json();
30
+ if (!response.ok) {
31
+ return { error: data.error || `Request failed with status ${response.status}` };
32
+ }
33
+ return { data };
34
+ }
35
+ catch (error) {
36
+ return { error: error.message || 'Network request failed' };
37
+ }
38
+ }
39
+ async function initLogin() {
40
+ return request('login/init', {
41
+ method: 'POST',
42
+ body: JSON.stringify({}),
43
+ });
44
+ }
45
+ async function pollLogin(deviceCode) {
46
+ return request('login/poll', {
47
+ method: 'POST',
48
+ body: JSON.stringify({ device_code: deviceCode }),
49
+ });
50
+ }
51
+ async function getMe() {
52
+ return request('me');
53
+ }
54
+ async function getProjects() {
55
+ return request('projects');
56
+ }
57
+ async function getTasks(options = {}) {
58
+ const params = new URLSearchParams();
59
+ if (options.project)
60
+ params.set('project', options.project);
61
+ if (options.state)
62
+ params.set('state', options.state);
63
+ if (options.limit)
64
+ params.set('limit', options.limit.toString());
65
+ const query = params.toString();
66
+ return request(`tasks${query ? `?${query}` : ''}`);
67
+ }
68
+ async function getTask(taskId) {
69
+ return request(`tasks/${taskId}`);
70
+ }
71
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":";;AA+CA,8BAYC;AAED,8BAUC;AAED,sBAKC;AAED,kCAWC;AAoBD,4BAYC;AAED,0BAEC;AA/HD,2CAA6D;AAO7D,KAAK,UAAU,OAAO,CACpB,QAAgB,EAChB,UAAuB,EAAE;IAEzB,MAAM,KAAK,GAAG,IAAA,oBAAQ,GAAE,CAAC;IACzB,MAAM,MAAM,GAAG,IAAA,qBAAS,GAAE,CAAC;IAE3B,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7C,OAAO,EAAE,KAAK,EAAE,oDAAoD,EAAE,CAAC;IACzE,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC;IAEpC,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,GAAG,CAAE,OAAO,CAAC,OAAkC,IAAI,EAAE,CAAC;KACvD,CAAC;IAEF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,GAAG,OAAO;YACV,OAAO;SACR,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA4B,CAAC;QAE7D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,8BAA8B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QAClF,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,IAAI,wBAAwB,EAAE,CAAC;IAC9D,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,SAAS;IAQ7B,OAAO,OAAO,CAAC,YAAY,EAAE;QAC3B,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;KACzB,CAAC,CAAC;AACL,CAAC;AAEM,KAAK,UAAU,SAAS,CAAC,UAAkB;IAMhD,OAAO,OAAO,CAAC,YAAY,EAAE;QAC3B,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KAClD,CAAC,CAAC;AACL,CAAC;AAEM,KAAK,UAAU,KAAK;IAIzB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC;AAEM,KAAK,UAAU,WAAW;IAU/B,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAoBM,KAAK,UAAU,QAAQ,CAAC,UAI3B,EAAE;IACJ,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,OAAO,CAAC,OAAO;QAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEjE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAChC,OAAO,OAAO,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACrD,CAAC;AAEM,KAAK,UAAU,OAAO,CAAC,MAAc;IAC1C,OAAO,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,20 @@
1
+ interface ConfigSchema {
2
+ api_url: string;
3
+ token: string | null;
4
+ default_project: string | null;
5
+ user: {
6
+ id: string;
7
+ email: string;
8
+ name: string;
9
+ } | null;
10
+ }
11
+ export declare function getConfig(): ConfigSchema;
12
+ export declare function setToken(token: string): void;
13
+ export declare function setUser(user: ConfigSchema['user']): void;
14
+ export declare function setDefaultProject(projectId: string | null): void;
15
+ export declare function clearConfig(): void;
16
+ export declare function isAuthenticated(): boolean;
17
+ export declare function getToken(): string | null;
18
+ export declare function getApiUrl(): string;
19
+ export {};
20
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;CACV;AAYD,wBAAgB,SAAS,IAAI,YAAY,CAOxC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,IAAI,CAExD;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEhE;AAED,wBAAgB,WAAW,IAAI,IAAI,CAIlC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAExC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getConfig = getConfig;
7
+ exports.setToken = setToken;
8
+ exports.setUser = setUser;
9
+ exports.setDefaultProject = setDefaultProject;
10
+ exports.clearConfig = clearConfig;
11
+ exports.isAuthenticated = isAuthenticated;
12
+ exports.getToken = getToken;
13
+ exports.getApiUrl = getApiUrl;
14
+ const conf_1 = __importDefault(require("conf"));
15
+ const config = new conf_1.default({
16
+ projectName: 'workdynamite',
17
+ defaults: {
18
+ api_url: 'https://tovgfzknrookxgmvibsk.supabase.co/functions/v1/cli-api',
19
+ token: null,
20
+ default_project: null,
21
+ user: null,
22
+ },
23
+ });
24
+ function getConfig() {
25
+ return {
26
+ api_url: config.get('api_url'),
27
+ token: config.get('token'),
28
+ default_project: config.get('default_project'),
29
+ user: config.get('user'),
30
+ };
31
+ }
32
+ function setToken(token) {
33
+ config.set('token', token);
34
+ }
35
+ function setUser(user) {
36
+ config.set('user', user);
37
+ }
38
+ function setDefaultProject(projectId) {
39
+ config.set('default_project', projectId);
40
+ }
41
+ function clearConfig() {
42
+ config.set('token', null);
43
+ config.set('user', null);
44
+ config.set('default_project', null);
45
+ }
46
+ function isAuthenticated() {
47
+ return !!config.get('token');
48
+ }
49
+ function getToken() {
50
+ return config.get('token');
51
+ }
52
+ function getApiUrl() {
53
+ return config.get('api_url');
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":";;;;;AAuBA,8BAOC;AAED,4BAEC;AAED,0BAEC;AAED,8CAEC;AAED,kCAIC;AAED,0CAEC;AAED,4BAEC;AAED,8BAEC;AA5DD,gDAAwB;AAaxB,MAAM,MAAM,GAAG,IAAI,cAAI,CAAe;IACpC,WAAW,EAAE,cAAc;IAC3B,QAAQ,EAAE;QACR,OAAO,EAAE,+DAA+D;QACxE,KAAK,EAAE,IAAI;QACX,eAAe,EAAE,IAAI;QACrB,IAAI,EAAE,IAAI;KACX;CACF,CAAC,CAAC;AAEH,SAAgB,SAAS;IACvB,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QAC9B,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC9C,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,SAAgB,QAAQ,CAAC,KAAa;IACpC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,OAAO,CAAC,IAA0B;IAChD,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,SAAgB,iBAAiB,CAAC,SAAwB;IACxD,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,WAAW;IACzB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1B,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzB,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,SAAgB,eAAe;IAC7B,OAAO,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,SAAgB,QAAQ;IACtB,OAAO,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,SAAS;IACvB,OAAO,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC/B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@workdynamite/cli",
3
+ "version": "1.0.0",
4
+ "description": "WorkDynamite CLI - Manage tasks from your terminal",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "workdynamite": "./bin/workdynamite.js",
8
+ "wd": "./bin/workdynamite.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "workdynamite",
18
+ "cli",
19
+ "task-management",
20
+ "git",
21
+ "developer-tools"
22
+ ],
23
+ "author": "WorkDynamite",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/workdynamite/cli"
28
+ },
29
+ "dependencies": {
30
+ "chalk": "^5.3.0",
31
+ "clipboardy": "^4.0.0",
32
+ "commander": "^11.1.0",
33
+ "conf": "^12.0.0",
34
+ "inquirer": "^9.2.12",
35
+ "node-fetch": "^3.3.2",
36
+ "open": "^10.0.3",
37
+ "ora": "^8.0.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/inquirer": "^9.0.7",
41
+ "@types/node": "^20.10.0",
42
+ "typescript": "^5.3.2"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ }
47
+ }
@@ -0,0 +1,69 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { setToken, setUser, getConfig, clearConfig, isAuthenticated } from '../lib/config.js';
5
+ import { getMe } from '../lib/api.js';
6
+
7
+ export const configCommand = new Command('config')
8
+ .description('Manage CLI configuration');
9
+
10
+ configCommand
11
+ .command('set-token <token>')
12
+ .description('Set the API token manually')
13
+ .action(async (token) => {
14
+ if (!token.startsWith('wk_')) {
15
+ console.log(chalk.yellow('Warning: Token should start with "wk_"'));
16
+ }
17
+
18
+ const spinner = ora('Validating token...').start();
19
+
20
+ // Temporarily set the token to validate it
21
+ setToken(token);
22
+
23
+ const { data, error } = await getMe();
24
+
25
+ if (error) {
26
+ // Clear invalid token
27
+ clearConfig();
28
+ spinner.fail(chalk.red(`Invalid token: ${error}`));
29
+ return;
30
+ }
31
+
32
+ setUser(data!.user);
33
+ spinner.succeed(chalk.green(`Authenticated as ${data!.user.email}`));
34
+
35
+ console.log(chalk.dim('\nYou can now use WorkDynamite CLI commands.'));
36
+ console.log(chalk.dim('Try "workdynamite status" or "workdynamite tasks".\n'));
37
+ });
38
+
39
+ configCommand
40
+ .command('show')
41
+ .description('Show current configuration')
42
+ .action(() => {
43
+ const config = getConfig();
44
+
45
+ console.log(chalk.bold('\nāš™ļø Configuration\n'));
46
+ console.log(chalk.dim('─'.repeat(40)));
47
+
48
+ console.log(` ${chalk.dim('API URL:')} ${config.api_url}`);
49
+ console.log(` ${chalk.dim('Token:')} ${config.token ? `${config.token.substring(0, 12)}...` : chalk.yellow('(not set)')}`);
50
+ console.log(` ${chalk.dim('User:')} ${config.user?.email || chalk.yellow('(not logged in)')}`);
51
+ console.log(` ${chalk.dim('Project:')} ${config.default_project || chalk.dim('(all projects)')}`);
52
+
53
+ console.log(chalk.dim('─'.repeat(40)));
54
+ console.log('');
55
+ });
56
+
57
+ configCommand
58
+ .command('clear')
59
+ .description('Clear all stored configuration')
60
+ .action(() => {
61
+ if (!isAuthenticated()) {
62
+ console.log(chalk.yellow('No configuration to clear.'));
63
+ return;
64
+ }
65
+
66
+ clearConfig();
67
+ console.log(chalk.green('āœ“ Configuration cleared.'));
68
+ console.log(chalk.dim('Run "workdynamite login" to authenticate again.'));
69
+ });
@@ -0,0 +1,116 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import open from 'open';
5
+ import { initLogin, pollLogin, getMe } from '../lib/api.js';
6
+ import { setToken, setUser, isAuthenticated, getConfig } from '../lib/config.js';
7
+
8
+ export const loginCommand = new Command('login')
9
+ .description('Authenticate with WorkDynamite')
10
+ .option('--token <token>', 'Use an existing API token instead of browser auth')
11
+ .action(async (options) => {
12
+ // Check if already authenticated
13
+ if (isAuthenticated()) {
14
+ const config = getConfig();
15
+ console.log(chalk.yellow(`Already logged in as ${config.user?.email}`));
16
+ console.log(chalk.dim('Run "workdynamite logout" to sign out first.'));
17
+ return;
18
+ }
19
+
20
+ // If token provided directly, use it
21
+ if (options.token) {
22
+ const spinner = ora('Validating token...').start();
23
+
24
+ setToken(options.token);
25
+ const { data, error } = await getMe();
26
+
27
+ if (error) {
28
+ setToken('');
29
+ spinner.fail(chalk.red(`Invalid token: ${error}`));
30
+ return;
31
+ }
32
+
33
+ setUser(data!.user);
34
+ spinner.succeed(chalk.green(`Authenticated as ${data!.user.email}`));
35
+ return;
36
+ }
37
+
38
+ // Start device authorization flow
39
+ console.log(chalk.bold('\nšŸš€ WorkDynamite CLI Login\n'));
40
+
41
+ const initSpinner = ora('Initializing login...').start();
42
+
43
+ const { data: initData, error: initError } = await initLogin();
44
+
45
+ if (initError) {
46
+ initSpinner.fail(chalk.red(`Failed to start login: ${initError}`));
47
+ return;
48
+ }
49
+
50
+ initSpinner.succeed('Login initialized');
51
+
52
+ const { device_code, user_code, verification_uri_complete } = initData!;
53
+
54
+ console.log('\n' + chalk.bold('To complete login:'));
55
+ console.log(chalk.dim('─────────────────────────────────────'));
56
+ console.log(`\n 1. Open this URL in your browser:\n`);
57
+ console.log(` ${chalk.cyan.underline(verification_uri_complete)}\n`);
58
+ console.log(` 2. Enter this code if prompted:\n`);
59
+ console.log(` ${chalk.bold.yellow(user_code)}\n`);
60
+ console.log(chalk.dim('─────────────────────────────────────\n'));
61
+
62
+ // Try to open browser automatically
63
+ try {
64
+ await open(verification_uri_complete);
65
+ console.log(chalk.dim('(Browser opened automatically)\n'));
66
+ } catch {
67
+ console.log(chalk.dim('(Open the URL manually)\n'));
68
+ }
69
+
70
+ // Poll for authorization
71
+ const pollSpinner = ora('Waiting for authorization...').start();
72
+
73
+ const maxAttempts = 60; // 5 minutes with 5-second intervals
74
+ let attempts = 0;
75
+
76
+ while (attempts < maxAttempts) {
77
+ await sleep(5000); // Wait 5 seconds between polls
78
+ attempts++;
79
+
80
+ const { data: pollData, error: pollError } = await pollLogin(device_code);
81
+
82
+ if (pollError === 'authorization_pending') {
83
+ pollSpinner.text = `Waiting for authorization... (${Math.floor((maxAttempts - attempts) * 5 / 60)}m remaining)`;
84
+ continue;
85
+ }
86
+
87
+ if (pollError === 'expired_token') {
88
+ pollSpinner.fail(chalk.red('Authorization expired. Please run login again.'));
89
+ return;
90
+ }
91
+
92
+ if (pollError) {
93
+ pollSpinner.fail(chalk.red(`Authorization failed: ${pollError}`));
94
+ return;
95
+ }
96
+
97
+ if (pollData) {
98
+ // Authorization successful - but we need the token from the web UI
99
+ pollSpinner.succeed(chalk.green('Authorization complete!'));
100
+
101
+ console.log(chalk.yellow('\nāš ļø Please copy the API token shown in your browser.'));
102
+ console.log(chalk.dim('Then run: workdynamite config set-token <your-token>\n'));
103
+
104
+ if (pollData.user) {
105
+ console.log(chalk.dim(`Authorized for: ${pollData.user.email}`));
106
+ }
107
+ return;
108
+ }
109
+ }
110
+
111
+ pollSpinner.fail(chalk.red('Authorization timed out. Please try again.'));
112
+ });
113
+
114
+ function sleep(ms: number): Promise<void> {
115
+ return new Promise(resolve => setTimeout(resolve, ms));
116
+ }