@lumoai/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 (61) hide show
  1. package/README.md +90 -0
  2. package/dist/cli/src/commands/auth-login.js +55 -0
  3. package/dist/cli/src/commands/auth-logout.js +14 -0
  4. package/dist/cli/src/commands/doc-bind.js +52 -0
  5. package/dist/cli/src/commands/doc-create.js +138 -0
  6. package/dist/cli/src/commands/doc-delete.js +52 -0
  7. package/dist/cli/src/commands/doc-list.js +91 -0
  8. package/dist/cli/src/commands/doc-move.js +113 -0
  9. package/dist/cli/src/commands/doc-share-list.js +62 -0
  10. package/dist/cli/src/commands/doc-share.js +77 -0
  11. package/dist/cli/src/commands/doc-show.js +99 -0
  12. package/dist/cli/src/commands/doc-unbind.js +47 -0
  13. package/dist/cli/src/commands/doc-unshare.js +71 -0
  14. package/dist/cli/src/commands/doc-update.js +144 -0
  15. package/dist/cli/src/commands/hook.js +21 -0
  16. package/dist/cli/src/commands/milestone-create.js +84 -0
  17. package/dist/cli/src/commands/milestone-delete.js +96 -0
  18. package/dist/cli/src/commands/milestone-list.js +55 -0
  19. package/dist/cli/src/commands/milestone-show.js +106 -0
  20. package/dist/cli/src/commands/milestone-update.js +167 -0
  21. package/dist/cli/src/commands/project-list.js +57 -0
  22. package/dist/cli/src/commands/session-attach.js +80 -0
  23. package/dist/cli/src/commands/session-detach.js +60 -0
  24. package/dist/cli/src/commands/session-status.js +58 -0
  25. package/dist/cli/src/commands/sprint-add.js +62 -0
  26. package/dist/cli/src/commands/sprint-close.js +151 -0
  27. package/dist/cli/src/commands/sprint-create.js +80 -0
  28. package/dist/cli/src/commands/sprint-delete.js +88 -0
  29. package/dist/cli/src/commands/sprint-list.js +85 -0
  30. package/dist/cli/src/commands/sprint-remove.js +57 -0
  31. package/dist/cli/src/commands/sprint-show.js +81 -0
  32. package/dist/cli/src/commands/sprint-start.js +68 -0
  33. package/dist/cli/src/commands/sprint-summary.js +138 -0
  34. package/dist/cli/src/commands/sprint-update.js +148 -0
  35. package/dist/cli/src/commands/task-comment.js +95 -0
  36. package/dist/cli/src/commands/task-context.js +137 -0
  37. package/dist/cli/src/commands/task-create.js +202 -0
  38. package/dist/cli/src/commands/task-list.js +94 -0
  39. package/dist/cli/src/commands/task-show.js +74 -0
  40. package/dist/cli/src/commands/task-update.js +468 -0
  41. package/dist/cli/src/commands/whoami.js +16 -0
  42. package/dist/cli/src/index.js +492 -0
  43. package/dist/cli/src/lib/api.js +33 -0
  44. package/dist/cli/src/lib/browser.js +33 -0
  45. package/dist/cli/src/lib/config.js +92 -0
  46. package/dist/cli/src/lib/doc-input.js +80 -0
  47. package/dist/cli/src/lib/doc-sort-order.js +23 -0
  48. package/dist/cli/src/lib/doc-tree.js +54 -0
  49. package/dist/cli/src/lib/format.js +33 -0
  50. package/dist/cli/src/lib/hook-log.js +81 -0
  51. package/dist/cli/src/lib/hook-runner.js +156 -0
  52. package/dist/cli/src/lib/markdown-tiptap.js +7 -0
  53. package/dist/cli/src/lib/prompt.js +71 -0
  54. package/dist/cli/src/lib/resolve-doc-id.js +34 -0
  55. package/dist/cli/src/lib/resolve-doc.js +27 -0
  56. package/dist/cli/src/lib/resolve-member.js +58 -0
  57. package/dist/cli/src/lib/resolve.js +170 -0
  58. package/dist/cli/src/lib/tag-resolver.js +49 -0
  59. package/dist/shared/src/index.js +35 -0
  60. package/dist/shared/src/markdown-tiptap.js +267 -0
  61. package/package.json +48 -0
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCloseDecisions = buildCloseDecisions;
4
+ exports.formatUnfinishedRefusal = formatUnfinishedRefusal;
5
+ exports.sprintClose = sprintClose;
6
+ const config_1 = require("../lib/config");
7
+ const api_1 = require("../lib/api");
8
+ const resolve_1 = require("../lib/resolve");
9
+ const format_1 = require("../lib/format");
10
+ /**
11
+ * Build the decisions map for POST /api/sprints/<id>/close.
12
+ * Only non-DONE tasks get a decision entry; DONE tasks are excluded entirely.
13
+ */
14
+ function buildCloseDecisions(tasks, mode) {
15
+ const decision = mode === 'move' ? 'moveToNext' : 'backToBacklog';
16
+ const result = {};
17
+ for (const task of tasks) {
18
+ if (task.status !== 'DONE') {
19
+ result[task.id] = decision;
20
+ }
21
+ }
22
+ return result;
23
+ }
24
+ /**
25
+ * Format a refusal message when unfinished tasks exist and no mode flag was given.
26
+ * Filters out DONE tasks before counting and rendering the list.
27
+ */
28
+ function formatUnfinishedRefusal(number, name, tasks) {
29
+ const unfinished = tasks.filter(t => t.status !== 'DONE');
30
+ const lines = [
31
+ `Refusing to close sprint #${number} "${name}": ${unfinished.length} task(s) not DONE.`,
32
+ '',
33
+ (0, format_1.formatTaskListTable)(unfinished),
34
+ '',
35
+ 'To resolve, pass one of:',
36
+ ' --move-all --yes move all unfinished tasks to the next sprint',
37
+ ' --backlog-all --yes return all unfinished tasks to the backlog',
38
+ ' Web UI decide task-by-task at lumo.so',
39
+ ];
40
+ return lines.join('\n');
41
+ }
42
+ async function sprintClose(identifier, opts) {
43
+ // CLI-side mutex: both flags together is an error.
44
+ if (opts.moveAll && opts.backlogAll) {
45
+ console.error('Error: --move-all and --backlog-all are mutually exclusive. Pick one.');
46
+ return 1;
47
+ }
48
+ // CLI-side: mode flags require --yes.
49
+ if ((opts.moveAll || opts.backlogAll) && !opts.yes) {
50
+ const flag = opts.moveAll ? '--move-all' : '--backlog-all';
51
+ console.error(`Error: ${flag} requires --yes to confirm. Re-run with --yes.`);
52
+ return 1;
53
+ }
54
+ const creds = (0, config_1.readCredentials)();
55
+ if (!creds) {
56
+ console.error('Error: not logged in. Run `lumo auth login` first.');
57
+ return 1;
58
+ }
59
+ const envUrl = process.env.LUMO_API_URL?.trim();
60
+ const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
61
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
62
+ const workspaceSlug = creds.workspaceSlug ?? '';
63
+ let resolved;
64
+ try {
65
+ resolved = await (0, resolve_1.resolveSprintId)(base, creds.token, identifier, opts.team, workspaceSlug);
66
+ }
67
+ catch (err) {
68
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
69
+ return 1;
70
+ }
71
+ // Fetch sprint + tasks in parallel so we can validate unfinished tasks.
72
+ let sprintRes;
73
+ let tasksRes;
74
+ try {
75
+ ;
76
+ [sprintRes, tasksRes] = await Promise.all([
77
+ fetch(`${base}/api/sprints/${resolved.id}`, {
78
+ headers: { Authorization: `Bearer ${creds.token}` },
79
+ }),
80
+ fetch(`${base}/api/sprints/${resolved.id}/tasks`, {
81
+ headers: { Authorization: `Bearer ${creds.token}` },
82
+ }),
83
+ ]);
84
+ }
85
+ catch (err) {
86
+ const msg = err instanceof Error ? err.message : String(err);
87
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
88
+ return 1;
89
+ }
90
+ if (sprintRes.status === 401 || tasksRes.status === 401) {
91
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
92
+ return 1;
93
+ }
94
+ if (!sprintRes.ok) {
95
+ console.error(`Error: sprint fetch failed (HTTP ${sprintRes.status})`);
96
+ return 1;
97
+ }
98
+ if (!tasksRes.ok) {
99
+ console.error(`Error: sprint tasks fetch failed (HTTP ${tasksRes.status})`);
100
+ return 1;
101
+ }
102
+ const { sprint } = (await sprintRes.json());
103
+ const { tasks } = (await tasksRes.json());
104
+ const unfinished = tasks.filter(t => t.status !== 'DONE');
105
+ // No mode flag: refuse if there are unfinished tasks.
106
+ if (!opts.moveAll && !opts.backlogAll) {
107
+ if (unfinished.length > 0) {
108
+ console.error(formatUnfinishedRefusal(sprint.number, sprint.name, tasks));
109
+ return 1;
110
+ }
111
+ // All tasks done — post with empty decisions.
112
+ }
113
+ // Build decisions map.
114
+ const decisions = opts.moveAll || opts.backlogAll
115
+ ? buildCloseDecisions(tasks, opts.moveAll ? 'move' : 'backlog')
116
+ : {};
117
+ let res;
118
+ try {
119
+ res = await fetch(`${base}/api/sprints/${resolved.id}/close`, {
120
+ method: 'POST',
121
+ headers: {
122
+ Authorization: `Bearer ${creds.token}`,
123
+ 'Content-Type': 'application/json',
124
+ },
125
+ body: JSON.stringify({ decisions }),
126
+ });
127
+ }
128
+ catch (err) {
129
+ const msg = err instanceof Error ? err.message : String(err);
130
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
131
+ return 1;
132
+ }
133
+ if (res.status === 401) {
134
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
135
+ return 1;
136
+ }
137
+ if (!res.ok) {
138
+ let errMsg = `sprint close failed (HTTP ${res.status})`;
139
+ try {
140
+ const errBody = (await res.json());
141
+ if (errBody.error)
142
+ errMsg = errBody.error;
143
+ }
144
+ catch {
145
+ // body wasn't JSON
146
+ }
147
+ console.error(`Error: ${errMsg}`);
148
+ return 1;
149
+ }
150
+ process.stdout.write(`Closed sprint #${sprint.number} "${sprint.name}"\n`);
151
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCreateSprintPayload = buildCreateSprintPayload;
4
+ exports.formatCreatedSprintLine = formatCreatedSprintLine;
5
+ exports.sprintCreate = sprintCreate;
6
+ const config_1 = require("../lib/config");
7
+ const api_1 = require("../lib/api");
8
+ const resolve_1 = require("../lib/resolve");
9
+ function buildCreateSprintPayload(opts) {
10
+ // start/end are required by the server schema; caller validates presence.
11
+ const payload = {
12
+ startDate: opts.start,
13
+ endDate: opts.end,
14
+ };
15
+ if (opts.name && opts.name.length > 0)
16
+ payload.name = opts.name;
17
+ return payload;
18
+ }
19
+ function formatCreatedSprintLine(sprint) {
20
+ return `Created sprint #${sprint.number} "${sprint.name}" ${sprint.id}`;
21
+ }
22
+ async function sprintCreate(opts) {
23
+ if (!opts.start || !opts.end) {
24
+ console.error('Error: --start and --end are required (YYYY-MM-DD)');
25
+ return 1;
26
+ }
27
+ const creds = (0, config_1.readCredentials)();
28
+ if (!creds) {
29
+ console.error('Error: not logged in. Run `lumo auth login` first.');
30
+ return 1;
31
+ }
32
+ const envUrl = process.env.LUMO_API_URL?.trim();
33
+ const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
34
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
35
+ let teamId;
36
+ try {
37
+ const team = await (0, resolve_1.resolveTeamId)(base, creds.token, opts.team);
38
+ teamId = team.id;
39
+ }
40
+ catch (err) {
41
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
42
+ return 1;
43
+ }
44
+ const payload = buildCreateSprintPayload(opts);
45
+ let res;
46
+ try {
47
+ res = await fetch(`${base}/api/teams/${teamId}/sprints`, {
48
+ method: 'POST',
49
+ headers: {
50
+ Authorization: `Bearer ${creds.token}`,
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ body: JSON.stringify(payload),
54
+ });
55
+ }
56
+ catch (err) {
57
+ const msg = err instanceof Error ? err.message : String(err);
58
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
59
+ return 1;
60
+ }
61
+ if (res.status === 401) {
62
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
63
+ return 1;
64
+ }
65
+ if (!res.ok) {
66
+ let serverMsg = null;
67
+ try {
68
+ const errBody = (await res.json());
69
+ if (typeof errBody.error === 'string')
70
+ serverMsg = errBody.error;
71
+ }
72
+ catch {
73
+ // ignore
74
+ }
75
+ console.error(`Error: ${serverMsg ?? `sprint create failed (HTTP ${res.status})`}`);
76
+ return 1;
77
+ }
78
+ const sprint = (await res.json());
79
+ process.stdout.write(formatCreatedSprintLine(sprint) + '\n');
80
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatDeleteRefusal = formatDeleteRefusal;
4
+ exports.sprintDelete = sprintDelete;
5
+ const config_1 = require("../lib/config");
6
+ const api_1 = require("../lib/api");
7
+ const resolve_1 = require("../lib/resolve");
8
+ function formatDeleteRefusal(number, name, taskCount) {
9
+ const head = `Refusing to delete sprint #${number} "${name}" without --yes.`;
10
+ const tail = `Re-run with --yes to confirm.`;
11
+ if (taskCount === 0)
12
+ return `${head}\n${tail}`;
13
+ return `${head}\n${taskCount} tasks under it will keep their data; only sprintId is cleared.\n${tail}`;
14
+ }
15
+ async function sprintDelete(identifier, opts) {
16
+ const creds = (0, config_1.readCredentials)();
17
+ if (!creds) {
18
+ console.error('Error: not logged in. Run `lumo auth login` first.');
19
+ return 1;
20
+ }
21
+ const envUrl = process.env.LUMO_API_URL?.trim();
22
+ const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
23
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
24
+ const workspaceSlug = creds.workspaceSlug ?? '';
25
+ let resolved;
26
+ try {
27
+ resolved = await (0, resolve_1.resolveSprintId)(base, creds.token, identifier, opts.team, workspaceSlug);
28
+ }
29
+ catch (err) {
30
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
31
+ return 1;
32
+ }
33
+ // Fetch sprint + progress for the refusal message (and to confirm it exists).
34
+ let showRes;
35
+ try {
36
+ showRes = await fetch(`${base}/api/sprints/${resolved.id}`, {
37
+ headers: { Authorization: `Bearer ${creds.token}` },
38
+ });
39
+ }
40
+ catch (err) {
41
+ const msg = err instanceof Error ? err.message : String(err);
42
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
43
+ return 1;
44
+ }
45
+ if (showRes.status === 401) {
46
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
47
+ return 1;
48
+ }
49
+ if (!showRes.ok) {
50
+ console.error(`Error: sprint fetch failed (HTTP ${showRes.status})`);
51
+ return 1;
52
+ }
53
+ const { sprint, progress } = (await showRes.json());
54
+ if (!opts.yes) {
55
+ console.error(formatDeleteRefusal(sprint.number, sprint.name, progress.total));
56
+ return 1;
57
+ }
58
+ let res;
59
+ try {
60
+ res = await fetch(`${base}/api/sprints/${resolved.id}`, {
61
+ method: 'DELETE',
62
+ headers: { Authorization: `Bearer ${creds.token}` },
63
+ });
64
+ }
65
+ catch (err) {
66
+ const msg = err instanceof Error ? err.message : String(err);
67
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
68
+ return 1;
69
+ }
70
+ if (res.status === 401) {
71
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
72
+ return 1;
73
+ }
74
+ if (!res.ok) {
75
+ let errMsg = `sprint delete failed (HTTP ${res.status})`;
76
+ try {
77
+ const errBody = (await res.json());
78
+ if (errBody.error)
79
+ errMsg = errBody.error;
80
+ }
81
+ catch {
82
+ // body wasn't JSON
83
+ }
84
+ console.error(`Error: ${errMsg}`);
85
+ return 1;
86
+ }
87
+ process.stdout.write(`Deleted sprint #${sprint.number} "${sprint.name}"\n`);
88
+ }
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatSprintList = formatSprintList;
4
+ exports.sprintList = sprintList;
5
+ const config_1 = require("../lib/config");
6
+ const api_1 = require("../lib/api");
7
+ const resolve_1 = require("../lib/resolve");
8
+ const VALID_STATUSES = ['DRAFT', 'ACTIVE', 'CLOSED'];
9
+ function formatDate(iso) {
10
+ if (!iso)
11
+ return '-';
12
+ return iso.slice(0, 10);
13
+ }
14
+ /**
15
+ * Render sprints as fixed-width rows (server already sorts by number desc):
16
+ * <STATUS> #<number> <start>..<end> <name>
17
+ */
18
+ function formatSprintList(rows) {
19
+ if (rows.length === 0)
20
+ return 'No sprints.';
21
+ const statusW = Math.max(...rows.map(r => r.status.length));
22
+ const numW = Math.max(...rows.map(r => String(r.number).length));
23
+ return rows
24
+ .map(r => {
25
+ const statusCol = r.status.padEnd(statusW);
26
+ const numCol = `#${r.number}`.padEnd(numW + 1); // +1 for the '#'
27
+ const dateRange = `${formatDate(r.startDate)}..${formatDate(r.endDate)}`;
28
+ return `${statusCol} ${numCol} ${dateRange} ${r.name}`;
29
+ })
30
+ .join('\n');
31
+ }
32
+ async function sprintList(options) {
33
+ // Validate --status if provided
34
+ let normalizedStatus;
35
+ if (options.status !== undefined) {
36
+ normalizedStatus = options.status.toUpperCase();
37
+ if (!VALID_STATUSES.includes(normalizedStatus)) {
38
+ console.error(`Error: invalid --status "${options.status}". Valid values: draft | active | closed`);
39
+ return 1;
40
+ }
41
+ }
42
+ const limit = options.limit !== undefined ? parseInt(options.limit, 10) : undefined;
43
+ if (limit !== undefined && (Number.isNaN(limit) || limit < 1)) {
44
+ console.error(`Error: invalid --limit "${options.limit}" (expected a positive integer)`);
45
+ return 1;
46
+ }
47
+ const creds = (0, config_1.readCredentials)();
48
+ if (!creds) {
49
+ console.error('Error: not logged in. Run `lumo auth login` first.');
50
+ return 1;
51
+ }
52
+ const envUrl = process.env.LUMO_API_URL?.trim();
53
+ const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
54
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
55
+ let teamId;
56
+ try {
57
+ const team = await (0, resolve_1.resolveTeamId)(base, creds.token, options.team);
58
+ teamId = team.id;
59
+ }
60
+ catch (err) {
61
+ const msg = err instanceof Error ? err.message : String(err);
62
+ console.error(`Error: ${msg}`);
63
+ return 1;
64
+ }
65
+ const params = new URLSearchParams();
66
+ if (normalizedStatus)
67
+ params.set('status', normalizedStatus);
68
+ if (limit !== undefined)
69
+ params.set('limit', String(limit));
70
+ const query = params.toString();
71
+ const url = `${base}/api/teams/${teamId}/sprints${query ? `?${query}` : ''}`;
72
+ const res = await fetch(url, {
73
+ headers: { Authorization: `Bearer ${creds.token}` },
74
+ });
75
+ if (res.status === 401) {
76
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
77
+ return 1;
78
+ }
79
+ if (!res.ok) {
80
+ console.error(`Error: sprint list failed (HTTP ${res.status})`);
81
+ return 1;
82
+ }
83
+ const data = (await res.json());
84
+ process.stdout.write(formatSprintList(data.sprints) + '\n');
85
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sprintRemove = sprintRemove;
4
+ const config_1 = require("../lib/config");
5
+ const api_1 = require("../lib/api");
6
+ const resolve_1 = require("../lib/resolve");
7
+ async function sprintRemove(identifier, taskIdentifier, opts) {
8
+ const creds = (0, config_1.readCredentials)();
9
+ if (!creds) {
10
+ console.error('Error: not logged in. Run `lumo auth login` first.');
11
+ return 1;
12
+ }
13
+ const envUrl = process.env.LUMO_API_URL?.trim();
14
+ const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
15
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
16
+ const workspaceSlug = creds.workspaceSlug ?? '';
17
+ let resolved;
18
+ try {
19
+ resolved = await (0, resolve_1.resolveSprintId)(base, creds.token, identifier, opts.team, workspaceSlug);
20
+ }
21
+ catch (err) {
22
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
23
+ return 1;
24
+ }
25
+ const tr = await fetch(`${base}/api/tasks/by-identifier/${encodeURIComponent(taskIdentifier)}`, { headers: { Authorization: `Bearer ${creds.token}` } });
26
+ if (!tr.ok) {
27
+ console.error(`Error: task ${taskIdentifier} not found (HTTP ${tr.status})`);
28
+ return 1;
29
+ }
30
+ const { task } = (await tr.json());
31
+ let res;
32
+ try {
33
+ res = await fetch(`${base}/api/sprints/${resolved.id}/tasks/${task.id}`, {
34
+ method: 'DELETE',
35
+ headers: { Authorization: `Bearer ${creds.token}` },
36
+ });
37
+ }
38
+ catch (err) {
39
+ const msg = err instanceof Error ? err.message : String(err);
40
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
41
+ return 1;
42
+ }
43
+ if (!res.ok && res.status !== 204) {
44
+ let errMsg = `sprint remove failed (HTTP ${res.status})`;
45
+ try {
46
+ const errBody = (await res.json());
47
+ if (errBody.error)
48
+ errMsg = errBody.error;
49
+ }
50
+ catch {
51
+ // ignore
52
+ }
53
+ console.error(`Error: ${errMsg}`);
54
+ return 1;
55
+ }
56
+ process.stdout.write(`Removed ${taskIdentifier} from sprint #${resolved.number}\n`);
57
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatSprintShow = formatSprintShow;
4
+ exports.sprintShow = sprintShow;
5
+ const config_1 = require("../lib/config");
6
+ const api_1 = require("../lib/api");
7
+ const resolve_1 = require("../lib/resolve");
8
+ const format_1 = require("../lib/format");
9
+ function fmtDate(iso) {
10
+ return iso ? iso.slice(0, 10) : '-';
11
+ }
12
+ function formatSprintShow(s, progress, tasks) {
13
+ const lines = [
14
+ `Sprint: #${s.number} ${s.name}`,
15
+ `Status: ${s.status}`,
16
+ `Team: ${s.team.name}`,
17
+ `Start: ${fmtDate(s.startDate)}`,
18
+ `End: ${fmtDate(s.endDate)}`,
19
+ `Started: ${fmtDate(s.startedAt)}`,
20
+ `Closed: ${fmtDate(s.closedAt)}`,
21
+ ``,
22
+ `Progress: ${progress.done} / ${progress.total}`,
23
+ ];
24
+ if (tasks.length > 0) {
25
+ lines.push('', (0, format_1.formatTaskListTable)(tasks));
26
+ }
27
+ return lines.join('\n');
28
+ }
29
+ async function sprintShow(identifier, opts) {
30
+ const creds = (0, config_1.readCredentials)();
31
+ if (!creds) {
32
+ console.error('Error: not logged in. Run `lumo auth login` first.');
33
+ return 1;
34
+ }
35
+ const envUrl = process.env.LUMO_API_URL?.trim();
36
+ const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
37
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
38
+ let sprintId;
39
+ try {
40
+ const resolved = await (0, resolve_1.resolveSprintId)(base, creds.token, identifier, opts.team, creds.workspaceSlug);
41
+ sprintId = resolved.id;
42
+ }
43
+ catch (err) {
44
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
45
+ return 1;
46
+ }
47
+ // Fetch sprint details and tasks in parallel.
48
+ let sprintRes;
49
+ let tasksRes;
50
+ try {
51
+ ;
52
+ [sprintRes, tasksRes] = await Promise.all([
53
+ fetch(`${base}/api/sprints/${sprintId}`, {
54
+ headers: { Authorization: `Bearer ${creds.token}` },
55
+ }),
56
+ fetch(`${base}/api/sprints/${sprintId}/tasks`, {
57
+ headers: { Authorization: `Bearer ${creds.token}` },
58
+ }),
59
+ ]);
60
+ }
61
+ catch (err) {
62
+ const msg = err instanceof Error ? err.message : String(err);
63
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
64
+ return 1;
65
+ }
66
+ if (sprintRes.status === 401 || tasksRes.status === 401) {
67
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
68
+ return 1;
69
+ }
70
+ if (!sprintRes.ok) {
71
+ console.error(`Error: sprint show failed (HTTP ${sprintRes.status})`);
72
+ return 1;
73
+ }
74
+ if (!tasksRes.ok) {
75
+ console.error(`Error: sprint tasks failed (HTTP ${tasksRes.status})`);
76
+ return 1;
77
+ }
78
+ const { sprint, progress } = (await sprintRes.json());
79
+ const { tasks } = (await tasksRes.json());
80
+ process.stdout.write(formatSprintShow(sprint, progress, tasks) + '\n');
81
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sprintStart = sprintStart;
4
+ const config_1 = require("../lib/config");
5
+ const api_1 = require("../lib/api");
6
+ const resolve_1 = require("../lib/resolve");
7
+ async function sprintStart(identifier, opts) {
8
+ const creds = (0, config_1.readCredentials)();
9
+ if (!creds) {
10
+ console.error('Error: not logged in. Run `lumo auth login` first.');
11
+ return 1;
12
+ }
13
+ const envUrl = process.env.LUMO_API_URL?.trim();
14
+ const apiUrl = envUrl && envUrl.length > 0 ? envUrl : creds.apiUrl;
15
+ const base = (0, api_1.trimTrailingSlash)(apiUrl);
16
+ const workspaceSlug = creds.workspaceSlug ?? '';
17
+ let resolved;
18
+ try {
19
+ resolved = await (0, resolve_1.resolveSprintId)(base, creds.token, identifier, opts.team, workspaceSlug);
20
+ }
21
+ catch (err) {
22
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
23
+ return 1;
24
+ }
25
+ // Fill in number/name if identifier was a UUID.
26
+ let displayNumber = resolved.number;
27
+ let displayName = resolved.name;
28
+ if (!displayName) {
29
+ const r = await fetch(`${base}/api/sprints/${resolved.id}`, {
30
+ headers: { Authorization: `Bearer ${creds.token}` },
31
+ });
32
+ if (r.ok) {
33
+ const { sprint } = (await r.json());
34
+ displayNumber = sprint.number;
35
+ displayName = sprint.name;
36
+ }
37
+ }
38
+ let res;
39
+ try {
40
+ res = await fetch(`${base}/api/sprints/${resolved.id}/start`, {
41
+ method: 'POST',
42
+ headers: { Authorization: `Bearer ${creds.token}` },
43
+ });
44
+ }
45
+ catch (err) {
46
+ const msg = err instanceof Error ? err.message : String(err);
47
+ console.error(`Error: could not reach Lumo API at ${apiUrl} (${msg})`);
48
+ return 1;
49
+ }
50
+ if (res.status === 401) {
51
+ console.error('Error: API key invalid or revoked. Run `lumo auth login`.');
52
+ return 1;
53
+ }
54
+ if (!res.ok) {
55
+ let errMsg = `sprint start failed (HTTP ${res.status})`;
56
+ try {
57
+ const errBody = (await res.json());
58
+ if (errBody.error)
59
+ errMsg = errBody.error;
60
+ }
61
+ catch {
62
+ // ignore
63
+ }
64
+ console.error(`Error: ${errMsg}`);
65
+ return 1;
66
+ }
67
+ process.stdout.write(`Started sprint #${displayNumber} "${displayName}"\n`);
68
+ }