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.
- package/bin/completion.mjs +67 -29
- package/bin/flow.mjs +106 -0
- package/package.json +2 -1
package/bin/completion.mjs
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
import { positional, die } from './_client.mjs';
|
|
10
10
|
|
|
11
11
|
const COMMANDS = [
|
|
12
|
-
'pull', 'claim', 'comment', 'close', 'create', '
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
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
|
|
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
|
-
|
|
38
|
-
local cur
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
local
|
|
71
|
-
case "\$
|
|
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
|
-
|
|
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.
|
|
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",
|