@iamcoder18/huly-cli 0.1.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/README.md +2576 -0
- package/bin/huly +9 -0
- package/dist/auth/cache.js +129 -0
- package/dist/auth/cache.js.map +1 -0
- package/dist/auth/client.js +192 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/env.js +101 -0
- package/dist/auth/env.js.map +1 -0
- package/dist/auth/prompts.js +68 -0
- package/dist/auth/prompts.js.map +1 -0
- package/dist/cli.js +1959 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/dry-run.js +39 -0
- package/dist/commands/dry-run.js.map +1 -0
- package/dist/commands/login.js +92 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/whoami.js +64 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/output/errors.js +99 -0
- package/dist/output/errors.js.map +1 -0
- package/dist/output/format.js +607 -0
- package/dist/output/format.js.map +1 -0
- package/dist/output/progress.js +30 -0
- package/dist/output/progress.js.map +1 -0
- package/dist/raw/api.js +67 -0
- package/dist/raw/api.js.map +1 -0
- package/dist/raw/ws.js +157 -0
- package/dist/raw/ws.js.map +1 -0
- package/dist/resources/_helpers.js +258 -0
- package/dist/resources/_helpers.js.map +1 -0
- package/dist/resources/_project-resolve.js +24 -0
- package/dist/resources/_project-resolve.js.map +1 -0
- package/dist/resources/calendar.js +659 -0
- package/dist/resources/calendar.js.map +1 -0
- package/dist/resources/card.js +358 -0
- package/dist/resources/card.js.map +1 -0
- package/dist/resources/channel.js +709 -0
- package/dist/resources/channel.js.map +1 -0
- package/dist/resources/comment.js +142 -0
- package/dist/resources/comment.js.map +1 -0
- package/dist/resources/component.js +154 -0
- package/dist/resources/component.js.map +1 -0
- package/dist/resources/document.js +584 -0
- package/dist/resources/document.js.map +1 -0
- package/dist/resources/issue-template.js +228 -0
- package/dist/resources/issue-template.js.map +1 -0
- package/dist/resources/issue.js +909 -0
- package/dist/resources/issue.js.map +1 -0
- package/dist/resources/milestone.js +177 -0
- package/dist/resources/milestone.js.map +1 -0
- package/dist/resources/misc.js +2 -0
- package/dist/resources/misc.js.map +1 -0
- package/dist/resources/project.js +341 -0
- package/dist/resources/project.js.map +1 -0
- package/dist/resources/project.parse.js +25 -0
- package/dist/resources/project.parse.js.map +1 -0
- package/dist/resources/time.js +148 -0
- package/dist/resources/time.js.map +1 -0
- package/dist/resources/todo.js +463 -0
- package/dist/resources/todo.js.map +1 -0
- package/dist/resources/user.js +131 -0
- package/dist/resources/user.js.map +1 -0
- package/dist/resources/workspace.js +252 -0
- package/dist/resources/workspace.js.map +1 -0
- package/dist/transport/identifiers.js +67 -0
- package/dist/transport/identifiers.js.map +1 -0
- package/dist/transport/ref-resolver.js +108 -0
- package/dist/transport/ref-resolver.js.map +1 -0
- package/dist/transport/sdk.js +69 -0
- package/dist/transport/sdk.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const useColor = !process.env.NO_COLOR && process.stdout.isTTY !== false;
|
|
3
|
+
const noColor = (s) => s;
|
|
4
|
+
const c = useColor ? chalk : Object.fromEntries(Object.keys(chalk).map((k) => [k, noColor]));
|
|
5
|
+
export function dim(s) { return useColor ? chalk.dim(s) : s; }
|
|
6
|
+
export function bold(s) { return useColor ? chalk.bold(s) : s; }
|
|
7
|
+
export function italic(s) { return useColor ? chalk.italic(s) : s; }
|
|
8
|
+
export function red(s) { return useColor ? chalk.red(s) : s; }
|
|
9
|
+
export function green(s) { return useColor ? chalk.green(s) : s; }
|
|
10
|
+
export function yellow(s) { return useColor ? chalk.yellow(s) : s; }
|
|
11
|
+
export function blue(s) { return useColor ? chalk.blue(s) : s; }
|
|
12
|
+
export function cyan(s) { return useColor ? chalk.cyan(s) : s; }
|
|
13
|
+
export function gray(s) { return useColor ? chalk.gray(s) : s; }
|
|
14
|
+
export function magenta(s) { return useColor ? chalk.magenta(s) : s; }
|
|
15
|
+
export const C = {
|
|
16
|
+
dim, bold, italic, red, green, yellow, blue, cyan, gray, magenta,
|
|
17
|
+
ok: (s) => useColor ? chalk.green('✓ ' + s) : '✓ ' + s,
|
|
18
|
+
fail: (s) => useColor ? chalk.red('✗ ' + s) : '✗ ' + s,
|
|
19
|
+
warn: (s) => useColor ? chalk.yellow('⚠ ' + s) : '⚠ ' + s,
|
|
20
|
+
info: (s) => useColor ? chalk.cyan('ℹ ' + s) : 'ℹ ' + s,
|
|
21
|
+
bullet: (s) => useColor ? chalk.cyan('● ') + s : '● ' + s,
|
|
22
|
+
arrow: (s) => useColor ? chalk.cyan('→ ') + s : '→ ' + s,
|
|
23
|
+
id: (s) => useColor ? chalk.gray(s) : s,
|
|
24
|
+
primary: (s) => useColor ? chalk.bold.cyan(s) : s,
|
|
25
|
+
emphasis: (s) => useColor ? chalk.bold(s) : s,
|
|
26
|
+
muted: (s) => useColor ? chalk.gray(s) : s,
|
|
27
|
+
none: () => useColor ? chalk.gray('—') : '—',
|
|
28
|
+
empty: () => useColor ? chalk.gray('(empty)') : '(empty)'
|
|
29
|
+
};
|
|
30
|
+
export function json(data) {
|
|
31
|
+
console.log(JSON.stringify(data, null, 2));
|
|
32
|
+
}
|
|
33
|
+
const SHARP = useColor ? '│' : '|';
|
|
34
|
+
const THIN = useColor ? '─' : '-';
|
|
35
|
+
const TLCOR = useColor ? '┌' : '+';
|
|
36
|
+
const TRCOR = useColor ? '┐' : '+';
|
|
37
|
+
const BLCOR = useColor ? '└' : '+';
|
|
38
|
+
const BRCOR = useColor ? '┘' : '+';
|
|
39
|
+
const TMID = useColor ? '┬' : '+';
|
|
40
|
+
const BMID = useColor ? '┴' : '+';
|
|
41
|
+
const LMID = useColor ? '├' : '+';
|
|
42
|
+
const RMID = useColor ? '┤' : '+';
|
|
43
|
+
const CROSS = useColor ? '┼' : '+';
|
|
44
|
+
function stripRef(value) {
|
|
45
|
+
if (value == null)
|
|
46
|
+
return '';
|
|
47
|
+
const s = String(value);
|
|
48
|
+
const parts = s.split(':');
|
|
49
|
+
return parts[parts.length - 1] ?? s;
|
|
50
|
+
}
|
|
51
|
+
function shortId(id) {
|
|
52
|
+
const s = String(id ?? '');
|
|
53
|
+
if (s === '')
|
|
54
|
+
return '';
|
|
55
|
+
// For ref-style IDs like "tracker:project:TEST" or "space:General",
|
|
56
|
+
// return the last segment for readability. Otherwise return the last 12 chars.
|
|
57
|
+
if (s.includes(':'))
|
|
58
|
+
return s.split(':').slice(-1)[0] ?? s;
|
|
59
|
+
return s.length > 12 ? s.slice(-12) : s;
|
|
60
|
+
}
|
|
61
|
+
function trim(s, n) {
|
|
62
|
+
return String(s ?? '').slice(0, n);
|
|
63
|
+
}
|
|
64
|
+
function ellipsize(s, w) {
|
|
65
|
+
if (s.length <= w)
|
|
66
|
+
return s;
|
|
67
|
+
if (w <= 1)
|
|
68
|
+
return s.slice(0, w);
|
|
69
|
+
return s.slice(0, w - 1) + '…';
|
|
70
|
+
}
|
|
71
|
+
function isoDate(ms) {
|
|
72
|
+
if (ms == null)
|
|
73
|
+
return '';
|
|
74
|
+
return new Date(Number(ms)).toISOString().slice(0, 16).replace('T', ' ');
|
|
75
|
+
}
|
|
76
|
+
function isoDay(ms) {
|
|
77
|
+
if (ms == null)
|
|
78
|
+
return '';
|
|
79
|
+
return new Date(Number(ms)).toISOString().slice(0, 10);
|
|
80
|
+
}
|
|
81
|
+
function relTime(ms) {
|
|
82
|
+
if (ms == null)
|
|
83
|
+
return C.none();
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
const diff = now - Number(ms);
|
|
86
|
+
if (diff < 0)
|
|
87
|
+
return isoDay(ms);
|
|
88
|
+
if (diff < 60_000)
|
|
89
|
+
return 'just now';
|
|
90
|
+
if (diff < 3_600_000)
|
|
91
|
+
return `${Math.floor(diff / 60_000)}m ago`;
|
|
92
|
+
if (diff < 86_400_000)
|
|
93
|
+
return `${Math.floor(diff / 3_600_000)}h ago`;
|
|
94
|
+
if (diff < 604_800_000)
|
|
95
|
+
return `${Math.floor(diff / 86_400_000)}d ago`;
|
|
96
|
+
if (diff < 2_592_000_000)
|
|
97
|
+
return `${Math.floor(diff / 604_800_000)}w ago`;
|
|
98
|
+
if (diff < 31_536_000_000)
|
|
99
|
+
return `${Math.floor(diff / 2_592_000_000)}mo ago`;
|
|
100
|
+
return `${Math.floor(diff / 31_536_000_000)}y ago`;
|
|
101
|
+
}
|
|
102
|
+
const PRIORITY_COLORS = {
|
|
103
|
+
Urgent: (s) => useColor ? chalk.red.bold(s) : s,
|
|
104
|
+
High: (s) => useColor ? chalk.red(s) : s,
|
|
105
|
+
Normal: (s) => useColor ? chalk.yellow(s) : s,
|
|
106
|
+
Low: (s) => useColor ? chalk.gray(s) : s,
|
|
107
|
+
None: (s) => useColor ? chalk.gray(s) : s
|
|
108
|
+
};
|
|
109
|
+
const STATUS_CATEGORY_COLORS = {
|
|
110
|
+
Won: (s) => useColor ? chalk.green(s) : s,
|
|
111
|
+
Lost: (s) => useColor ? chalk.red(s) : s,
|
|
112
|
+
Active: (s) => useColor ? chalk.cyan(s) : s,
|
|
113
|
+
ToDo: (s) => useColor ? chalk.yellow(s) : s,
|
|
114
|
+
UnStarted: (s) => useColor ? chalk.gray(s) : s
|
|
115
|
+
};
|
|
116
|
+
function colorizeStatus(s) {
|
|
117
|
+
if (!s)
|
|
118
|
+
return C.none();
|
|
119
|
+
const stripped = stripRef(s);
|
|
120
|
+
for (const [cat, fn] of Object.entries(STATUS_CATEGORY_COLORS)) {
|
|
121
|
+
if (cat.toLowerCase() === stripped.toLowerCase())
|
|
122
|
+
return fn(stripped);
|
|
123
|
+
}
|
|
124
|
+
return stripped;
|
|
125
|
+
}
|
|
126
|
+
function colorizePriority(s) {
|
|
127
|
+
if (!s)
|
|
128
|
+
return C.none();
|
|
129
|
+
const stripped = stripRef(s);
|
|
130
|
+
for (const [pri, fn] of Object.entries(PRIORITY_COLORS)) {
|
|
131
|
+
if (pri.toLowerCase() === stripped.toLowerCase())
|
|
132
|
+
return fn(stripped);
|
|
133
|
+
}
|
|
134
|
+
return stripped;
|
|
135
|
+
}
|
|
136
|
+
const STATUS_GLYPH = {
|
|
137
|
+
Won: '✓',
|
|
138
|
+
Lost: '✗',
|
|
139
|
+
Active: '●',
|
|
140
|
+
ToDo: '○',
|
|
141
|
+
UnStarted: '◌'
|
|
142
|
+
};
|
|
143
|
+
function statusGlyph(s) {
|
|
144
|
+
if (!s)
|
|
145
|
+
return C.muted('—');
|
|
146
|
+
const stripped = stripRef(s);
|
|
147
|
+
for (const [cat, glyph] of Object.entries(STATUS_GLYPH)) {
|
|
148
|
+
if (cat.toLowerCase() === stripped.toLowerCase())
|
|
149
|
+
return glyph;
|
|
150
|
+
}
|
|
151
|
+
return '·';
|
|
152
|
+
}
|
|
153
|
+
const PRIORITY_GLYPH = {
|
|
154
|
+
Urgent: '↑↑↑',
|
|
155
|
+
High: '↑↑',
|
|
156
|
+
Normal: '↑',
|
|
157
|
+
Low: '↓',
|
|
158
|
+
None: '·'
|
|
159
|
+
};
|
|
160
|
+
function priorityGlyph(s) {
|
|
161
|
+
if (!s)
|
|
162
|
+
return C.muted('—');
|
|
163
|
+
const stripped = stripRef(s);
|
|
164
|
+
for (const [pri, glyph] of Object.entries(PRIORITY_GLYPH)) {
|
|
165
|
+
if (pri.toLowerCase() === stripped.toLowerCase())
|
|
166
|
+
return glyph;
|
|
167
|
+
}
|
|
168
|
+
return '·';
|
|
169
|
+
}
|
|
170
|
+
function calcWidth(rows, header, requested) {
|
|
171
|
+
if (requested !== undefined)
|
|
172
|
+
return requested;
|
|
173
|
+
let w = header.length;
|
|
174
|
+
for (const r of rows) {
|
|
175
|
+
const cellLen = stripAnsi(r.join('')).length;
|
|
176
|
+
if (cellLen > w)
|
|
177
|
+
w = cellLen;
|
|
178
|
+
}
|
|
179
|
+
return Math.min(Math.max(w, 3), 60);
|
|
180
|
+
}
|
|
181
|
+
function stripAnsi(s) {
|
|
182
|
+
return s.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
|
|
183
|
+
}
|
|
184
|
+
function padCell(content, width, align = 'left') {
|
|
185
|
+
const len = stripAnsi(content).length;
|
|
186
|
+
if (len >= width)
|
|
187
|
+
return content;
|
|
188
|
+
const pad = width - len;
|
|
189
|
+
if (align === 'right')
|
|
190
|
+
return ' '.repeat(pad) + content;
|
|
191
|
+
if (align === 'center') {
|
|
192
|
+
const left = Math.floor(pad / 2);
|
|
193
|
+
return ' '.repeat(left) + content + ' '.repeat(pad - left);
|
|
194
|
+
}
|
|
195
|
+
return content + ' '.repeat(pad);
|
|
196
|
+
}
|
|
197
|
+
function renderRow(values, widths, aligns, border, useBox) {
|
|
198
|
+
const cells = values.map((v, i) => padCell(v, widths[i] ?? 0, aligns[i] ?? 'left'));
|
|
199
|
+
if (border) {
|
|
200
|
+
return (useBox ? SHARP : '|') + ' ' + cells.join(' ' + (useBox ? SHARP : '|') + ' ') + ' ' + (useBox ? SHARP : '|');
|
|
201
|
+
}
|
|
202
|
+
return ' ' + cells.join(' ');
|
|
203
|
+
}
|
|
204
|
+
function renderSep(widths, border, left, mid, right, useBox) {
|
|
205
|
+
if (!border)
|
|
206
|
+
return dim(widths.map((w) => '─'.repeat(w)).join(' '));
|
|
207
|
+
const sep = (useBox ? THIN : '-').repeat;
|
|
208
|
+
const parts = widths.map((w) => sep(w + 2));
|
|
209
|
+
const j = useBox ? TMID : '+';
|
|
210
|
+
return left + parts.join(j) + right;
|
|
211
|
+
}
|
|
212
|
+
export function table(rows, columns, opts = {}) {
|
|
213
|
+
if (rows.length === 0) {
|
|
214
|
+
const msg = '(no results)';
|
|
215
|
+
if (opts.title !== undefined) {
|
|
216
|
+
console.log();
|
|
217
|
+
console.log(C.muted(opts.title));
|
|
218
|
+
}
|
|
219
|
+
console.log(dim(' ' + msg));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const rendered = rows.map((row) => columns.map((col) => {
|
|
223
|
+
try {
|
|
224
|
+
const v = col.format ? col.format(row) : row[col.key] == null ? '' : String(row[col.key]);
|
|
225
|
+
return col.color ? col.color(row) ?? v : v;
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return '';
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
231
|
+
const aligns = columns.map((c) => c.align ?? 'left');
|
|
232
|
+
const widths = columns.map((c, i) => calcWidth(rendered.map((r) => [r[i] ?? '']), c.header, c.width));
|
|
233
|
+
const border = !opts.noBorder;
|
|
234
|
+
if (opts.title !== undefined) {
|
|
235
|
+
const title = C.bold(' ' + opts.title);
|
|
236
|
+
if (border) {
|
|
237
|
+
const totalWidth = widths.reduce((a, b) => a + b + 3, -1);
|
|
238
|
+
console.log(TLCOR + THIN.repeat(totalWidth) + TRCOR);
|
|
239
|
+
console.log(SHARP + ' ' + C.emphasis(opts.title) + ' '.repeat(Math.max(0, totalWidth - stripAnsi(opts.title).length - 1)) + SHARP);
|
|
240
|
+
console.log(LMID + renderSep(widths, false, '', CROSS, RMID, true).slice(1, -1).replace(/[┌┐]/g, '├').replace(/[─┬]/g, '─'));
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
console.log(title);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const headerRow = renderRow(columns.map((c) => c.header), widths, aligns, border, true);
|
|
247
|
+
console.log(border ? SHARP + ' ' + columns.map((c, i) => C.emphasis(padCell(c.header, widths[i] ?? 0, aligns[i] ?? 'left'))).join(' ' + SHARP + ' ') + ' ' + SHARP : ' ' + columns.map((c, i) => C.emphasis(padCell(c.header, widths[i] ?? 0, aligns[i] ?? 'left'))).join(' '));
|
|
248
|
+
if (border) {
|
|
249
|
+
const sep = widths.map((w) => (useColor ? '─' : '-').repeat(w + 2));
|
|
250
|
+
const j = useColor ? '┼' : '+';
|
|
251
|
+
console.log(LMID + sep.join(j) + RMID);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.log(renderSep(widths, false, '', '', '', true));
|
|
255
|
+
}
|
|
256
|
+
for (const r of rendered) {
|
|
257
|
+
console.log(renderRow(r, widths, aligns, border, true));
|
|
258
|
+
}
|
|
259
|
+
if (border) {
|
|
260
|
+
const sep = widths.map((w) => (useColor ? '─' : '-').repeat(w + 2));
|
|
261
|
+
const j = useColor ? '┴' : '+';
|
|
262
|
+
console.log(BLCOR + sep.join(j) + BRCOR);
|
|
263
|
+
}
|
|
264
|
+
if (opts.count === true) {
|
|
265
|
+
const countText = `${rows.length} ${rows.length === 1 ? 'result' : 'results'}`;
|
|
266
|
+
console.log(C.muted(' ' + countText));
|
|
267
|
+
}
|
|
268
|
+
if (opts.footer !== undefined) {
|
|
269
|
+
console.log(C.muted(' ' + opts.footer));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
export function kv(rows, opts = {}) {
|
|
273
|
+
if (rows.length === 0)
|
|
274
|
+
return;
|
|
275
|
+
if (opts.title !== undefined) {
|
|
276
|
+
console.log(C.emphasis(opts.title));
|
|
277
|
+
}
|
|
278
|
+
const w = Math.max(...rows.map(([k]) => k.length));
|
|
279
|
+
for (const [k, v] of rows) {
|
|
280
|
+
const value = v == null || v === '' ? C.none() : v;
|
|
281
|
+
console.log(` ${C.muted(k.padEnd(w))} ${value}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
export function panel(title, lines) {
|
|
285
|
+
const titleLen = stripAnsi(title).length;
|
|
286
|
+
const maxLen = Math.max(titleLen, ...lines.map((l) => stripAnsi(l).length));
|
|
287
|
+
const width = Math.min(maxLen + 4, 100);
|
|
288
|
+
const top = ' ' + TLCOR + THIN.repeat(width) + TRCOR;
|
|
289
|
+
const bottom = ' ' + BLCOR + THIN.repeat(width) + BRCOR;
|
|
290
|
+
const titleLine = ' ' + SHARP + ' ' + C.emphasis(title.padEnd(width - 2)) + ' ' + SHARP;
|
|
291
|
+
console.log(top);
|
|
292
|
+
console.log(titleLine);
|
|
293
|
+
for (const line of lines) {
|
|
294
|
+
const padded = line.padEnd(width - 2);
|
|
295
|
+
console.log(' ' + SHARP + ' ' + padded + ' ' + SHARP);
|
|
296
|
+
}
|
|
297
|
+
console.log(bottom);
|
|
298
|
+
}
|
|
299
|
+
export function header(title, opts = {}) {
|
|
300
|
+
const accent = opts.accent !== undefined ? ` ${C.dim('·')} ${opts.accent}` : '';
|
|
301
|
+
console.log();
|
|
302
|
+
console.log(C.primary('━'.repeat(3) + ' ' + title) + accent);
|
|
303
|
+
if (opts.subtitle !== undefined) {
|
|
304
|
+
console.log(C.muted(' ' + opts.subtitle));
|
|
305
|
+
}
|
|
306
|
+
console.log();
|
|
307
|
+
}
|
|
308
|
+
export function section(title) {
|
|
309
|
+
console.log();
|
|
310
|
+
console.log(C.emphasis(title));
|
|
311
|
+
console.log(C.muted('─'.repeat(Math.max(8, title.length + 2))));
|
|
312
|
+
}
|
|
313
|
+
export function fail(msg) { console.log(C.fail(msg)); }
|
|
314
|
+
export function warn(msg) { console.log(C.warn(msg)); }
|
|
315
|
+
export function info(msg) { console.log(C.info(msg)); }
|
|
316
|
+
export function shouldJson(opts) {
|
|
317
|
+
return Boolean(opts.json || opts.ci || process.env.CI);
|
|
318
|
+
}
|
|
319
|
+
export async function withTimeout(p, ms, fallback) {
|
|
320
|
+
let timer;
|
|
321
|
+
const timeout = new Promise((resolve) => {
|
|
322
|
+
timer = setTimeout(() => resolve(fallback), ms);
|
|
323
|
+
});
|
|
324
|
+
try {
|
|
325
|
+
return await Promise.race([p, timeout]);
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
if (timer !== undefined)
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
export { shortId, trim, stripRef, isoDate, isoDay, relTime, colorizeStatus, colorizePriority, statusGlyph, priorityGlyph };
|
|
333
|
+
export function success(kind, name, id) {
|
|
334
|
+
const line = C.ok(kind) + C.muted(' ') + C.emphasis(name) + (id != null ? C.muted(' ') + C.id(`(${id})`) : '');
|
|
335
|
+
console.log(line);
|
|
336
|
+
}
|
|
337
|
+
export function updated(kind, id) {
|
|
338
|
+
const line = C.info(kind) + C.muted(' ') + C.id(`(${id})`);
|
|
339
|
+
console.log(line);
|
|
340
|
+
}
|
|
341
|
+
export function removed(kind, name, id) {
|
|
342
|
+
const line = C.fail(kind) + C.muted(' ') + C.emphasis(name) + (id != null ? C.muted(' ') + C.id(`(${id})`) : '');
|
|
343
|
+
console.log(line);
|
|
344
|
+
}
|
|
345
|
+
export function bulkRemoved(deleted, skipped, kind = 'items') {
|
|
346
|
+
if (deleted === 0 && skipped === 0) {
|
|
347
|
+
console.log(C.muted(` (nothing to delete)`));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const parts = [];
|
|
351
|
+
if (deleted > 0)
|
|
352
|
+
parts.push(C.ok(`${deleted} ${kind} deleted`));
|
|
353
|
+
if (skipped > 0)
|
|
354
|
+
parts.push(C.warn(`${skipped} skipped`));
|
|
355
|
+
console.log(' ' + parts.join(C.muted(' · ')));
|
|
356
|
+
}
|
|
357
|
+
export const COLUMNS = {
|
|
358
|
+
idShort: () => [
|
|
359
|
+
{ key: '_id', header: '_ID', format: (r) => C.id(shortId(r._id)) }
|
|
360
|
+
],
|
|
361
|
+
issue: () => [
|
|
362
|
+
{ key: 'identifier', header: 'ID', width: 14, align: 'left', format: (r) => {
|
|
363
|
+
const v = r.identifier;
|
|
364
|
+
if (v != null && v !== '')
|
|
365
|
+
return C.emphasis(String(v));
|
|
366
|
+
// Fall back to a short id when the project sequence hasn't been assigned
|
|
367
|
+
// (e.g. legacy issues, or issues created via raw tx without an increment)
|
|
368
|
+
const id = r._id;
|
|
369
|
+
if (id != null && id !== '')
|
|
370
|
+
return C.muted('#' + shortId(id));
|
|
371
|
+
return C.muted('—');
|
|
372
|
+
} },
|
|
373
|
+
{ key: 'title', header: 'TITLE', format: (r) => {
|
|
374
|
+
const t = trim(r.title, 80);
|
|
375
|
+
return t || C.muted('(untitled)');
|
|
376
|
+
} },
|
|
377
|
+
{ key: 'status', header: 'STATUS', width: 14, format: (r) => {
|
|
378
|
+
const s = r.status;
|
|
379
|
+
return `${statusGlyph(String(s ?? ''))} ${colorizeStatus(String(s ?? ''))}`;
|
|
380
|
+
} },
|
|
381
|
+
{ key: 'priority', header: 'PRIORITY', width: 11, align: 'center', format: (r) => {
|
|
382
|
+
const p = r.priority;
|
|
383
|
+
return priorityGlyph(String(p ?? ''));
|
|
384
|
+
} },
|
|
385
|
+
{ key: 'updatedOn', header: 'UPDATED', width: 11, align: 'right', format: (r) => relTime(r.modifiedOn) }
|
|
386
|
+
],
|
|
387
|
+
issueTemplate: () => [
|
|
388
|
+
{ key: 'title', header: 'TITLE', format: (r) => {
|
|
389
|
+
const t = trim(r.title, 80);
|
|
390
|
+
return t || C.muted('(untitled)');
|
|
391
|
+
} },
|
|
392
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
393
|
+
],
|
|
394
|
+
project: () => [
|
|
395
|
+
{ key: 'identifier', header: 'ID', width: 8, align: 'left', format: (r) => {
|
|
396
|
+
const v = r.identifier;
|
|
397
|
+
return v != null && v !== '' ? C.emphasis(String(v)) : C.muted('—');
|
|
398
|
+
} },
|
|
399
|
+
{ key: 'name', header: 'NAME', format: (r) => trim(r.name, 60) || C.muted('(no name)') },
|
|
400
|
+
{ key: 'description', header: 'DESCRIPTION', format: (r) => {
|
|
401
|
+
const d = String(r.description ?? '').trim();
|
|
402
|
+
if (!d)
|
|
403
|
+
return C.muted('—');
|
|
404
|
+
return trim(d, 50);
|
|
405
|
+
} },
|
|
406
|
+
{ key: 'archived', header: 'STATE', width: 8, align: 'center', format: (r) => {
|
|
407
|
+
const a = r.archived;
|
|
408
|
+
return a ? C.red('archived') : C.green('active');
|
|
409
|
+
} },
|
|
410
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
411
|
+
],
|
|
412
|
+
card: () => [
|
|
413
|
+
{ key: 'title', header: 'TITLE', format: (r) => {
|
|
414
|
+
const t = trim(r.title, 80);
|
|
415
|
+
return t || C.muted('(untitled)');
|
|
416
|
+
} },
|
|
417
|
+
{ key: 'status', header: 'STATUS', format: (r) => {
|
|
418
|
+
const s = r.status;
|
|
419
|
+
return s != null && s !== '' ? colorizeStatus(String(s)) : C.muted('—');
|
|
420
|
+
} },
|
|
421
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
422
|
+
],
|
|
423
|
+
task: () => [
|
|
424
|
+
{ key: 'title', header: 'TITLE', format: (r) => trim(r.title, 80) || C.muted('(untitled)') },
|
|
425
|
+
{ key: 'status', header: 'STATUS', format: (r) => {
|
|
426
|
+
const s = r.status;
|
|
427
|
+
return s != null && s !== '' ? colorizeStatus(String(s)) : C.muted('—');
|
|
428
|
+
} },
|
|
429
|
+
{ key: 'assignee', header: 'ASSIGNEE', format: (r) => {
|
|
430
|
+
const a = r.assignee;
|
|
431
|
+
return a != null && a !== '' ? String(a) : C.muted('unassigned');
|
|
432
|
+
} },
|
|
433
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
434
|
+
],
|
|
435
|
+
document: () => [
|
|
436
|
+
{ key: 'title', header: 'TITLE', format: (r) => trim(r.title, 80) || C.muted('(untitled)') },
|
|
437
|
+
{ key: 'modifiedOn', header: 'UPDATED', width: 12, format: (r) => relTime(r.modifiedOn) },
|
|
438
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
439
|
+
],
|
|
440
|
+
event: () => [
|
|
441
|
+
{ key: 'title', header: 'TITLE', format: (r) => trim(r.title, 60) || C.muted('(untitled)') },
|
|
442
|
+
{ key: 'startDate', header: 'START', width: 17, format: (r) => {
|
|
443
|
+
const s = r.startDate;
|
|
444
|
+
return s != null ? isoDate(s) : C.muted('—');
|
|
445
|
+
} },
|
|
446
|
+
{ key: 'dueDate', header: 'END', width: 17, format: (r) => {
|
|
447
|
+
const e = r.dueDate;
|
|
448
|
+
return e != null ? isoDate(e) : C.muted('—');
|
|
449
|
+
} },
|
|
450
|
+
{ key: 'location', header: 'LOCATION', format: (r) => {
|
|
451
|
+
const l = r.location;
|
|
452
|
+
return l != null && l !== '' ? String(l) : C.muted('—');
|
|
453
|
+
} },
|
|
454
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
455
|
+
],
|
|
456
|
+
comment: () => [
|
|
457
|
+
{ key: 'message', header: 'MESSAGE', format: (r) => {
|
|
458
|
+
const m = r.message;
|
|
459
|
+
if (m == null)
|
|
460
|
+
return C.muted('(empty)');
|
|
461
|
+
if (typeof m === 'string')
|
|
462
|
+
return trim(m, 80);
|
|
463
|
+
if (typeof m === 'object' && Object.keys(m).length === 0)
|
|
464
|
+
return C.muted('(empty — use `huly comment get <id>`)');
|
|
465
|
+
const content = m.content;
|
|
466
|
+
return trim(typeof content === 'string' ? content : '', 80) || C.muted('(no content)');
|
|
467
|
+
} },
|
|
468
|
+
{ key: 'createdOn', header: 'CREATED', width: 12, format: (r) => relTime(r.createdOn) },
|
|
469
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
470
|
+
],
|
|
471
|
+
channel: () => [
|
|
472
|
+
{ key: 'name', header: 'NAME', format: (r) => {
|
|
473
|
+
const n = r.name;
|
|
474
|
+
return n != null && n !== '' ? '# ' + String(n) : C.muted('(no name)');
|
|
475
|
+
} },
|
|
476
|
+
{ key: 'topic', header: 'TOPIC', format: (r) => {
|
|
477
|
+
const t = r.topic;
|
|
478
|
+
return t != null && t !== '' ? trim(t, 60) : C.muted('—');
|
|
479
|
+
} },
|
|
480
|
+
{ key: 'members', header: 'MEMBERS', width: 8, align: 'right', format: (r) => {
|
|
481
|
+
const m = r.members;
|
|
482
|
+
return m != null ? String(Array.isArray(m) ? m.length : 0) : C.muted('0');
|
|
483
|
+
} },
|
|
484
|
+
{ key: 'archived', header: 'STATE', width: 10, align: 'center', format: (r) => {
|
|
485
|
+
const a = r.archived;
|
|
486
|
+
return a ? C.red('archived') : C.green('active');
|
|
487
|
+
} },
|
|
488
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
489
|
+
],
|
|
490
|
+
channelMessage: () => [
|
|
491
|
+
{
|
|
492
|
+
key: 'message',
|
|
493
|
+
header: 'MESSAGE',
|
|
494
|
+
format: (r) => {
|
|
495
|
+
const m = r.message;
|
|
496
|
+
if (m == null)
|
|
497
|
+
return C.muted('(empty)');
|
|
498
|
+
if (typeof m === 'string')
|
|
499
|
+
return trim(m, 80);
|
|
500
|
+
if (typeof m === 'object' && Object.keys(m).length === 0)
|
|
501
|
+
return C.muted('(empty — use `huly channel get <id>`)');
|
|
502
|
+
const content = m.content;
|
|
503
|
+
return trim(typeof content === 'string' ? content : '', 80) || C.muted('(no content)');
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{ key: 'createdOn', header: 'CREATED', width: 12, format: (r) => relTime(r.createdOn) },
|
|
507
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
508
|
+
],
|
|
509
|
+
timeReport: () => [
|
|
510
|
+
{ key: 'hours', header: 'HOURS', width: 8, align: 'right', format: (r) => {
|
|
511
|
+
const v = Number(r.value);
|
|
512
|
+
return Number.isFinite(v) ? v.toFixed(2) : C.muted('—');
|
|
513
|
+
} },
|
|
514
|
+
{ key: 'minutes', header: 'MIN', width: 6, align: 'right', format: (r) => {
|
|
515
|
+
const v = Number(r.value);
|
|
516
|
+
return Number.isFinite(v) ? String(Math.round(v * 60)) : C.muted('—');
|
|
517
|
+
} },
|
|
518
|
+
{ key: 'description', header: 'DESCRIPTION', format: (r) => {
|
|
519
|
+
const d = String(r.description ?? '').trim();
|
|
520
|
+
return d || C.muted('(no description)');
|
|
521
|
+
} },
|
|
522
|
+
{ key: 'date', header: 'DATE', width: 12, format: (r) => {
|
|
523
|
+
const d = r.date;
|
|
524
|
+
return d != null ? isoDay(d) : C.muted('—');
|
|
525
|
+
} },
|
|
526
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
527
|
+
],
|
|
528
|
+
notification: () => [
|
|
529
|
+
{ key: 'type', header: 'TYPE', width: 16 },
|
|
530
|
+
{ key: 'title', header: 'TITLE', format: (r) => trim(r.title, 60) || C.muted('(untitled)') },
|
|
531
|
+
{ key: 'isRead', header: 'READ', width: 6, align: 'center', format: (r) => {
|
|
532
|
+
const r2 = r.isRead;
|
|
533
|
+
return r2 ? C.gray('●') : C.green('○');
|
|
534
|
+
} },
|
|
535
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
536
|
+
],
|
|
537
|
+
activity: () => [
|
|
538
|
+
{ key: 'message', header: 'MESSAGE', format: (r) => trim(r.message, 80) || C.muted('(no message)') },
|
|
539
|
+
{ key: 'createdOn', header: 'CREATED', width: 12, format: (r) => relTime(r.createdOn) },
|
|
540
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
541
|
+
],
|
|
542
|
+
approval: () => [
|
|
543
|
+
{ key: 'status', header: 'STATUS', format: (r) => colorizeStatus(String(r.status ?? '')) },
|
|
544
|
+
{ key: 'title', header: 'TITLE', format: (r) => trim(r.title, 60) || C.muted('(untitled)') },
|
|
545
|
+
{ key: 'createdOn', header: 'CREATED', width: 12, format: (r) => relTime(r.createdOn) },
|
|
546
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
547
|
+
],
|
|
548
|
+
component: () => [
|
|
549
|
+
{ key: 'label', header: 'LABEL', format: (r) => {
|
|
550
|
+
const l = r.label;
|
|
551
|
+
return l != null && l !== '' ? C.emphasis(String(l)) : C.muted('(no label)');
|
|
552
|
+
} },
|
|
553
|
+
{ key: 'description', header: 'DESCRIPTION', format: (r) => {
|
|
554
|
+
const d = String(r.description ?? '').trim();
|
|
555
|
+
return d || C.muted('—');
|
|
556
|
+
} },
|
|
557
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
558
|
+
],
|
|
559
|
+
milestone: () => [
|
|
560
|
+
{ key: 'label', header: 'LABEL', format: (r) => {
|
|
561
|
+
const l = r.label;
|
|
562
|
+
return l != null && l !== '' ? C.emphasis(String(l)) : C.muted('(no label)');
|
|
563
|
+
} },
|
|
564
|
+
{ key: 'targetDate', header: 'TARGET', width: 12, format: (r) => {
|
|
565
|
+
const t = r.targetDate;
|
|
566
|
+
if (t == null)
|
|
567
|
+
return C.muted('—');
|
|
568
|
+
const days = Math.ceil((Number(t) - Date.now()) / 86_400_000);
|
|
569
|
+
const label = isoDay(t);
|
|
570
|
+
return days >= 0 ? `${label} ${C.muted('(' + days + 'd)')}` : `${label} ${C.red('(overdue)')}`;
|
|
571
|
+
} },
|
|
572
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
573
|
+
],
|
|
574
|
+
label: () => [
|
|
575
|
+
{ key: 'title', header: 'LABEL', format: (r) => {
|
|
576
|
+
const t = r.title;
|
|
577
|
+
return t != null && t !== '' ? C.emphasis(String(t)) : C.muted('(no label)');
|
|
578
|
+
} },
|
|
579
|
+
{ key: 'color', header: 'COLOR', width: 8, format: (r) => {
|
|
580
|
+
const c2 = r.color;
|
|
581
|
+
return c2 != null && c2 !== '' ? `● ${c2}` : C.muted('—');
|
|
582
|
+
} },
|
|
583
|
+
{ key: '_id', header: '_ID', width: 12, align: 'right', format: (r) => C.id(shortId(r._id)) }
|
|
584
|
+
],
|
|
585
|
+
member: () => [
|
|
586
|
+
{ key: 'name', header: 'NAME', format: (r) => trim(r.name, 50) || C.muted('(unknown)') },
|
|
587
|
+
{ key: 'role', header: 'ROLE', width: 12, format: (r) => {
|
|
588
|
+
const role = r.role;
|
|
589
|
+
return role != null ? C.emphasis(String(role)) : C.muted('—');
|
|
590
|
+
} },
|
|
591
|
+
{ key: 'email', header: 'EMAIL', format: (r) => {
|
|
592
|
+
const e = r.email;
|
|
593
|
+
return e != null && e !== '' ? String(e) : C.muted('—');
|
|
594
|
+
} }
|
|
595
|
+
],
|
|
596
|
+
workspace: () => [
|
|
597
|
+
{ key: 'name', header: 'NAME', format: (r) => C.emphasis(String(r.name ?? '')) },
|
|
598
|
+
{ key: 'url', header: 'URL', format: (r) => String(r.url ?? '') },
|
|
599
|
+
{ key: 'uuid', header: 'UUID', width: 14, format: (r) => C.id(trim(r.uuid, 12) + '…') },
|
|
600
|
+
{ key: 'mode', header: 'MODE', width: 10, align: 'center', format: (r) => {
|
|
601
|
+
const m = String(r.mode ?? 'unknown');
|
|
602
|
+
return m === 'active' ? C.green('● active') : m === 'pending-deletion' ? C.red('● pending-deletion') : m === 'deleted' ? C.muted('● deleted') : m;
|
|
603
|
+
} },
|
|
604
|
+
{ key: 'lastVisit', header: 'LAST VISIT', width: 14, format: (r) => relTime(r.lastVisit) }
|
|
605
|
+
]
|
|
606
|
+
};
|
|
607
|
+
//# sourceMappingURL=format.js.map
|