bullswarm 0.10.2 → 0.10.3

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/AGENTS.md CHANGED
@@ -37,7 +37,7 @@ content. Published as `bullswarm` on npm.
37
37
  ## Development
38
38
 
39
39
  ```bash
40
- npm test # 232 tests, no network needed (meters read from cache)
40
+ npm test # 235 tests, no network needed (meters read from cache)
41
41
  node bin/bullswarm.js doctor --json # readiness report
42
42
  node bin/bullswarm.js workflow list # discover workflows
43
43
  node bin/bullswarm.js workflow runs # ongoing workflow instances
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # bullswarm changelog
2
2
 
3
+ ## 0.10.3 — contextual help everywhere
4
+
5
+ - Added side-effect-free `-h` / `--help` handling for the top-level CLI and
6
+ every command and nested subcommand, including workflow drafts, run history,
7
+ approvals, actions, integrations, and strategy policy controls.
8
+ - Added a centralized command help tree so contextual help is consistent and
9
+ intercepted before setup, provider discovery, state writes, or destructive
10
+ command execution.
11
+
3
12
  ## 0.10.2 — cross-agent skill integration
4
13
 
5
14
  - Added explicit `bullswarm integrate status|install|remove` support for Codex,
package/README.md CHANGED
@@ -5,6 +5,15 @@ orchestrator, build and expand the plan, route bounded worker actions by quota,
5
5
  verify the result, and finish without an initiating agent authoring a graph.
6
6
  Every delegate output is judged by content before it counts.
7
7
 
8
+ Every command and nested subcommand supports contextual `-h` / `--help`
9
+ without initializing state or executing the command:
10
+
11
+ ```bash
12
+ bullswarm --help
13
+ bullswarm workflow run --help
14
+ bullswarm workflow draft step add --help
15
+ ```
16
+
8
17
  ## The doctrine (non-negotiable)
9
18
 
10
19
  1. **Judge by CONTENT, not exit code.** Every delegate CLI can exit 0 while
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bullswarm",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "description": "Route work across coding-agent CLI subscriptions — paced by live quota meters, verified by content, never trusting exit codes.",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -10,6 +10,10 @@ CLI subscription has the most quota headroom. Every delegate output is
10
10
  judged by **content**, not exit code. A non-zero exit is never a success; a
11
11
  `verified` output is.
12
12
 
13
+ Every command and nested subcommand supports side-effect-free `-h` / `--help`.
14
+ When a flag or argument is uncertain, inspect the exact surface before acting,
15
+ for example `bullswarm workflow draft step add --help`.
16
+
13
17
  This skill is registered globally by:
14
18
 
15
19
  ```bash
package/src/cli.js CHANGED
@@ -17,6 +17,7 @@ import { release } from './lib/release.js';
17
17
  import { cmdWorkflow } from './workflow/cli.js';
18
18
  import { cmdStrategy, maybeRefreshStrategy } from './strategy-cli.js';
19
19
  import { cmdIntegrate, installIntegration } from './integrate.js';
20
+ import { helpForArgs } from './help.js';
20
21
 
21
22
  export function getBullswarmDir() {
22
23
  const h = process.env.BULLSWARM_HOME?.trim();
@@ -394,6 +395,11 @@ async function cmdDoctor(opts) {
394
395
  // --- main ---------------------------------------------------------------------
395
396
 
396
397
  export async function main(argv) {
398
+ const help = helpForArgs(argv);
399
+ if (help) {
400
+ console.log(help);
401
+ return 0;
402
+ }
397
403
  const [verb, ...rest] = argv;
398
404
  const opts = parseArgs(rest);
399
405
  const { ensureSetup } = await import('./setup.js');
package/src/help.js ADDED
@@ -0,0 +1,150 @@
1
+ // Central CLI help tree. Help is resolved before setup or command dispatch so
2
+ // `--help` is side-effect free even for commands that normally touch state.
3
+
4
+ const top = `Usage: bullswarm <command> [options]
5
+
6
+ Route bounded work across coding-agent subscriptions and verify the result.
7
+
8
+ Commands:
9
+ setup discover and configure installed coding agents
10
+ integrate register Bullswarm guidance with Codex, Claude, and Grok
11
+ run dispatch one bounded task
12
+ health re-judge saved delegate outputs
13
+ pools show routing pools, meters, and quarantine state
14
+ strategy discover models and manage tier assignments
15
+ doctor report installation readiness
16
+ workflow create, execute, observe, and audit workflows
17
+ runs alias for workflow runs
18
+ version print the installed version
19
+ release create a version commit and tag
20
+
21
+ Run bullswarm <command> --help for command-specific help.`;
22
+
23
+ const leaf = (usage, body = '') => `Usage: ${usage}${body ? `\n\n${body}` : ''}`;
24
+
25
+ const HELP = {
26
+ _text: top,
27
+ setup: { _text: leaf('bullswarm setup [--yes] [--integrate] [--agents <list>] [--json]',
28
+ 'Discover installed agent CLIs and initialize routing. Without --yes on a TTY, opens the interactive wizard.') },
29
+ integrate: {
30
+ _text: leaf('bullswarm integrate <status|install|remove|retire-legacy> [options]',
31
+ 'Manage the canonical Bullswarm skill and recursion-safe global awareness rules.'),
32
+ status: { _text: leaf('bullswarm integrate status [--agents codex,claude,grok] [--json]') },
33
+ install: { _text: leaf('bullswarm integrate install [--agents codex,claude,grok] --yes [--json]') },
34
+ remove: { _text: leaf('bullswarm integrate remove [--agents codex,claude,grok] --yes [--json]') },
35
+ 'retire-legacy': { _text: leaf('bullswarm integrate retire-legacy --yes [--json]',
36
+ 'Recoverably archive the retired Claude offload skill.') },
37
+ },
38
+ run: { _text: leaf('bullswarm run --lane <analyze|build|chore> --add-dir <dir> (--task-file <file> | --prompt <text>) [options]',
39
+ 'Options: --effort <high|medium|low>, --timeout <seconds>, --json. Routes one task and verifies its content.') },
40
+ health: { _text: leaf('bullswarm health [--json]', 'Re-judge saved outputs and report failed gates or quarantine clusters.') },
41
+ pools: { _text: leaf('bullswarm pools [--force] [--json]', 'Show live meter, quota-surplus, burst-gate, and quarantine state.') },
42
+ doctor: { _text: leaf('bullswarm doctor [--json]', 'Self-initialize if needed and report readiness without dispatching work.') },
43
+ version: { _text: leaf('bullswarm version') },
44
+ release: { _text: leaf('bullswarm release <patch|minor|major> [--dry-run]') },
45
+ strategy: {
46
+ _text: leaf('bullswarm strategy <command> [options]',
47
+ 'Commands: refresh, apply, show, assign, clear-assignment, set-subscription, auto.'),
48
+ refresh: { _text: leaf('bullswarm strategy refresh [--json] [--apply --yes] [--refresh-hours <n>]') },
49
+ recommend: { _text: leaf('bullswarm strategy recommend [--json] [--apply --yes]', 'Alias for strategy refresh.') },
50
+ apply: { _text: leaf('bullswarm strategy apply --yes [--refresh-hours <n>]') },
51
+ show: { _text: leaf('bullswarm strategy show [--json]') },
52
+ assign: { _text: leaf('bullswarm strategy assign <high|medium|low> --pool <pool> --model <model>') },
53
+ 'clear-assignment': { _text: leaf('bullswarm strategy clear-assignment <high|medium|low>') },
54
+ 'set-subscription': { _text: leaf('bullswarm strategy set-subscription <pool> [--plan <name>] [--monthly-usd <n|unknown>] [--included-usd <n|unknown>] [--quota-window <name>]') },
55
+ auto: {
56
+ _text: leaf('bullswarm strategy auto <status|off> [--yes]'),
57
+ status: { _text: leaf('bullswarm strategy auto status') },
58
+ off: { _text: leaf('bullswarm strategy auto off --yes') },
59
+ },
60
+ },
61
+ workflow: {
62
+ _text: leaf('bullswarm workflow <command> [options]',
63
+ 'Commands: goal, run, validate, list, draft, runs, capabilities, inspect, tui, watch, events, steer, action, approval.'),
64
+ goal: { _text: leaf('bullswarm workflow goal "<goal>" [--cwd <dir>] [--detach] [--json] [planning options]',
65
+ 'Use --resume <shortId|runId> to resume. Planning options include --orchestrator, --max-agents, --max-expansion-rounds, --max-actions, --max-items-per-expansion, --max-workflow-seconds, --concurrency, and --retry-attempts.') },
66
+ run: { _text: leaf('bullswarm workflow run <file-or-name> [--input k=v]... [--resume <shortId|runId>] [--json] [--quiet]') },
67
+ validate: { _text: leaf('bullswarm workflow validate <file-or-name>') },
68
+ list: { _text: leaf('bullswarm workflow list [--json]') },
69
+ capabilities: { _text: leaf('bullswarm workflow capabilities [--json]') },
70
+ inspect: { _text: leaf('bullswarm workflow inspect <file-or-name>') },
71
+ tui: { _text: leaf('bullswarm workflow tui [<runId>] [--json] [--all] [--show <runId>] [--cancel <runId>]') },
72
+ watch: { _text: leaf('bullswarm workflow watch <runId> [--interval <seconds>] [--heartbeat <seconds>] [--jsonl] [--once]') },
73
+ events: { _text: leaf('bullswarm workflow events <runId> [--after <sequence>] [--json]') },
74
+ steer: { _text: leaf('bullswarm workflow steer <runId> --message <guidance> [--json]') },
75
+ action: {
76
+ _text: leaf('bullswarm workflow action <command> ...', 'Commands: show.'),
77
+ show: { _text: leaf('bullswarm workflow action show <runId> <actionId> [--json]') },
78
+ },
79
+ approval: {
80
+ _text: leaf('bullswarm workflow approval <approve|reject> <runId> [--json]'),
81
+ approve: { _text: leaf('bullswarm workflow approval approve <runId> [--json]') },
82
+ reject: { _text: leaf('bullswarm workflow approval reject <runId> [--json]') },
83
+ },
84
+ runs: runsHelp(),
85
+ draft: draftHelp(),
86
+ },
87
+ };
88
+
89
+ // Top-level `runs` is a documented alias and gets the same nested help.
90
+ HELP.runs = HELP.workflow.runs;
91
+
92
+ export const HELP_PATHS = Object.freeze(collectPaths(HELP));
93
+
94
+ export function helpForArgs(argv) {
95
+ const wantsHelp = argv.includes('--help') || argv.includes('-h') || argv[0] === 'help';
96
+ if (!wantsHelp) return null;
97
+ const tokens = argv[0] === 'help' ? argv.slice(1) : argv;
98
+ let node = HELP;
99
+ for (const token of tokens) {
100
+ if (token === '--help' || token === '-h' || token.startsWith('-')) continue;
101
+ if (!node[token]) break;
102
+ node = node[token];
103
+ }
104
+ return node._text ?? HELP._text;
105
+ }
106
+
107
+ function runsHelp() {
108
+ return {
109
+ _text: leaf('bullswarm workflow runs [list] [--all|--historical] [--name <workflow>] [--since <time>] [--until <time>] [--limit <n>] [--json]',
110
+ 'Commands: show <id>, delete <id> --yes. Time filters use the workflow initiation timestamp.'),
111
+ list: { _text: leaf('bullswarm workflow runs list [--all|--historical] [--name <workflow>] [--since <time>] [--until <time>] [--limit <n>] [--json]') },
112
+ show: { _text: leaf('bullswarm workflow runs show <shortId|runId> [--json]') },
113
+ delete: { _text: leaf('bullswarm workflow runs delete <shortId|runId> --yes [--force] [--json]') },
114
+ };
115
+ }
116
+
117
+ function draftHelp() {
118
+ return {
119
+ _text: leaf('bullswarm workflow draft <command> [options]',
120
+ 'Commands: create, show, list, phase, step, set, validate, export, delete, run.'),
121
+ create: { _text: leaf('bullswarm workflow draft create <name> [--description <text>] [--input k=v]... [--required <keys>] [--json]') },
122
+ show: { _text: leaf('bullswarm workflow draft show <name> [--json]') },
123
+ list: { _text: leaf('bullswarm workflow draft list [--json]') },
124
+ phase: {
125
+ _text: leaf('bullswarm workflow draft phase <add|remove> <draft> <phase> [--json]'),
126
+ add: { _text: leaf('bullswarm workflow draft phase add <draft> <phase> [--json]') },
127
+ remove: { _text: leaf('bullswarm workflow draft phase remove <draft> <phase> [--json]') },
128
+ },
129
+ step: {
130
+ _text: leaf('bullswarm workflow draft step <add|remove|set> <draft> <phase> <step-id> [options]'),
131
+ add: { _text: leaf('bullswarm workflow draft step add <draft> <phase> <step-id> [--type <run|fanout|verify|decide>] [--lane <lane>] [--prompt <text>] [step options]') },
132
+ remove: { _text: leaf('bullswarm workflow draft step remove <draft> <phase> <step-id> [--json]') },
133
+ set: { _text: leaf('bullswarm workflow draft step set <draft> <phase> <step-id> <field> --value <text> [--json]') },
134
+ },
135
+ set: { _text: leaf('bullswarm workflow draft set <draft> <field> --value <text> [--json]') },
136
+ validate: { _text: leaf('bullswarm workflow draft validate <name> [--json]') },
137
+ export: { _text: leaf('bullswarm workflow draft export <name> <out-file> [--json]') },
138
+ delete: { _text: leaf('bullswarm workflow draft delete <name> --yes [--json]') },
139
+ run: { _text: leaf('bullswarm workflow draft run <name> [--input k=v]... [--resume <shortId|runId>] [--json] [--quiet]') },
140
+ };
141
+ }
142
+
143
+ function collectPaths(node, prefix = []) {
144
+ const paths = prefix.length ? [prefix] : [[]];
145
+ for (const [name, child] of Object.entries(node)) {
146
+ if (name === '_text') continue;
147
+ paths.push(...collectPaths(child, [...prefix, name]));
148
+ }
149
+ return paths;
150
+ }