flowcollab 0.1.9 → 0.2.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.
@@ -9,10 +9,10 @@
9
9
  import { positional, die } from './_client.mjs';
10
10
 
11
11
  const COMMANDS = [
12
- 'pull', 'claim', 'comment', 'close', 'create', 'propose', 'approve', 'reject',
13
- 'assign', 'decisions', 'ping', 'whoami', 'heartbeat', 'sync', 'status', 'handoff',
14
- 'standup', 'search', 'init', 'project', 'close-sprint', 'review', 'login',
15
- 'edit', 'unblock', 'log', 'completion',
12
+ 'login', 'pull', 'claim', 'comment', 'close', 'create', 'edit', 'unblock',
13
+ 'propose', 'approve', 'reject', 'assign', 'decisions', 'handoff', 'review',
14
+ 'search', 'status', 'standup', 'sync', 'log', 'heartbeat', 'ping', 'pr',
15
+ 'project', 'close-sprint', 'whoami', 'completion',
16
16
  ];
17
17
 
18
18
  const FLAGS = {
@@ -30,25 +30,13 @@ const FLAGS = {
30
30
  };
31
31
 
32
32
  function bashScript() {
33
- const cmds = COMMANDS.map(c => `flow-${c}`).join(' ');
33
+ const hyphenated = COMMANDS.map(c => `flow-${c}`).join(' ');
34
+ const subcmds = COMMANDS.join(' ');
34
35
  return `# Flow CLI bash completion
35
36
  # Source this file or add to ~/.bash_completion.d/flow
36
37
 
37
- _flow_complete() {
38
- local cur prev words
39
- COMPREPLY=()
40
- cur="\${COMP_WORDS[COMP_CWORD]}"
41
- prev="\${COMP_WORDS[COMP_CWORD-1]}"
42
-
43
- local commands="${cmds}"
44
-
45
- if [[ \${COMP_CWORD} -eq 1 ]]; then
46
- COMPREPLY=( \$(compgen -W "\$commands" "\$cur") )
47
- return
48
- fi
49
-
50
- local cmd="\${COMP_WORDS[0]}"
51
- local subcmd="\${cmd#flow-}"
38
+ _flow_subcmd_flags() {
39
+ local subcmd="\$1" cur="\$2"
52
40
  case "\$subcmd" in
53
41
  ${Object.entries(FLAGS).map(([cmd, flags]) => ` ${cmd})
54
42
  COMPREPLY=( \$(compgen -W "${flags.join(' ')}" "\$cur") )
@@ -56,35 +44,85 @@ ${Object.entries(FLAGS).map(([cmd, flags]) => ` ${cmd})
56
44
  esac
57
45
  }
58
46
 
47
+ _flow_complete() {
48
+ local cur prev
49
+ COMPREPLY=()
50
+ cur="\${COMP_WORDS[COMP_CWORD]}"
51
+ local cmd="\${COMP_WORDS[0]}"
52
+
53
+ if [[ "\$cmd" == "flow" ]]; then
54
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
55
+ COMPREPLY=( \$(compgen -W "${subcmds}" "\$cur") )
56
+ return
57
+ fi
58
+ _flow_subcmd_flags "\${COMP_WORDS[1]}" "\$cur"
59
+ else
60
+ local subcmd="\${cmd#flow-}"
61
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
62
+ COMPREPLY=( \$(compgen -W "${hyphenated}" "\$cur") )
63
+ return
64
+ fi
65
+ _flow_subcmd_flags "\$subcmd" "\$cur"
66
+ fi
67
+ }
68
+
69
+ complete -F _flow_complete flow
59
70
  ${COMMANDS.map(c => `complete -F _flow_complete flow-${c}`).join('\n')}
60
71
  `;
61
72
  }
62
73
 
63
74
  function zshScript() {
64
75
  const cmds = COMMANDS.map(c => `flow-${c}`);
65
- return `#compdef ${cmds.join(' ')}
76
+ const subcmdList = COMMANDS.map(c => `'${c}'`).join(' ');
77
+ return `#compdef flow ${cmds.join(' ')}
66
78
  # Flow CLI zsh completion
67
79
  # Place in ~/.zfunc/_flow and add fpath=(~/.zfunc $fpath) to ~/.zshrc
68
80
 
69
- _flow_args() {
70
- local cmd="\${words[1]#flow-}"
71
- case "\$cmd" in
81
+ _flow_flags() {
82
+ local subcmd="\$1"
83
+ case "\$subcmd" in
72
84
  ${Object.entries(FLAGS).map(([cmd, flags]) => ` ${cmd})
73
85
  local opts=(${flags.map(f => `'${f}'`).join(' ')})
74
86
  _arguments "\${opts[@]}"
75
- ;;`).join('\n')}
76
- *)
77
- _arguments '*:arg:'
78
- ;;
87
+ return ;;`).join('\n')}
79
88
  esac
89
+ _arguments '*:arg:'
90
+ }
91
+
92
+ _flow() {
93
+ local state
94
+ if [[ "\${words[1]}" == "flow" ]]; then
95
+ _arguments '1:subcommand:(${subcmdList})' '*:: :->args'
96
+ case "\$state" in args) _flow_flags "\${words[1]}" ;; esac
97
+ else
98
+ _flow_flags "\${words[1]#flow-}"
99
+ fi
80
100
  }
81
101
 
82
- ${cmds.map(c => `compdef _flow_args ${c}`).join('\n')}
102
+ compdef _flow flow
103
+ ${cmds.map(c => `compdef _flow ${c}`).join('\n')}
83
104
  `;
84
105
  }
85
106
 
86
107
  function fishScript() {
87
108
  const lines = [];
109
+ // Parent 'flow' command: subcommand completions
110
+ lines.push('# flow (parent command)');
111
+ for (const cmd of COMMANDS) {
112
+ lines.push(`complete -c flow -f -n '__fish_use_subcommand' -a ${cmd}`);
113
+ }
114
+ lines.push('');
115
+ // Per-subcommand flags for 'flow <sub>' form
116
+ for (const [cmd, flags] of Object.entries(FLAGS)) {
117
+ lines.push(`# flow ${cmd}`);
118
+ for (const f of flags) {
119
+ const name = f.replace(/^--/, '').replace(/=$/, '');
120
+ const hasArg = f.endsWith('=');
121
+ lines.push(`complete -c flow -n '__fish_seen_subcommand_from ${cmd}' -l ${name}${hasArg ? ' -r' : ''}`);
122
+ }
123
+ }
124
+ lines.push('');
125
+ // Hyphenated aliases
88
126
  for (const cmd of COMMANDS) {
89
127
  lines.push(`# flow-${cmd}`);
90
128
  const flags = FLAGS[cmd] || [];
package/bin/flow.mjs ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ /* flow — unified CLI router.
3
+ Usage: flow <command> [args]
4
+ Run 'flow --help' for the full command list.
5
+ */
6
+
7
+ import { spawnSync } from 'child_process';
8
+ import { readFileSync } from 'fs';
9
+ import { fileURLToPath } from 'url';
10
+ import { dirname } from 'path';
11
+
12
+ // Mirror the _client.mjs TLS re-spawn so the child sees --use-system-ca
13
+ // and doesn't re-spawn a third time (checks process.execArgv).
14
+ if (process.platform === 'win32' && !process.execArgv.includes('--use-system-ca')) {
15
+ const r = spawnSync(process.execPath, ['--use-system-ca', ...process.argv.slice(1)], {
16
+ stdio: 'inherit',
17
+ env: process.env,
18
+ });
19
+ process.exit(r.status ?? 1);
20
+ }
21
+
22
+ const __dir = dirname(fileURLToPath(import.meta.url));
23
+
24
+ // ── Command registry ────────────────────────────────────────────────────────
25
+ const CMDS = [
26
+ ['login', 'login.mjs', 'Authorize the CLI via browser (device flow)'],
27
+ ['pull', 'pull.mjs', 'Fetch board snapshot + @mentions (start of day)'],
28
+ ['claim', 'claim.mjs', 'Claim a task and mark it in-progress'],
29
+ ['comment', 'comment.mjs', 'Post a comment on a task'],
30
+ ['close', 'close.mjs', 'Mark a task done with a summary'],
31
+ ['create', 'create.mjs', 'Create a new task'],
32
+ ['edit', 'edit.mjs', 'Update task fields (title, priority, area, due…)'],
33
+ ['unblock', 'unblock.mjs', 'Clear a task\'s blocked-by dependency'],
34
+ ['propose', 'propose.mjs', 'Propose a sub-task for human approval'],
35
+ ['approve', 'approve.mjs', 'Approve a pending proposal'],
36
+ ['reject', 'reject.mjs', 'Reject a pending proposal with a reason'],
37
+ ['assign', 'assign.mjs', 'Assign a task to another actor'],
38
+ ['decisions', 'decisions.mjs', 'List pending proposals awaiting approval'],
39
+ ['handoff', 'handoff.mjs', 'Hand off a task to another agent'],
40
+ ['review', 'review.mjs', 'Request code review; moves task to in-review'],
41
+ ['search', 'search.mjs', 'Search tasks by text, status, area, or assignee'],
42
+ ['status', 'status.mjs', 'Quick board summary'],
43
+ ['standup', 'standup.mjs', 'Done/in-progress digest; --velocity for trend chart'],
44
+ ['sync', 'sync.mjs', 'Delta sync since N minutes (default 60)'],
45
+ ['log', 'log.mjs', 'Tail recent timeline events'],
46
+ ['heartbeat', 'heartbeat.mjs', 'Send a presence ping (~60s while active)'],
47
+ ['ping', 'ping.mjs', 'Health check or send a direct agent message'],
48
+ ['pr', 'pr.mjs', 'Record a PR link on a task timeline'],
49
+ ['project', 'project.mjs', 'List GitHub Projects v2 items'],
50
+ ['close-sprint', 'close-sprint.mjs', 'Close a sprint milestone (owner only)'],
51
+ ['whoami', 'whoami.mjs', 'Verify token and show current identity'],
52
+ ['completion', 'completion.mjs', 'Print shell completion script (bash/zsh/fish)'],
53
+ ];
54
+
55
+ const CMD_MAP = new Map(CMDS.map(([name, file]) => [name, file]));
56
+
57
+ // ── Help ────────────────────────────────────────────────────────────────────
58
+ function printHelp() {
59
+ let version = '';
60
+ try {
61
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
62
+ version = ` v${pkg.version}`;
63
+ } catch {}
64
+
65
+ process.stdout.write(`flow${version} — FlowCollab CLI\n\n`);
66
+ process.stdout.write(`Usage: flow <command> [options]\n\n`);
67
+ process.stdout.write(`Commands:\n`);
68
+ const pad = Math.max(...CMDS.map(([n]) => n.length)) + 2;
69
+ for (const [name, , desc] of CMDS) {
70
+ process.stdout.write(` ${name.padEnd(pad)}${desc}\n`);
71
+ }
72
+ process.stdout.write(`\nRun 'flow <command> --help' for command-specific flags.\n`);
73
+ process.stdout.write(`Hyphenated aliases (flow-login, flow-pull, …) remain supported.\n`);
74
+ }
75
+
76
+ // ── Dispatch ────────────────────────────────────────────────────────────────
77
+ const [, , sub, ...rest] = process.argv;
78
+
79
+ if (!sub || sub === '--help' || sub === '-h') {
80
+ printHelp();
81
+ process.exit(0);
82
+ }
83
+
84
+ if (sub === '--version' || sub === '-v') {
85
+ try {
86
+ const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
87
+ process.stdout.write(`${version}\n`);
88
+ } catch {
89
+ process.stdout.write('unknown\n');
90
+ }
91
+ process.exit(0);
92
+ }
93
+
94
+ const target = CMD_MAP.get(sub);
95
+ if (!target) {
96
+ process.stderr.write(`flow: unknown command '${sub}'\nRun 'flow --help' for the command list.\n`);
97
+ process.exit(1);
98
+ }
99
+
100
+ // Splice the subcommand out of argv so target module sees clean args.
101
+ // e.g. [node, flow.mjs, pull, --focus] → [node, flow.mjs, --focus]
102
+ // _client.mjs's arg()/positional() use process.argv.slice(2), so they
103
+ // read [--focus] — identical to what flow-pull sees when called directly.
104
+ process.argv.splice(2, 1);
105
+
106
+ await import(new URL(target, import.meta.url));
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "flowcollab",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "description": "Multi-Claude coordination layer — shared task board + CLI for teams running Claude Code",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "bin/"
8
8
  ],
9
9
  "bin": {
10
+ "flow": "bin/flow.mjs",
10
11
  "flow-create": "bin/create.mjs",
11
12
  "flow-pull": "bin/pull.mjs",
12
13
  "flow-claim": "bin/claim.mjs",