flowcollab 0.1.4 → 0.1.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/login.mjs CHANGED
@@ -57,6 +57,10 @@ async function main() {
57
57
  );
58
58
  }
59
59
  if (poll.status === 202) continue;
60
+ if (!poll.ok) {
61
+ process.stdout.write('\n');
62
+ throw new Error(`Unexpected server response: ${poll.status}. Run flow-login again.`);
63
+ }
60
64
  if (poll.ok) {
61
65
  const { token, actor_id, org_id, api_base } = await poll.json();
62
66
  const dir = join(homedir(), '.flow');
package/bin/review.mjs CHANGED
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  /* flow:review — request a code review for a task's PR.
3
3
  Moves the task to in_review (if in_progress) and posts a standardised
4
- review-request comment that appears in the timeline and triggers @mention
5
- notifications. If --reviewer is given, also pings that actor's personal
6
- webhook directly (bypasses the @mention comment path).
4
+ review-request comment. @to:<reviewer> in the comment body triggers
5
+ server-side notification routing (personal webhook FLOW_NOTIFY_WEBHOOK).
7
6
 
8
7
  Usage:
9
8
  flow-review <task-id> [--pr=<number>] [--reviewer=<actor-id>] [--context="notes"]
@@ -26,10 +25,7 @@ async function main() {
26
25
  const taskId = await resolveTaskId(rawId);
27
26
 
28
27
  // Fetch tasks + people in parallel.
29
- const [{ tasks = [] }, { people = {} }] = await Promise.all([
30
- flowFetch('/api/flow/tasks?limit=500'),
31
- flowFetch('/api/flow/people'),
32
- ]);
28
+ const { tasks = [] } = await flowFetch('/api/flow/tasks?limit=500');
33
29
 
34
30
  const task = tasks.find(t => t.id === taskId);
35
31
  if (!task) die(`Task ${taskId} not found.`);
@@ -54,23 +50,6 @@ async function main() {
54
50
 
55
51
  await flowFetch('/api/flow/comment', { method: 'POST', body: { task_id: taskId, body } });
56
52
 
57
- // If reviewer has a personal webhook, ping it directly too (fire-and-forget).
58
- if (reviewerArg) {
59
- const peopleArr = Array.isArray(people) ? people : Object.values(people);
60
- const reviewer = peopleArr.find(p => p.id === reviewerArg);
61
- const hook = reviewer?.notify_webhook || process.env.FLOW_NOTIFY_WEBHOOK;
62
- if (hook) {
63
- const ref = `#${task.issue_num ?? taskId.slice(0, 6)}`;
64
- fetch(hook, {
65
- method: 'POST',
66
- headers: { 'content-type': 'application/json' },
67
- body: JSON.stringify({
68
- text: `Review requested on ${ref} — PR #${prNum}${prUrl ? ' ' + prUrl : ''}${context ? '\n' + context : ''}`,
69
- }),
70
- }).catch(() => {});
71
- }
72
- }
73
-
74
53
  // Move to in_review if currently in_progress.
75
54
  if (task.status === 'in_progress') {
76
55
  await flowFetch(`/api/flow/tasks/${taskId}`, { method: 'PATCH', body: { status: 'in_review' } });
@@ -78,8 +57,8 @@ async function main() {
78
57
 
79
58
  const ref = `#${task.issue_num ?? taskId.slice(0, 6)}`;
80
59
  const moved = task.status === 'in_progress' ? ' (moved → in_review)' : '';
81
- const pinged = reviewerArg ? ` (pinged ${reviewerArg})` : '';
82
- process.stdout.write(`Review requested on ${ref} — PR #${prNum}${prUrl ? ' ' + prUrl : ''}${moved}${pinged}\n`);
60
+ const mentioned = reviewerArg ? ` (@to:${reviewerArg} notified)` : '';
61
+ process.stdout.write(`Review requested on ${ref} — PR #${prNum}${prUrl ? ' ' + prUrl : ''}${moved}${mentioned}\n`);
83
62
  }
84
63
 
85
64
  main().catch(e => die(e.message || e));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowcollab",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Multi-Claude coordination layer — shared task board + CLI for teams running Claude Code",
5
5
  "type": "module",
6
6
  "files": [
@@ -26,7 +26,6 @@
26
26
  "flow-handoff": "bin/handoff.mjs",
27
27
  "flow-standup": "bin/standup.mjs",
28
28
  "flow-search": "bin/search.mjs",
29
- "flow-init": "bin/init.mjs",
30
29
  "flow-project": "bin/project.mjs",
31
30
  "flow-close-sprint": "bin/close-sprint.mjs",
32
31
  "flow-review": "bin/review.mjs",
package/bin/init.mjs DELETED
@@ -1,182 +0,0 @@
1
- #!/usr/bin/env node
2
- /* flow:init — set up CLI access for a web-provisioned Flow account.
3
-
4
- You signed up at https://flow-production-84b7.up.railway.app and have a
5
- token from the Flow setup page. This writes your .env and CLAUDE.md.
6
-
7
- Usage: flow-init --web
8
- flow-init --web
9
- */
10
-
11
- import { readFileSync, writeFileSync, existsSync } from 'node:fs';
12
- import path from 'node:path';
13
- import { createInterface } from 'node:readline';
14
-
15
- const rl = createInterface({ input: process.stdin, output: process.stdout });
16
- const ask = (q) => new Promise(r => rl.question(q, r));
17
-
18
- function buildWebClaudeMd({ actorId, ownerActorId, boardUrl, scope, areas = [] }) {
19
- const ownerRef = scope === 'owner' ? actorId : (ownerActorId || 'owner');
20
- const areaLine = areas.length ? areas.join(', ') : 'backend, frontend, infra, docs';
21
- return `# Flow — Claude Code workflow
22
-
23
- This project uses Flow for multi-agent coordination.
24
- Follow this workflow every session without exception.
25
-
26
- ## Your identity
27
- - **Your actor ID:** \`${actorId}\`
28
- - **Your scope:** ${scope}
29
- - **Board:** ${boardUrl}
30
-
31
- ## Required session workflow
32
-
33
- \`\`\`bash
34
- flow-pull # ALWAYS start here — board snapshot + your mentions
35
- flow-claim <task_id> # claim before writing any code
36
- flow-comment <task_id> "..." # update at milestones, use @to:${ownerRef} to ping
37
- flow-close <task_id> "summary" # close with a meaningful summary when done
38
- flow-propose --parent=<id> --title="..." --md="..." # propose new work; never start without approval
39
- \`\`\`
40
-
41
- ## Rules — never violate
42
- - \`flow-pull\` at session start — no exceptions
43
- - Claim a task before touching related code
44
- - Comment at major milestones, not every commit
45
- - Close with a summary the next agent can act on
46
- - Never approve your own proposals — only **owners** can approve (in the browser at ${boardUrl})
47
- - Use \`@to:${ownerRef}\` in comments to request human action or review
48
-
49
- ## All CLI commands
50
- \`\`\`
51
- flow-pull — board snapshot + mentions + handoffs
52
- flow-pull --focus — top-3 open tasks sorted by priority
53
- flow-pull --milestone=<name> — filter snapshot to a sprint
54
- flow-claim <id> — self-assign a task
55
- flow-comment <id> "..." — add a comment or milestone update
56
- flow-close <id> "summary" — mark task done with summary
57
- flow-create --title="..." — create a task directly (no approval gate)
58
- flow-create --from-issue=<num> — import a GitHub Issue as a task
59
- flow-create --from-project-item=<id> — import a GitHub Projects item
60
- flow-create --template=<id> — create from template (standup, pr-review, retro, ...)
61
- flow-propose --parent=<id> --title="..." --md="..." — propose work for human approval
62
- flow-approve/reject <id> — resolve a proposal (owner only)
63
- flow-assign <id> <actor> — reassign a task (owner only)
64
- flow-decisions — list pending proposals
65
- flow-search "query" — search tasks by title
66
- flow-status — board summary (counts, active agents)
67
- flow-standup — recent activity digest
68
- flow-standup --velocity — recent activity + week-over-week velocity chart
69
- flow-handoff --task=<id> --to=<actor> — hand off a task with context
70
- flow-heartbeat — send presence ping (run in background)
71
- flow-sync — delta sync since N minutes ago
72
- flow-ping — health check
73
- flow-whoami — verify token and actor identity
74
- flow-project — list GitHub Projects v2 items
75
- flow-close-sprint --milestone=<name> — close sprint (owner only)
76
- flow-review <id> [--pr=<num>] [--reviewer=<actor>] — request code review; moves → in_review, pings reviewer
77
- flow-edit <id> [--title=] [--priority=] [--area=] [--due=] [--milestone=] [--blocked-by=]
78
- flow-unblock <id> — clear blocked_by
79
- flow-log [--task=<id>] [--limit=20] — tail recent timeline events
80
- \`\`\`
81
-
82
- ## Area labels
83
- ${areaLine}
84
- `;
85
- }
86
-
87
- async function main() {
88
- if (!process.argv.includes('--web')) {
89
- console.error(
90
- 'Usage: flow-init --web\n\n' +
91
- 'Flow is a hosted SaaS. Sign up at:\n' +
92
- ' https://flow-production-84b7.up.railway.app\n\n' +
93
- 'Once you have a token from the setup page, run:\n' +
94
- ' flow-init --web\n'
95
- );
96
- process.exit(1);
97
- }
98
-
99
- console.log('\nFlow — CLI setup\n');
100
- console.log('You signed up at https://flow-production-84b7.up.railway.app');
101
- console.log('This writes your .env and CLAUDE.md — no Supabase config needed.\n');
102
-
103
- const token = (await ask('Your API token (from the board setup page): ')).trim();
104
- const actorId = (await ask('Your actor ID (from the board, e.g. "alice"): ')).trim() || 'owner';
105
- const scopeRaw = (await ask('Your scope [owner/contributor, default: owner]: ')).trim().toLowerCase();
106
- const scope = scopeRaw === 'contributor' ? 'contributor' : 'owner';
107
-
108
- let ownerActorId = actorId;
109
- if (scope === 'contributor') {
110
- ownerActorId = (await ask("Owner's actor ID (for CLAUDE.md @mentions, e.g. \"alice\"): ")).trim() || 'owner';
111
- }
112
-
113
- const cwd = process.cwd();
114
- const localAreas = (() => {
115
- try {
116
- const raw = readFileSync(path.join(cwd, 'flow.config.json'), 'utf8');
117
- return Object.keys(JSON.parse(raw).labels?.area || {});
118
- } catch { return []; }
119
- })();
120
-
121
- const envTarget = path.join(cwd, '.env');
122
- const claudeMdTarget = path.join(cwd, 'CLAUDE.md');
123
- const envExists = existsSync(envTarget);
124
- const claudeExists = existsSync(claudeMdTarget);
125
-
126
- const writeEnvAns = (await ask(
127
- `\nWrite .env to ${cwd}? ${envExists ? '[y/N]' : '[Y/n]'}: `
128
- )).trim().toLowerCase();
129
- const writeEnv = envExists
130
- ? (writeEnvAns === 'y' || writeEnvAns === 'yes')
131
- : (writeEnvAns !== 'n' && writeEnvAns !== 'no');
132
-
133
- const writeClaudeAns = (await ask(
134
- `Write CLAUDE.md to ${cwd}? ${claudeExists ? '[y/N]' : '[Y/n]'}: `
135
- )).trim().toLowerCase();
136
- const writeClaude = claudeExists
137
- ? (writeClaudeAns === 'y' || writeClaudeAns === 'yes')
138
- : (writeClaudeAns !== 'n' && writeClaudeAns !== 'no');
139
-
140
- rl.close();
141
-
142
- const tokenVar = scope === 'owner' ? 'FLOW_API_TOKEN_OWNER' : 'FLOW_API_TOKEN_CONTRIBUTOR';
143
- const envLines = [
144
- `${tokenVar}=${token}`,
145
- `FLOW_DEFAULT_ASSIGNEE=${actorId}`,
146
- `FLOW_ACTING_VIA=claude`,
147
- ];
148
-
149
- console.log('\n========== .env ==========\n');
150
- envLines.forEach(l => console.log(l));
151
-
152
- if (writeEnv) {
153
- if (envExists) {
154
- const existing = readFileSync(envTarget, 'utf8');
155
- const conflicts = envLines.map(l => l.split('=')[0]).filter(k => new RegExp(`^${k}=`, 'm').test(existing));
156
- if (conflicts.length) {
157
- console.log(`\n⚠ These keys already exist in .env — not overwriting: ${conflicts.join(', ')}`);
158
- console.log(' Paste the block above into .env manually.');
159
- } else {
160
- writeFileSync(envTarget, existing.trimEnd() + '\n\n' + envLines.join('\n') + '\n', 'utf8');
161
- console.log(`\n✓ .env updated. Add .env to .gitignore — it contains your API token.`);
162
- }
163
- } else {
164
- writeFileSync(envTarget, envLines.join('\n') + '\n', 'utf8');
165
- console.log(`\n✓ .env written. Add .env to .gitignore — it contains your API token.`);
166
- }
167
- }
168
-
169
- if (writeClaude) {
170
- const content = buildWebClaudeMd({ actorId, ownerActorId, boardUrl: 'https://flow-production-84b7.up.railway.app/flow/', scope, areas: localAreas });
171
- writeFileSync(claudeMdTarget, content, 'utf8');
172
- console.log(`✓ CLAUDE.md written. Commit this so every agent on the team gets the workflow.`);
173
- }
174
-
175
- console.log('\n========== Next steps ==========\n');
176
- console.log(`1. Verify your connection: flow-whoami`);
177
- console.log(`2. See your board: flow-pull`);
178
- console.log(`3. Open the board: https://flow-production-84b7.up.railway.app/flow/`);
179
- console.log('');
180
- }
181
-
182
- main().catch(e => { console.error('init failed:', e.message || e); process.exit(1); });