@opvs-ai/cli 0.14.0 → 0.15.1

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
@@ -118,6 +118,57 @@ Override workspace selection and settings without modifying config:
118
118
  opvs boards list # List all boards
119
119
  opvs boards get <id> # Board details + columns
120
120
  opvs boards create -n "Sprint 1" # Create a board
121
+ opvs boards templates --board-type factory # Presets + each column's claim wiring
122
+ opvs boards create -n "Build" --board-type factory --column-preset factory-full
123
+ opvs projects add-board <project-uuid> <new-board-uuid> # REQUIRED for a factory board — see below
124
+ opvs boards set-routing ASP --harness codex # Board routing defaults ('none' clears)
125
+ opvs boards projects ASP # Projects claiming the board (none|resolved|ambiguous)
126
+ opvs boards add-to-project ASP <project-uuid> # Additive
127
+ opvs boards move-to-project ASP <project-uuid> --yes # REMOVES it from every other project — previews first
128
+ opvs boards health ASP --profile factory # Unsupplied thresholds print NOT CHECKED
129
+ opvs boards apply-preset ASP factory-full # DRY RUN by default; --yes applies
130
+ opvs boards apply-preset ASP factory-full --yes --json | jq .applied # ONE JSON document: the applied plan
131
+ opvs boards update ASP --archived true # Hidden from `boards list`; cards stay CLAIMABLE
132
+ ```
133
+
134
+ > ⚠️ **A factory board claims NOTHING until it is in exactly ONE active project.** Every claim on a
135
+ > factory board in no project (`none`) or in two or more (`ambiguous`) returns
136
+ > `{task_id: null, reason: "board_project_unresolved"}` — the columns can be perfectly wired and the
137
+ > factory still builds nothing. `boards create`, `boards update --board-type factory`,
138
+ > `add-to-project`, `move-to-project`, `apply-preset`, `projects add-board | remove-board | archive`
139
+ > all check afterwards and warn on stderr when a factory board is left unresolved.
140
+
141
+ ### Members
142
+
143
+ ```bash
144
+ opvs members list <boardId>
145
+ opvs members add <boardId> --user <uuid> # or --agent <uuid>; a member of YOUR brand
146
+ opvs members invite <boardId> --email x@y.com --role commenter # outside your brand → a guest of this board
147
+ opvs members remove <boardId> <uuid> --type agent # --type defaults to user
148
+ ```
149
+
150
+ > `--role` (viewer | commenter | editor) caps an **external guest** only; a member of your own brand
151
+ > keeps full access whatever you pass.
152
+
153
+ ### Columns
154
+
155
+ ```bash
156
+ opvs columns list <boardId>
157
+ opvs columns update <columnId> --claimable-by-roles builder # REPLACES the role set
158
+ opvs columns update <columnId> --on-claim-move-to executing --sla-minutes 120 --sla-action comment
159
+ opvs columns update <columnId> --managed-by user:<uuid> # owner inbox + SLA @mention; grants nothing
160
+ opvs columns update <columnId> --wip-limit none # 'none' clears; 'ten' is refused, not a clear
161
+ ```
162
+
163
+ > ⚠️ A role on ANY column changes the whole board: role-less workers are then held to
164
+ > declared columns only. `--on-done-move-to` is advisory — nothing moves a card by it today.
165
+
166
+ ### Projects
167
+
168
+ ```bash
169
+ opvs projects set-routing <project-uuid> --model claude-opus-5
170
+ opvs projects health <project-uuid> --profile planner-orchestration
171
+ opvs projects bundle set <project-uuid> --command ship --command-body ship=./ship.md # a body map REPLACES
121
172
  ```
122
173
 
123
174
  ### Tasks
@@ -129,6 +180,12 @@ opvs tasks get <id> # Task details
129
180
  opvs tasks create --board <id> -t "Title" # Create task
130
181
  opvs tasks update <id> --status review # Update status
131
182
  opvs tasks update <id> --result-file out.md # Attach result from file
183
+ opvs tasks update <id> --pr 1201 --branch fix/x --worktree llm16:/root/OPVS-x --deploy-needs agentboard
184
+ opvs tasks update <id> --blocked-reason "CI red" --status blocked # the reason alone changes no status
185
+ opvs tasks create --board <id> -t "Title" --workflow-doc ship --workflow-rule "Never force-push" --estimate 45
186
+ opvs tasks claim <boardId> --role builder # Take ONE runnable card; there is no lease
187
+ opvs tasks search -q OSB-78 # By ref, PR number (#1256), title, description
188
+ opvs tasks search -q deploy --board OSB,ASP --status pending --column Review --offset 20
132
189
  ```
133
190
 
134
191
  ### Comments
@@ -137,6 +194,9 @@ opvs tasks update <id> --result-file out.md # Attach result from file
137
194
  opvs comments list <task-id> # List task comments
138
195
  opvs comments add <task-id> "message" # Add inline comment
139
196
  opvs comments add <task-id> -f output.md # Comment from file
197
+ opvs comments add <task-id> "x" --internal # A LABEL — every reader still sees it
198
+ opvs comments edit <comment-id> "new text" # Any board writer can edit any comment; old text is not kept
199
+ opvs files fetch-url <task-id> <url> # Store the file's BYTES on the card (attach-url keeps a link)
140
200
  ```
141
201
 
142
202
  ### Docs
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "stamp_version": 1,
3
3
  "package": "@opvs-ai/cli",
4
- "version": "0.14.0",
5
- "source_digest": "1aaeceffd9ec2bce9fa7b87a30607a8847e306696b95429bdf230adae7c11d76",
6
- "source_files": 38,
7
- "built_at": "2026-09-11T12:20:56.934Z",
4
+ "version": "0.15.1",
5
+ "source_digest": "89167e4c4459cfe842c705996bfa50b6d8abf2e910c65af7d61a6a9ca985d8bf",
6
+ "source_files": 41,
7
+ "built_at": "2026-09-15T21:57:07.190Z",
8
8
  "node": "v22.22.0"
9
9
  }
@@ -2,4 +2,61 @@
2
2
  * opvs boards — board CRUD
3
3
  */
4
4
  import { Command } from "commander";
5
+ interface ColumnChange {
6
+ slug: string;
7
+ name: string;
8
+ action: string;
9
+ changes?: Record<string, unknown[]>;
10
+ cards_affected?: number;
11
+ notes?: string[];
12
+ }
13
+ interface PresetConflict {
14
+ slug: string;
15
+ code: string;
16
+ detail: string;
17
+ blocking?: boolean;
18
+ }
19
+ interface ApplyPresetPlan {
20
+ preset?: string;
21
+ board_type?: string;
22
+ mode?: string;
23
+ applied?: boolean;
24
+ columns?: ColumnChange[];
25
+ conflicts?: PresetConflict[];
26
+ summary?: Record<string, number>;
27
+ extra_columns?: string[];
28
+ }
29
+ /**
30
+ * Print an apply-preset plan: one line per column with the card count it puts
31
+ * in scope, then conflicts and the columns the preset leaves alone. The card
32
+ * count is the number an operator reads to decide whether to apply, so it is
33
+ * on every row rather than only in the summary.
34
+ */
35
+ export declare function renderPresetPlan(plan: ApplyPresetPlan): string;
36
+ /**
37
+ * The columns whose planned change differs between the dry run the operator saw
38
+ * and the plan the server actually applied (ASP-118 F14).
39
+ *
40
+ * The apply is a SECOND request with no plan token or version check
41
+ * (`board_preset_service.py`), so another session can change the columns in
42
+ * between — and then a different set of rebinds and card counts is written
43
+ * than the one on screen. Same shape as `move-to-project`'s `removed_from`
44
+ * comparison: the server's answer is the truth, and a difference is reported.
45
+ *
46
+ * @returns one line per differing column slug; empty when they match.
47
+ */
48
+ export declare function presetPlanDrift(preview: ApplyPresetPlan, applied: ApplyPresetPlan): string[];
5
49
  export declare function registerBoardCommands(program: Command): void;
50
+ /**
51
+ * Parse repeatable `--manager slug=type:id` into the `managers` map
52
+ * `ApplyPresetRequest` takes. The shape is checked here because the server's
53
+ * coherence rule (a `user` needs `managed_by_user_id`, an `agent` needs
54
+ * `managed_by_agent_id`) is exactly what a hand-typed flag gets wrong, and a
55
+ * whole-request 422 names the field path rather than the flag.
56
+ */
57
+ export declare function parseManagers(raw: string[]): Record<string, {
58
+ managed_by_type: "user" | "agent";
59
+ managed_by_user_id?: string;
60
+ managed_by_agent_id?: string;
61
+ }>;
62
+ export {};