c8ctl-plugin-nano 1.42.0 → 1.43.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/README.md CHANGED
@@ -473,8 +473,13 @@ c8ctl nano hire --name coder --rank senior --command copilot --protocol acp --pe
473
473
  # OpenCode — native ACP server (assembles `opencode acp`)
474
474
  c8ctl nano hire --name coder --rank senior --command opencode --arg acp --protocol acp --permission yolo
475
475
 
476
- # Claude Code via the `claude-agent-acp` adapter
477
- c8ctl nano hire --name coder --rank senior --command claude-agent-acp --protocol acp --permission yolo
476
+ # Qwennative ACP behind a HIDDEN flag (`qwen --experimental-acp`; present in
477
+ # the shipped cli.js but not in `qwen --help`). Detection recognises the
478
+ # switch, so no second `--acp` is appended.
479
+ c8ctl nano hire --name coder --rank senior --command qwen --arg --experimental-acp --protocol acp --permission yolo
480
+
481
+ # Claude Code — via the `claude-code-acp` adapter (npm @zed-industries/claude-code-acp)
482
+ c8ctl nano hire --name coder --rank senior --command claude-code-acp --protocol acp --permission yolo
478
483
 
479
484
  # Pi — via the `pi-acp` adapter
480
485
  c8ctl nano hire --name coder --rank senior --command pi-acp --protocol acp --permission yolo
@@ -492,6 +497,23 @@ on the host executor; container sandboxes remain **pipe-only** for now). There i
492
497
  **no change to the Camunda-8 worker⇄engine job protocol** — ACP governs only how a
493
498
  worker drives its local agent harness, not how it talks to the engine.
494
499
 
500
+ **Selecting the model under ACP.** `--model` only exports `AGENT_MODEL` into the
501
+ harness environment; it is **not** injected into the harness argv. And structured
502
+ `--arg` values are POSIX single-quoted by the plugin, so a literal `$AGENT_MODEL`
503
+ in an `--arg` does **not** expand (only the `--command` string is
504
+ shell-interpolated, since the harness is spawned with `shell: true`). The
505
+ deterministic, supported way to pin a model for an ACP hire is therefore to
506
+ **bake it into `--command`**, where the shell does interpolate:
507
+
508
+ ```bash
509
+ # The --command string IS shell-interpolated, so $AGENT_MODEL (or a literal name) works here:
510
+ c8ctl nano hire --name coder --rank senior --command 'copilot --acp --model gpt-5.4' --protocol acp
511
+ ```
512
+
513
+ (Equivalently, set a harness-specific env var with `--env` when the harness reads
514
+ the model from its environment.) This is the seam the nano-workforce install
515
+ script uses to route a detected model to each harness deterministically.
516
+
495
517
  ### Live profile reload (no restart on `assign`)
496
518
 
497
519
  A running `c8ctl nano work <name>` **watches** the profile it is servicing. When
@@ -857,6 +879,121 @@ How it works and where things live:
857
879
  - Stopping is SIGTERM → grace → SIGKILL, per worker and for the daemon; `stop`
858
880
  always clears `supervisor.json` so a stale marker never wedges a future start.
859
881
 
882
+ ## Composing a workforce: `workforce`
883
+
884
+ `supervisor` is imperative — you compose a fleet with a `start --worker …` plus a
885
+ pile of `add … --instances N` calls that live only in your shell history. A
886
+ **workforce manifest** makes that fleet a **reusable, inspectable artifact**: a
887
+ named set of supervised workers you compose once and bring up convergently with
888
+ one command.
889
+
890
+ ```bash
891
+ # Compose a fleet (each add creates/updates one entry; no supervisor needed yet)
892
+ c8ctl nano workforce add copilot --instances 5 --auto
893
+ c8ctl nano workforce add claude --instances 1 --auto
894
+ c8ctl nano workforce add qwen --instances 2 --roles pr-review,feature
895
+
896
+ c8ctl nano workforce list # print the manifest (+ --json)
897
+ c8ctl nano workforce start # ensure the daemon is up, then reconcile
898
+ c8ctl nano workforce status # desired vs actual, per worker (+ --json)
899
+ c8ctl nano workforce stop # remove this manifest's workers (+ stop an empty daemon)
900
+ c8ctl nano workforce remove qwen # drop an entry ("all" clears the manifest)
901
+ ```
902
+
903
+ Every subcommand takes `--profile <name>` (default `default`) to select which
904
+ manifest it operates on, so you can keep several — `--profile review-only`,
905
+ `--profile full-fleet` — side by side.
906
+
907
+ ### The manifest
908
+
909
+ A manifest is a **separate JSON document per name**, stored at
910
+ `<stateHome>/workforce/<name>.json` (`<stateHome>` is `C8CTL_NANO_HOME` or the
911
+ per-OS app dir). It is deliberately **not** a key in `config.json`: manifests are
912
+ user-curated documents meant to be read, edited by hand, diffed and copied
913
+ between machines; `config.json` is plugin state.
914
+
915
+ ```jsonc
916
+ {
917
+ "version": 1,
918
+ "name": "default",
919
+ "workers": [
920
+ { "profile": "copilot", "instances": 5, "roles": "auto" },
921
+ { "profile": "claude", "instances": 1, "roles": "auto" },
922
+ { "profile": "qwen", "instances": 2, "roles": ["pr-review", "feature"] }
923
+ ]
924
+ }
925
+ ```
926
+
927
+ | Field | Meaning |
928
+ | --- | --- |
929
+ | `profile` | A hired profile name (must exist in `hires`; validated at `add` and again at `start`). |
930
+ | `instances` | How many workers to run for this entry (`1`..`MAX_ADD_INSTANCES`). |
931
+ | `roles` | `"auto"` → run the worker with `--auto`; **or** an array of capability names. |
932
+ | `autoScope` | Optional, only with `roles: "auto"` → forwards `--auto-scope <value>`. |
933
+ | `args` | Optional array of extra `work` flags forwarded verbatim (escape hatch: `--sandbox`, `--stream`, …). |
934
+
935
+ ### `roles` → job types
936
+
937
+ `roles` is resolved **at start time from the manifest**, independent of what the
938
+ profile was hired with (the install script hires with `--capabilities ""`):
939
+
940
+ - `"auto"` → `c8ctl nano work <profile> --auto [--auto-scope X]`. No capability
941
+ gate; serves every deployed agent job type. This is what the install script
942
+ sets, and the default when you pass neither `--auto` nor `--roles` to `add`.
943
+ - `["pr-review","feature"]` → repeatable `--job-type <rank>:<role>`, e.g. for a
944
+ `senior` hire: `--job-type senior:pr-review --job-type senior:feature`.
945
+
946
+ `--job-type` is chosen deliberately over `nano assign`: it uses an existing
947
+ work-time flag (no new override surface on `work`) and **does not mutate the
948
+ profile** — the same profile can appear in two manifests with different role
949
+ sets. `--auto` and `--roles` are mutually exclusive; `--auto-scope` requires
950
+ `--auto`.
951
+
952
+ ### Reconcile semantics — `start` is convergent, not additive
953
+
954
+ `start` ensures the supervisor daemon is running (starting it if needed), then
955
+ **reconciles** the running workers to the manifest:
956
+
957
+ - Start workers that are missing.
958
+ - Stop workers that belong to this manifest but are no longer desired (entry
959
+ removed, or `instances` reduced).
960
+ - Leave already-running, still-desired workers **untouched** — no restart churn,
961
+ no job interruption. A second `start` with an unchanged manifest starts
962
+ nothing, stops nothing and restarts nothing.
963
+ - Workers **not** owned by this manifest (added by hand with `supervisor add`, or
964
+ owned by another manifest) are never touched.
965
+
966
+ The enabling detail is **deterministic worker names**: workforce-owned workers
967
+ are named `wf-<manifest>-<profile>-<index>` (`wf-default-copilot-1` …
968
+ `wf-default-copilot-5`). Since `--instances` can't combine with `--name`, `start`
969
+ issues N single adds with an explicit `--name` each. The `wf-<manifest>-` prefix
970
+ is also how `status`/`stop` identify ownership; a name collision with a
971
+ hand-added worker running a different profile is skipped with a warning rather
972
+ than clobbered.
973
+
974
+ ### Portability
975
+
976
+ A manifest is portable: copy `default.json` to another machine, `hire` the same
977
+ profile names there, and `workforce start`. Because `roles` resolves from the
978
+ manifest (not the hire), the fleet comes up identically even if the profiles were
979
+ hired with `--capabilities ""`.
980
+
981
+ ### Validation & errors
982
+
983
+ - `add`: the profile must exist in `hires` (otherwise an error pointing at
984
+ `c8ctl nano hire`); `instances` in `1..MAX_ADD_INSTANCES`; `--roles`/`--auto`
985
+ mutually exclusive; `--auto-scope` requires `--auto`.
986
+ - `start`: an entry whose profile was since deleted → a clear error, the entry is
987
+ skipped, the rest continue, and the command exits non-zero (a partial start
988
+ never leaves a half-reconciled fleet silently).
989
+ - A malformed/torn manifest, or an unknown `version`, is refused with an explicit
990
+ error naming the file path — never silently treated as empty.
991
+ - `start` with an empty/absent manifest → a friendly pointer at `workforce add`,
992
+ exit 0.
993
+
994
+ `--json` on `list`/`status` emits machine-readable output (through the same
995
+ output-mode-aware logger) so the install script and CI can consume it.
996
+
860
997
  ## Cleaning up disk
861
998
 
862
999
  ```bash