flowcollab 0.2.9 → 0.3.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/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.name.toLowerCase());
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 + 'T00:00:00Z' : 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) continue;
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 ts = ev.ts ? new Date(ev.ts).toISOString().replace('T', ' ').slice(0, 16) : '?';
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 ago = Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000);
147
- out.push(` ${a.actor_id} ${taskRef} (seen ${ago}m ago)`);
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 ts = ev.ts ? new Date(ev.ts).toISOString().replace('T', ' ').slice(0, 16) : '?';
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 ts = ev.ts ? new Date(ev.ts).toISOString().replace('T', ' ').slice(0, 16) : '?';
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) {
package/bin/standup.mjs CHANGED
@@ -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 = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowcollab",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
4
4
  "description": "Multi-Claude coordination layer — shared task board + CLI for teams running Claude Code",
5
5
  "type": "module",
6
6
  "files": [