flowcollab 0.2.4 → 0.2.6

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/_client.mjs CHANGED
@@ -76,6 +76,10 @@ export async function flowFetch(path, { method = 'GET', body } = {}) {
76
76
  'content-type': 'application/json',
77
77
  };
78
78
  if (actingViaClaude()) headers['x-flow-acting-via'] = 'claude';
79
+ const org = process.env.FLOW_ORG_ID || _gc.orgId || '';
80
+ if (org) headers['x-flow-org'] = org;
81
+ const proj = process.env.FLOW_PROJECT_ID || _gc.projectId || '';
82
+ if (proj) headers['x-flow-project'] = proj;
79
83
 
80
84
  const res = await fetch(url, {
81
85
  method,
@@ -96,6 +100,7 @@ export async function flowFetch(path, { method = 'GET', body } = {}) {
96
100
 
97
101
  export const RESOLVED_BASE = DEFAULT_BASE;
98
102
  export const RESOLVED_ACTOR = process.env.FLOW_DEFAULT_ASSIGNEE || _gc.actorId || '';
103
+ export const RESOLVED_PROJECT = process.env.FLOW_PROJECT_ID || _gc.projectId || '';
99
104
  export function resolvedTokenDisplay() {
100
105
  const raw = envToken();
101
106
  if (!raw) return '(none — run `flow-login`)';
package/bin/close.mjs CHANGED
@@ -16,7 +16,8 @@ async function main() {
16
16
  body: { task_id: id, ...(summary ? { summary } : {}) },
17
17
  });
18
18
 
19
- process.stdout.write(`Closed #${r.task.id.slice(0, 6)} — "${r.task.title}"\n`);
19
+ const ref = r.task.issue_num != null ? r.task.issue_num : r.task.id.slice(0, 6);
20
+ process.stdout.write(`Closed #${ref} — "${r.task.title}"\n`);
20
21
  }
21
22
 
22
23
  main().catch(e => die(e.message || e));
package/bin/pull.mjs CHANGED
@@ -12,8 +12,9 @@
12
12
  3. Recent timeline activity on YOUR tasks (last 5 per task)
13
13
  */
14
14
 
15
- import { flowFetch, arg, RESOLVED_ACTOR } from './_client.mjs';
16
- import { existsSync, writeFileSync } from 'fs';
15
+ import { flowFetch, arg, RESOLVED_ACTOR, RESOLVED_PROJECT } from './_client.mjs';
16
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
17
+ import { homedir } from 'os';
17
18
  import { join } from 'path';
18
19
 
19
20
  // Per-status age threshold (days) before pull flags a task as aging.
@@ -92,16 +93,29 @@ function mentionRegex(assignee) {
92
93
  return new RegExp(`@to:(?:${a}-claude|${a}|claude)(?=[\\s.,;:!?)\\]]|$)`, 'i');
93
94
  }
94
95
 
96
+ async function getActiveProjectName() {
97
+ try {
98
+ const { projects } = await flowFetch('/api/flow/projects');
99
+ if (!projects?.length) return null;
100
+ if (RESOLVED_PROJECT) {
101
+ const match = projects.find(p => p.id === RESOLVED_PROJECT);
102
+ if (match) return match.name;
103
+ }
104
+ return projects[0]?.name || null;
105
+ } catch { return null; }
106
+ }
107
+
95
108
  async function main() {
96
109
  const explicit = arg('assignee');
97
110
  const filterAssignee = explicit === 'all' ? '' : (explicit || RESOLVED_ACTOR);
98
111
  const focusMode = arg('focus') !== undefined;
99
112
  const filterMilestone = arg('milestone') || '';
100
113
 
101
- const [tasksRes, decRes, presenceRes] = await Promise.all([
114
+ const [tasksRes, decRes, presenceRes, projectName] = await Promise.all([
102
115
  flowFetch('/api/flow/tasks?limit=200'),
103
116
  flowFetch('/api/flow/decisions'),
104
117
  flowFetch('/api/flow/presence').catch(() => null),
118
+ getActiveProjectName(),
105
119
  ]);
106
120
 
107
121
  const allTasks = tasksRes.tasks || [];
@@ -300,7 +314,8 @@ async function main() {
300
314
  focusMode ? 'focus' : null,
301
315
  ].filter(Boolean).join(', ') || 'unfiltered';
302
316
 
303
- out.push(`=== Flow board snapshot (${snapshotLabel}) ===`);
317
+ const headerTitle = projectName ? `Flow Board ${projectName}` : 'Flow Board';
318
+ out.push(`=== ${headerTitle} snapshot (${snapshotLabel}) ===`);
304
319
 
305
320
  if (focusMode) {
306
321
  const open = tasks.filter(t => t.status !== 'done' && t.status !== 'awaiting_direction');
@@ -385,6 +400,20 @@ async function main() {
385
400
  } catch (e) {
386
401
  process.stderr.write(`flow-pull: --sync-md failed: ${e.message}\n`);
387
402
  }
403
+
404
+ // Write project_id to global config
405
+ try {
406
+ const { projects } = await flowFetch('/api/flow/projects');
407
+ const defaultProject = projects?.find(p => p.slug === 'main') || projects?.[0];
408
+ if (defaultProject?.id) {
409
+ const configPath = join(homedir(), '.flow', 'config.json');
410
+ let gc = {};
411
+ try { gc = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
412
+ gc.projectId = defaultProject.id;
413
+ writeFileSync(configPath, JSON.stringify(gc, null, 2));
414
+ process.stdout.write(` project_id → ~/.flow/config.json (project: ${defaultProject.slug})\n`);
415
+ }
416
+ } catch { /* non-fatal */ }
388
417
  }
389
418
  }
390
419
 
package/bin/scan.mjs CHANGED
@@ -176,10 +176,11 @@ function printSection(heading, suggestions) {
176
176
 
177
177
  // ── Main ───────────────────────────────────────────────────────────────────────
178
178
  async function main() {
179
- const doTodos = !!arg('todos');
180
- const doIssues = !!arg('issues');
181
- const doPRs = !!arg('prs');
182
- const doSecurity = !!arg('security');
179
+ const argv = process.argv;
180
+ const doTodos = argv.includes('--todos');
181
+ const doIssues = argv.includes('--issues');
182
+ const doPRs = argv.includes('--prs');
183
+ const doSecurity = argv.includes('--security');
183
184
  const all = !doTodos && !doIssues && !doPRs && !doSecurity;
184
185
 
185
186
  const dir = arg('dir') || process.cwd();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowcollab",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Multi-Claude coordination layer — shared task board + CLI for teams running Claude Code",
5
5
  "type": "module",
6
6
  "files": [