@gonovus-ai/spec-flow-cli 0.4.0 → 0.5.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.
Files changed (33) hide show
  1. package/README.md +135 -2
  2. package/dist/cli.js +24 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/custom-field-color-commands.d.ts +7 -0
  5. package/dist/custom-field-color-commands.js +22 -0
  6. package/dist/custom-field-color-commands.js.map +1 -0
  7. package/dist/github-connection-commands.js +23 -3
  8. package/dist/github-connection-commands.js.map +1 -1
  9. package/dist/product-map-commands.d.ts +2 -0
  10. package/dist/product-map-commands.js +2 -0
  11. package/dist/product-map-commands.js.map +1 -1
  12. package/dist/project-administration-commands.d.ts +3 -0
  13. package/dist/project-administration-commands.js +31 -0
  14. package/dist/project-administration-commands.js.map +1 -0
  15. package/dist/resource-command-runtime.d.ts +16 -0
  16. package/dist/resource-command-runtime.js +2 -0
  17. package/dist/resource-command-runtime.js.map +1 -0
  18. package/dist/screen-image-commands.d.ts +10 -0
  19. package/dist/screen-image-commands.js +77 -0
  20. package/dist/screen-image-commands.js.map +1 -0
  21. package/dist/spec-discussion-commands.d.ts +3 -0
  22. package/dist/spec-discussion-commands.js +56 -0
  23. package/dist/spec-discussion-commands.js.map +1 -0
  24. package/dist/spec-owner-commands.d.ts +3 -0
  25. package/dist/spec-owner-commands.js +37 -0
  26. package/dist/spec-owner-commands.js.map +1 -0
  27. package/dist/work-item-asset-commands.d.ts +13 -0
  28. package/dist/work-item-asset-commands.js +10 -9
  29. package/dist/work-item-asset-commands.js.map +1 -1
  30. package/dist/work-item-query-commands.d.ts +3 -0
  31. package/dist/work-item-query-commands.js +29 -0
  32. package/dist/work-item-query-commands.js.map +1 -0
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -22,7 +22,7 @@ accepts signed S3 and Azure BlockBlob reservations, including reservations from
22
22
  its URL out of logs. Uploading bytes alone does not attach an asset; complete the
23
23
  matching task or Screen finalization command separately.
24
24
 
25
- The current CLI contract is **0.4.0**, coordinated with the official Skill's exact
25
+ The current CLI contract is **0.5.0**, coordinated with the official Skill's exact
26
26
  pin. It breaks 0.2.0 callers: Spec, Capability, Glossary, Proposal and
27
27
  authoring-history commands address shared knowledge with `--workspace-id` instead of
28
28
  `--project-id`, `project directory` is replaced by `spec list`, Session grants
@@ -36,7 +36,7 @@ seven-day Better Auth CLI Session. It supports the Node.js 24 runtime line;
36
36
  Node.js 22 and unverified future major versions are intentionally unsupported.
37
37
 
38
38
  ```bash
39
- npm install --global @gonovus-ai/spec-flow-cli@0.4.0
39
+ npm install --global @gonovus-ai/spec-flow-cli@0.5.0
40
40
  specflow auth login
41
41
  specflow auth status
42
42
  specflow auth logout
@@ -824,3 +824,136 @@ the same editing and publication contract. To move a Spec to Unassigned, use
824
824
  `specflow spec update --workspace-id <id> --spec-id <id> --capability-id none
825
825
  --expected-version <version>`. Capability deletion rejects child Capabilities;
826
826
  deleting a leaf moves its Specs to Unassigned and records their placement changes.
827
+
828
+
829
+ Connect 1–50 repositories atomically with one idempotency key:
830
+
831
+ ```bash
832
+ specflow github connection connect-batch --project-id PROJECT_ID \
833
+ --repository owner/tasks:10:20 --repository owner/mobile:10:21 \
834
+ --outbound-repository-id 20 --idempotency-key UUID
835
+ ```
836
+
837
+ Repeat `--repository owner/name:installationId:repositoryId` for each repository.
838
+ Use `--no-outbound` to explicitly select none. Without either option, only the
839
+ first repository of the Project's first batch becomes outbound; later batches
840
+ only create from GitHub. A Project with an outbound repository cannot nominate
841
+ another during connection. Workspace binding conflicts reject the whole batch.
842
+ A real Web/CLI Session with current Workspace settings permission and personal
843
+ GitHub administrator access to every repository is required. The single-repository
844
+ `github connection connect` command remains available.
845
+
846
+ ## Spec responsibility, Scenario discussions and audit pages (0.5.0)
847
+
848
+ Version 0.5.0 adds these commands compatibly to 0.4.0 (ADR 0065). The Skill
849
+ source pins the matching minor version; publishing remains a separate release.
850
+ All commands emit the existing JSON result on stdout; `--json` is accepted
851
+ explicitly. Failures keep the existing structured stderr and exit categories.
852
+
853
+ ```bash
854
+ specflow spec owner read --workspace-id W --spec-id PAY-S1
855
+ specflow spec owner candidates --workspace-id W --spec-id PAY-S1
856
+ specflow spec owner assign --workspace-id W --spec-id PAY-S1 --user-id USER --expected-version '"1"' --idempotency-key UUID
857
+ specflow spec owner clear --workspace-id W --spec-id PAY-S1 --expected-version '"2"' --idempotency-key UUID
858
+ specflow spec discussion targets --workspace-id W --spec-id PAY-S1 --context-kind Published
859
+ specflow spec discussion list --workspace-id W --spec-id PAY-S1 --context-kind Published --scenario-id PAY-S1-1 --limit 30
860
+ specflow spec discussion comments --workspace-id W --spec-id PAY-S1 --thread-id THREAD_UUID --limit 30
861
+ specflow spec discussion start --workspace-id W --spec-id PAY-S1 --scenario-id PAY-S1-1 --context-hash HASH --content 'Question' --idempotency-key UUID
862
+ specflow spec discussion reply --workspace-id W --spec-id PAY-S1 --thread-id THREAD_UUID --context-kind WorkingDraft --context-hash HASH --content 'Reply' --idempotency-key UUID
863
+ specflow spec discussion edit-comment --workspace-id W --spec-id PAY-S1 --thread-id THREAD_UUID --comment-id COMMENT_UUID --content 'Edited' --expected-version '"1"' --idempotency-key UUID
864
+ specflow spec discussion delete-comment --workspace-id W --spec-id PAY-S1 --thread-id THREAD_UUID --comment-id COMMENT_UUID --expected-version '"2"' --idempotency-key UUID
865
+ specflow spec discussion resolve --workspace-id W --spec-id PAY-S1 --thread-id THREAD_UUID --expected-version '"3"' --idempotency-key UUID
866
+ specflow spec discussion reopen --workspace-id W --spec-id PAY-S1 --thread-id THREAD_UUID --expected-version '"4"' --idempotency-key UUID
867
+ specflow project rename --project-id PROJECT_UUID --name 'Delivery' --expected-version '"1"' --idempotency-key UUID
868
+ specflow project audit-log --project-id PROJECT_UUID --limit 50
869
+ specflow workspace audit-log --workspace-id W --limit 50
870
+ ```
871
+
872
+ Owner reads return `{ owner, version }`, candidates return `{ candidates }`, and
873
+ assignment/clear return `{ version }`. Responsibility grants no additional access;
874
+ only currently eligible Workspace members can be assigned. Clearing is explicit.
875
+
876
+ Discussion targets return `{ targets, targetsAvailable, currentContext,
877
+ canComment }`. Obtain the allocated Scenario ID and its `context.hash` there;
878
+ pass the exact hash as `--context-hash` to start/reply. Every discussion command
879
+ accepts `--context-kind Published|WorkingDraft` (default Published). Never use a
880
+ Published hash for different Working Draft content. `SPEC_DISCUSSION_CONTEXT_CHANGED`
881
+ retains its public code and maps to `Invalid Operation`; deliberately reread the
882
+ selected context before submitting a new intent. Edits preserve original comment
883
+ context, and deletion retains a tombstone. Only authors edit/delete their comments;
884
+ initiators or current Spec editors resolve/reopen threads. Use `comments[].version`
885
+ for comment writes and `threads[].version` for status writes; they are independent.
886
+ Discussion writes return `{ threadId, commentId, replayed }`.
887
+
888
+ Thread, comment and audit reads return one bounded page and `nextCursor`; repeat
889
+ with `--cursor <nextCursor>` and the same scope/view/filter to continue. Discussion
890
+ pages have `threads` or `comments`; audit pages have `events` including original
891
+ source and actor attribution. Audit access is checked separately for the Project
892
+ or Workspace. Rename returns the existing `{ project }` lifecycle result; it
893
+ retains Project identity and rejects ordinary writes to Archived Projects.
894
+
895
+ All new writes require an explicit UUID idempotency key. A bounded transport retry
896
+ reuses the same input, observed version, credential and key; retain that key after
897
+ an ambiguous result. Stale versions map to `Resource Changed`. The CLI neither
898
+ fetches a replacement version nor changes the requested context automatically.
899
+ Workspace read Tokens cannot use these private operations. Current Role, eligibility,
900
+ credential and lifecycle rules remain enforced by the existing service endpoints.
901
+
902
+ ## Shared task views, statistics, colors and personal tasks (ZTA-875/876/878)
903
+
904
+ ```sh
905
+ specflow work-item view list --project-id PROJECT
906
+ specflow work-item view save --project-id PROJECT --input '{"id":"UUID","name":"Mine","layout":"list","groupedByStatus":false,"filters":{"assignees":[],"customFields":{},"dueDates":[],"labels":[],"priorities":[],"search":"","sortBy":"number","sortOrder":"asc","statuses":[]}}'
907
+ specflow work-item view save --project-id PROJECT --input '<complete view JSON without version>' --expected-version '"1"'
908
+ specflow work-item statistics --project-id PROJECT --query '{"view":"list","assignee":"USER_ID","priority":"high,urgent"}'
909
+ specflow workspace my-work-items --workspace-id WORKSPACE --limit 50 [--cursor CURSOR]
910
+ specflow work-item-custom-field create --project-id PROJECT --name Risk --type dropdown --option High --option Low --option-colors '["danger",null]'
911
+ specflow work-item-custom-field option-colors --project-id PROJECT --field-id FIELD --colors '["warning",null]' --expected-version '"1"' --idempotency-key UUID
912
+ specflow work-item-custom-field option-colors --project-id PROJECT --field-id FIELD --clear --expected-version '"2"' --idempotency-key UUID
913
+ ```
914
+
915
+ Views retain the existing board/list layout and complete filters, including optional
916
+ `repositoryConnectionId`. Save omits the expected version only for a new ID.
917
+ Identical immediate retries use the same ID, content and observed version; the
918
+ server owns replay and stale-version behavior. No view Idempotency-Key is invented.
919
+ Statistics returns the server aggregate, including trends at `overview.timeline`;
920
+ there is no client page counting. Query accepts existing server selection keys:
921
+ `view`, `repositoryConnectionId`, `assignee`, `customFields`, `due`, `dueAfter`,
922
+ `dueBefore`, `label`, `priority`, `search`, `sortBy`, `sortOrder`, `startAfter`,
923
+ `startBefore`, `status`. CSV and customFields JSON strings retain server semantics.
924
+ Personal tasks returns `total`, Project `groups` and `nextCursor` from the current
925
+ User's authorized unfinished tasks in Active Projects; retain the server cursor.
926
+ Colors align one-to-one with dropdown options; null entries clear individual colors,
927
+ `--clear` clears all. The server validates color values, lengths and field type.
928
+ Assistant can discover views, statistics/trends, option colors and my unfinished
929
+ Work Items. View and color writes wait for approval and retain version preconditions.
930
+
931
+ ## Local Screen image files (ZTA-881 CLI)
932
+
933
+ ```sh
934
+ specflow map screen image-upload --workspace-id WORKSPACE --screen-id SCREEN --file ./image.png --content-type image/png --expected-version '"1"' --idempotency-key RESERVE_KEY --finalize-key FINALIZE_KEY
935
+ specflow map screen image-download --workspace-id WORKSPACE --screen-id SCREEN --asset-id ASSET_UUID --output-file ./download.png
936
+ ```
937
+
938
+ Upload reserves against the observed Screen version, validates the returned fresh
939
+ HTTP(S) reservation, PUTs bytes without Session credentials or redirects, and
940
+ finalizes using a separate retained key and the same version. Local files must be
941
+ nonempty regular files of at most 10 MiB; server MIME/size checks still apply.
942
+ Retain both phase keys and identical inputs after failures. Once finalized, a new
943
+ reservation attempt with the old reserve key returns upload-expired; reread the
944
+ Screen to resolve an ambiguous completion. Existing image-reserve/image-finalize/
945
+ image-remove commands remain available for explicit phase control.
946
+ Download accepts only authorized Workspace/Screen/asset identities, never an
947
+ arbitrary URL, and creates a new owner-only file without overwriting existing files.
948
+ Assistant image handling is outside this CLI slice.
949
+
950
+ Global search uses the server's cross-resource search and current authority:
951
+
952
+ ```sh
953
+ specflow search-global --query 'PAY-12' --limit 20
954
+ specflow search-global --query 'payment' --limit 20 --cursor '<nextCursor>'
955
+ ```
956
+
957
+ The JSON response preserves object kind, ID, owning Workspace/Project, totalCount
958
+ and nextCursor. Continue with the same query until nextCursor is null. The server
959
+ has no type filter. `specflow spec search` retains its published-Spec semantics.
package/dist/cli.js CHANGED
@@ -3,13 +3,18 @@ import { randomUUID } from "node:crypto";
3
3
  import createClient from "openapi-fetch";
4
4
  import { z } from "zod";
5
5
  import { addAuthenticationCommandTopology } from "./authentication-command-topology.js";
6
+ import { registerCustomFieldColorCommands, parseOptionColors } from "./custom-field-color-commands.js";
6
7
  import { registerGitHubAccountCommands } from "./github-account-commands.js";
7
8
  import { parseHttpOrigin, unresolvedSpecFlowApiOrigin, } from "./http-origin.js";
8
9
  import { registerProductMapCommands } from "./product-map-commands.js";
10
+ import { registerProjectAdministrationCommands } from "./project-administration-commands.js";
11
+ import { registerSpecDiscussionCommands } from "./spec-discussion-commands.js";
12
+ import { registerSpecOwnerCommands } from "./spec-owner-commands.js";
9
13
  import { specFlowCliUserAgent } from "./user-agent.js";
10
14
  import { registerUserApiKeyCommands } from "./user-api-key-commands.js";
11
15
  import { registerWorkItemAssetCommands } from "./work-item-asset-commands.js";
12
16
  import { readWorkItemImportFile } from "./work-item-exchange-file.js";
17
+ import { registerWorkItemQueryCommands } from "./work-item-query-commands.js";
13
18
  import { registerWorkItemResourceLinkCommands } from "./work-item-resource-link-commands.js";
14
19
  import { parseTimeEntryTimestamp, parseTimeEntryUuid, parseTimeEntryDescription } from "./work-item-time-entry-input.js";
15
20
  export function formatResourceDiagnostic(message) {
@@ -1267,7 +1272,22 @@ export async function runCli(arguments_, dependencies) {
1267
1272
  });
1268
1273
  const workItem = program.command("work-item");
1269
1274
  registerWorkItemAssetCommands(workItem, { dependencies, run, setExitCode: (value) => { exitCode = value; }, rejectWrite: () => rejectTokenWrite(dependencies) });
1270
- registerProductMapCommands(program, { run, setExitCode: value => { exitCode = value; }, canWrite: () => !hasWorkspaceReadToken(dependencies.environment), rejectWrite: () => rejectTokenWrite(dependencies), parseOpaqueVersion });
1275
+ const resourceRuntime = { run, setExitCode: (value) => { exitCode = value; }, parseOpaqueVersion, parseLimit: parseSearchLimit };
1276
+ registerSpecOwnerCommands(spec, resourceRuntime);
1277
+ registerSpecDiscussionCommands(spec, resourceRuntime);
1278
+ registerProjectAdministrationCommands(project, workspace, resourceRuntime);
1279
+ program.command("search-global").description("Search all currently accessible objects; preserves server totals and cursor")
1280
+ .requiredOption("--query <query>")
1281
+ .option("--types <types>", "Comma-separated kinds: project,work-item,capability,spec,glossary,screen,journey,user")
1282
+ .option("--limit <limit>", "Server page size (1–100)", resourceRuntime.parseLimit, 20)
1283
+ .option("--cursor <cursor>")
1284
+ .action(async (options) => {
1285
+ resourceRuntime.setExitCode(await resourceRuntime.run(client => client.GET("/v1/search", {
1286
+ params: { query: { query: options.query, limit: options.limit, ...(options.cursor === undefined ? {} : { cursor: options.cursor }), ...(options.types === undefined ? {} : { types: options.types }) } },
1287
+ }), { rejectWorkspaceReadToken: true }));
1288
+ });
1289
+ registerWorkItemQueryCommands(workItem, workspace, resourceRuntime);
1290
+ registerProductMapCommands(program, { dependencies, run, setExitCode: value => { exitCode = value; }, canWrite: () => !hasWorkspaceReadToken(dependencies.environment), rejectWrite: () => rejectTokenWrite(dependencies), parseOpaqueVersion });
1271
1291
  registerWorkItemResourceLinkCommands(workItem, spec, { run, setExitCode: (value) => { exitCode = value; }, canWrite: () => !hasWorkspaceReadToken(dependencies.environment), rejectWrite: () => rejectTokenWrite(dependencies), parseOpaqueVersion, parseLimit: parseSearchLimit });
1272
1292
  const timeEntry = workItem.command("time-entry").description("Record and read task time entries");
1273
1293
  timeEntry.command("list")
@@ -2164,6 +2184,7 @@ export async function runCli(arguments_, dependencies) {
2164
2184
  });
2165
2185
  });
2166
2186
  const workItemCustomField = program.command("work-item-custom-field");
2187
+ registerCustomFieldColorCommands(workItemCustomField, resourceRuntime);
2167
2188
  workItemCustomField
2168
2189
  .command("list")
2169
2190
  .requiredOption("--project-id <projectId>")
@@ -2180,6 +2201,7 @@ export async function runCli(arguments_, dependencies) {
2180
2201
  .option("--option <option>", "Dropdown option; repeat for every choice", collectOption, [])
2181
2202
  .option("--default-value <defaultValue>")
2182
2203
  .option("--required", "Reject Work Items that leave the field blank")
2204
+ .option("--option-colors <json>", "One color or null per dropdown option", parseOptionColors)
2183
2205
  .action(async (options) => {
2184
2206
  if (hasWorkspaceReadToken(dependencies.environment)) {
2185
2207
  exitCode = rejectTokenWrite(dependencies);
@@ -2192,6 +2214,7 @@ export async function runCli(arguments_, dependencies) {
2192
2214
  ? {}
2193
2215
  : { defaultValue: options.defaultValue }),
2194
2216
  name: options.name,
2217
+ ...(options.optionColors === undefined ? {} : { optionColors: options.optionColors }),
2195
2218
  ...(options.option.length === 0
2196
2219
  ? {}
2197
2220
  : { options: options.option }),