flowcollab 0.2.9 → 0.3.1
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/bin/create.mjs +1 -1
- package/bin/edit.mjs +1 -1
- package/bin/log.mjs +1 -1
- package/bin/login.mjs +6 -1
- package/bin/pull.mjs +10 -6
- package/bin/scan.mjs +6 -4
- package/bin/standup.mjs +2 -2
- package/bin/sync.mjs +1 -1
- package/package.json +1 -1
package/bin/create.mjs
CHANGED
|
@@ -53,7 +53,7 @@ async function main() {
|
|
|
53
53
|
if (!num || num < 1) die('--from-issue must be a positive integer (e.g. --from-issue=42)');
|
|
54
54
|
process.stdout.write(`Fetching GitHub Issue #${num}...\n`);
|
|
55
55
|
const issue = await fetchGithubIssue(num);
|
|
56
|
-
const labels = (issue.labels || []).map(l => l
|
|
56
|
+
const labels = (issue.labels || []).map(l => l?.name?.toLowerCase()).filter(Boolean);
|
|
57
57
|
const type = labels.includes('bug') ? 'bug'
|
|
58
58
|
: labels.includes('documentation') ? 'docs'
|
|
59
59
|
: 'feature';
|
package/bin/edit.mjs
CHANGED
|
@@ -14,7 +14,7 @@ const FIELD_MAP = {
|
|
|
14
14
|
title: v => ({ title: v }),
|
|
15
15
|
priority: v => ({ priority: v }),
|
|
16
16
|
area: v => ({ area: v }),
|
|
17
|
-
due: v => ({ due_at: /^\d{4}-\d{2}-\d{2}$/.test(v) ? v + '
|
|
17
|
+
due: v => ({ due_at: /^\d{4}-\d{2}-\d{2}$/.test(v) ? v + 'T12:00:00Z' : v }),
|
|
18
18
|
milestone: v => ({ milestone: v }),
|
|
19
19
|
'blocked-by': v => ({ blocked_by: v === 'null' ? [] : [v] }),
|
|
20
20
|
};
|
package/bin/log.mjs
CHANGED
|
@@ -36,7 +36,7 @@ function fmtEvent(e) {
|
|
|
36
36
|
|
|
37
37
|
async function main() {
|
|
38
38
|
const taskRaw = arg('task');
|
|
39
|
-
const limit = Math.min(200, Math.max(1, parseInt(arg('limit') || '20')));
|
|
39
|
+
const limit = Math.min(200, Math.max(1, parseInt(arg('limit') || '20') || 20));
|
|
40
40
|
|
|
41
41
|
const res = await flowFetch('/api/flow/tasks?limit=500');
|
|
42
42
|
const tasks = res.tasks ?? [];
|
package/bin/login.mjs
CHANGED
|
@@ -40,13 +40,18 @@ async function main() {
|
|
|
40
40
|
// 3. Poll every 2s until authorized or expired
|
|
41
41
|
const deadline = Date.now() + expires_in * 1000;
|
|
42
42
|
process.stdout.write('Waiting for authorization');
|
|
43
|
+
let netErrCount = 0;
|
|
43
44
|
while (Date.now() < deadline) {
|
|
44
45
|
await new Promise(ok => setTimeout(ok, 2000));
|
|
45
46
|
process.stdout.write('.');
|
|
46
47
|
const poll = await fetch(
|
|
47
48
|
`${base}/api/flow/auth/poll?device_token=${encodeURIComponent(device_token)}`,
|
|
48
49
|
).catch(() => null);
|
|
49
|
-
if (!poll)
|
|
50
|
+
if (!poll) {
|
|
51
|
+
if (++netErrCount >= 3) { process.stdout.write('\n'); throw new Error(`Cannot reach ${base} — check your connection.`); }
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
netErrCount = 0;
|
|
50
55
|
if (poll.status === 410) {
|
|
51
56
|
process.stdout.write('\n');
|
|
52
57
|
const body = await poll.json().catch(() => ({}));
|
package/bin/pull.mjs
CHANGED
|
@@ -76,7 +76,8 @@ function extractBody(ev) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function fmtTimeline(ev, taskTitle) {
|
|
79
|
-
const
|
|
79
|
+
const _d = ev.ts ? new Date(ev.ts) : null;
|
|
80
|
+
const ts = _d && !isNaN(_d) ? _d.toISOString().replace('T', ' ').slice(0, 16) : '?';
|
|
80
81
|
const who = ev.slot_label || ev.actor_id || '?';
|
|
81
82
|
const actingAs = ev.acting_as_id && ev.acting_as_id !== ev.actor_id ? `(as ${ev.acting_as_id})` : '';
|
|
82
83
|
const body = extractBody(ev);
|
|
@@ -143,8 +144,9 @@ async function main() {
|
|
|
143
144
|
const taskRef = task
|
|
144
145
|
? `working on #${task.issue_num ?? a.task_id.slice(0, 6)} "${sanitizeText(task.title, 60)}"`
|
|
145
146
|
: 'idle';
|
|
146
|
-
const
|
|
147
|
-
|
|
147
|
+
const seenMs = a.last_seen_at ? Date.now() - new Date(a.last_seen_at).getTime() : NaN;
|
|
148
|
+
const agoStr = !isNaN(seenMs) ? `${Math.round(seenMs / 60000)}m ago` : 'recently';
|
|
149
|
+
out.push(` ${a.actor_id} ${taskRef} (seen ${agoStr})`);
|
|
148
150
|
}
|
|
149
151
|
out.push('');
|
|
150
152
|
}
|
|
@@ -162,7 +164,8 @@ async function main() {
|
|
|
162
164
|
const t = taskById.get(ev.task_id);
|
|
163
165
|
const title = t ? t.title : '(unknown task)';
|
|
164
166
|
const taskRef = t ? `#${t.issue_num ?? t.id.slice(0, 6)}` : `#${ev.task_id.slice(0, 6)}`;
|
|
165
|
-
const
|
|
167
|
+
const _md = ev.ts ? new Date(ev.ts) : null;
|
|
168
|
+
const ts = _md && !isNaN(_md) ? _md.toISOString().replace('T', ' ').slice(0, 16) : '?';
|
|
166
169
|
out.push(` ${taskRef} ${title} (${ts}, from ${ev.actor_id || '?'})`);
|
|
167
170
|
out.push(` └─ <comment_from_untrusted_user>${extractBody(ev)}</comment_from_untrusted_user>`);
|
|
168
171
|
});
|
|
@@ -218,7 +221,8 @@ async function main() {
|
|
|
218
221
|
const t = taskById.get(ev.task_id);
|
|
219
222
|
const ref = t ? `#${t.issue_num ?? t.id.slice(0, 6)}` : `#${ev.task_id.slice(0, 6)}`;
|
|
220
223
|
const title = t ? sanitizeText(t.title, 60) : '(unknown task)';
|
|
221
|
-
const
|
|
224
|
+
const _hd = ev.ts ? new Date(ev.ts) : null;
|
|
225
|
+
const ts = _hd && !isNaN(_hd) ? _hd.toISOString().replace('T', ' ').slice(0, 16) : '?';
|
|
222
226
|
out.push(` ${ref} ${title} (from ${ev.actor_id || '?'}, ${ts})`);
|
|
223
227
|
const hd = t?.handoff_data;
|
|
224
228
|
if (hd?.context || ev.payload?.context) {
|
|
@@ -410,7 +414,7 @@ async function main() {
|
|
|
410
414
|
let gc = {};
|
|
411
415
|
try { gc = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
|
|
412
416
|
gc.projectId = defaultProject.id;
|
|
413
|
-
writeFileSync(configPath, JSON.stringify(gc, null, 2));
|
|
417
|
+
writeFileSync(configPath, JSON.stringify(gc, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
414
418
|
process.stdout.write(` project_id → ~/.flow/config.json (project: ${defaultProject.slug})\n`);
|
|
415
419
|
}
|
|
416
420
|
} catch { /* non-fatal */ }
|
package/bin/scan.mjs
CHANGED
|
@@ -204,20 +204,22 @@ async function main() {
|
|
|
204
204
|
|
|
205
205
|
if (all || doIssues) {
|
|
206
206
|
process.stdout.write(' [issues] fetching GitHub Issues vs Flow tasks...\n');
|
|
207
|
-
let s;
|
|
208
|
-
try { s = await scanIssues(repo); } catch (e) { process.stdout.write(` [issues] Error: ${e.message}\n`); s = []; }
|
|
207
|
+
let s, issueErr = false;
|
|
208
|
+
try { s = await scanIssues(repo); } catch (e) { process.stdout.write(` [issues] Error: ${e.message}\n`); issueErr = true; s = []; }
|
|
209
209
|
if (doIssues) { printSection('Untracked GitHub Issues', s || []); return; }
|
|
210
210
|
if (s === null) process.stdout.write('\n[issues] Skipped — FLOW_GITHUB_REPO not set.\n');
|
|
211
|
+
else if (issueErr) process.stdout.write('\n[issues] Scan failed — check FLOW_GITHUB_REPO and token.\n');
|
|
211
212
|
else if (all && s.length) printSection('Untracked GitHub Issues', s);
|
|
212
213
|
else if (all) process.stdout.write('\n[issues] All open Issues are tracked in Flow.\n');
|
|
213
214
|
}
|
|
214
215
|
|
|
215
216
|
if (all || doPRs) {
|
|
216
217
|
process.stdout.write(' [prs] fetching open PRs vs Flow tasks...\n');
|
|
217
|
-
let s;
|
|
218
|
-
try { s = await scanPRs(repo); } catch (e) { process.stdout.write(` [prs] Error: ${e.message}\n`); s = []; }
|
|
218
|
+
let s, prErr = false;
|
|
219
|
+
try { s = await scanPRs(repo); } catch (e) { process.stdout.write(` [prs] Error: ${e.message}\n`); prErr = true; s = []; }
|
|
219
220
|
if (doPRs) { printSection('Unlinked open PRs', s || []); return; }
|
|
220
221
|
if (s === null) process.stdout.write('\n[prs] Skipped — FLOW_GITHUB_REPO not set.\n');
|
|
222
|
+
else if (prErr) process.stdout.write('\n[prs] Scan failed — check FLOW_GITHUB_REPO and token.\n');
|
|
221
223
|
else if (all && s.length) printSection('Unlinked open PRs', s);
|
|
222
224
|
else if (all) process.stdout.write('\n[prs] All open PRs are linked to Flow tasks.\n');
|
|
223
225
|
}
|
package/bin/standup.mjs
CHANGED
|
@@ -30,7 +30,7 @@ const TITLE_W = Math.max(20, TERM_COLS - 29);
|
|
|
30
30
|
const SHORT_TITLE_W = Math.max(20, TERM_COLS - 37); // agents have longer prefix
|
|
31
31
|
|
|
32
32
|
async function main() {
|
|
33
|
-
const sinceHours = Math.max(1, parseInt(arg('since') || '24'));
|
|
33
|
+
const sinceHours = Math.max(1, parseInt(arg('since') || '24') || 24);
|
|
34
34
|
const since = new Date(Date.now() - sinceHours * 3600000);
|
|
35
35
|
const showVelocity = process.argv.includes('--velocity');
|
|
36
36
|
|
|
@@ -52,7 +52,7 @@ async function main() {
|
|
|
52
52
|
// Done since N hours — de-dup by task (most recent close per task)
|
|
53
53
|
const doneEvents = timeline
|
|
54
54
|
.filter(ev => ev.kind === 'event' && ev.payload?.act === 'closed' && new Date(ev.ts) >= since)
|
|
55
|
-
.sort((a, b) => b.ts.localeCompare(a.ts));
|
|
55
|
+
.sort((a, b) => (b.ts || '').localeCompare(a.ts || ''));
|
|
56
56
|
const seenDone = new Set();
|
|
57
57
|
const doneItems = [];
|
|
58
58
|
for (const ev of doneEvents) {
|
package/bin/sync.mjs
CHANGED
|
@@ -34,7 +34,7 @@ function relAgo(ts) {
|
|
|
34
34
|
function pad(s, n) { return String(s).padEnd(n).slice(0, n); }
|
|
35
35
|
|
|
36
36
|
async function main() {
|
|
37
|
-
const sinceMinutes = Math.max(1, parseInt(arg('since') || '60'));
|
|
37
|
+
const sinceMinutes = Math.max(1, parseInt(arg('since') || '60') || 60);
|
|
38
38
|
const r = await flowFetch(`/api/flow/sync?since_minutes=${sinceMinutes}`);
|
|
39
39
|
|
|
40
40
|
const out = [];
|