@ciphrix/cli 0.1.0 → 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/README.md CHANGED
@@ -72,8 +72,8 @@ changes show the proposed action and ask for confirmation; automation can opt in
72
72
  - **Manage operational registers** — work with assets, vendors, risks, treatments, ownership, and files.
73
73
  - **Operate monitoring** — inspect checks, resources, runs, findings, integrations, and background jobs.
74
74
 
75
- Names and stable Ciphrix codes are accepted throughout, so neither people nor agents need to work with
76
- opaque database identifiers.
75
+ Established record commands accept names and stable Ciphrix codes where advertised. Access inventory,
76
+ review tasks and proposals require the exact IDs returned by their read commands; never guess identifiers.
77
77
 
78
78
  ## Built for agent workspaces
79
79
 
@@ -99,7 +99,9 @@ The skill works with compatible agent harnesses including Codex, Claude Code, Cu
99
99
  is also included in the npm package at [`skills/ciphrix/SKILL.md`](skills/ciphrix/SKILL.md). Installing
100
100
  the npm package never modifies an agent's configuration directories.
101
101
 
102
- Run `npx skills update` to update installed skills when a newer version is available.
102
+ Run `npx skills update` to update installed skills when a newer version is available. Keep the skill
103
+ aligned with your installed CLI version; its generated command reference and local help describe the
104
+ available grammar. Server capability discovery remains authoritative for which operations you can use.
103
105
 
104
106
  ### Ask in natural language, operate through the CLI
105
107
 
@@ -155,6 +157,47 @@ one command. Installed help is authoritative. The complete generated reference i
155
157
 
156
158
  </details>
157
159
 
160
+ ### Context, reviews and agent-assisted work
161
+
162
+ The onboarding group reads separate Business and Operating Context, saves agreed questionnaire answers,
163
+ and offers explicit optional setup and completion. Business Context readiness is decided by the server;
164
+ Operating Context and optional setup do not block journey completion. Saving context never automatically
165
+ starts agents or completes the journey. Local `--help` works offline; `onboarding --help-onboarding`
166
+ returns authenticated server guidance and the current status pack.
167
+
168
+ The access group separates inventory, reviewed import proposals, assigned reviewer decisions and campaign
169
+ results. A revoke decision records a removal request, not provider-side revocation. Recording decisions,
170
+ completing a reviewer task, and ending a campaign are separate operations. Imports merge only explicitly
171
+ selected reviewed rows; inspect per-row failures even when some rows applied.
172
+
173
+ Agent findings and advisory test/control assessments do not make formal human decisions. Assigned-task
174
+ discovery does not complete work. File staging and processing are explicit, separate operations; processing
175
+ may update context or create vendors. Evidence/CSV commands return expiring signed references without
176
+ automatically downloading files. Use each command's help for the exact inputs, next reads and effects.
177
+
178
+ ### Manifest compatibility, JSON and retries
179
+
180
+ The CLI discovers allowed tools and their effects from the authenticated server manifest. New command
181
+ groups require server-declared command bindings. An unavailable binding is reported as unsupported;
182
+ the CLI never guesses a different endpoint or interprets the manifest as authorization. Existing
183
+ commands keep their successful JSON formats. New capability commands emit complete success/error
184
+ envelopes with `--json`, preserving warnings, pagination, truncation and structured error details.
185
+ Failures and partially applied mutations return a nonzero exit status.
186
+
187
+ Confirmed writes require explicit consent. New commands in JSON/non-interactive mode require `--yes`
188
+ for confirmed operations; immediate writes do not accept that flag. Write controls are rejected for
189
+ reads. The optional `--idempotency-key` names one logical write: reuse identical input for a retry,
190
+ never a new key to bypass an unknown outcome. There are no automatic write retries. Confirmed replay
191
+ also needs a valid confirmation token; a fresh invocation re-preflights and may be blocked by changed
192
+ state or expiry. The CLI does not save confirmation tokens or promise recovery across invocations.
193
+ Inspect current records/jobs when uncertain. Replayed status packs and signed URLs are original
194
+ snapshots, not refreshed state or renewed downloads.
195
+
196
+ Mutation JSON files must be regular files, at most 1 MiB, containing an explicitly selected array.
197
+ Use clear flags or JSON null where supported, not the string `"null"`. Do not infer confirmations
198
+ from suggested matches. New repeated flags collect separate values; existing command conventions remain
199
+ compatible. See the generated command reference for all options and nested payload details.
200
+
158
201
  ## Safe by default
159
202
 
160
203
  - Credentials use the operating system keychain by default and are isolated by API environment.
@@ -164,7 +207,7 @@ one command. Installed help is authoritative. The complete generated reference i
164
207
  - Destructive and consequential actions require confirmation unless `--yes` is explicitly supplied.
165
208
  - JSON output is undecorated and suitable for programs and agents.
166
209
  - Requests time out, responses are size-limited, and local evidence uploads reject symbolic links and
167
- files over 50 MiB.
210
+ files over 25 MiB.
168
211
 
169
212
  Please report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
170
213
 
package/dist/actions.d.ts CHANGED
@@ -8,6 +8,8 @@ export interface ApplyOptions {
8
8
  fallback: string;
9
9
  yes?: boolean | undefined;
10
10
  confirm?: ((question: string) => Promise<boolean>) | undefined;
11
+ idempotencyKey?: string | undefined;
12
+ quiet?: boolean | undefined;
11
13
  }
12
14
  /**
13
15
  * Runs a tool, handling the two-step confirm contract when the tool requires it.
@@ -16,4 +18,4 @@ export interface ApplyOptions {
16
18
  * - Tools that require confirmation: the first call is a preflight (summary + token), the user is asked
17
19
  * once, then the change is applied with the token and an idempotency key.
18
20
  */
19
- export declare const applyTool: ({ ctx, io, toolName, input, fallback, yes, confirm, }: ApplyOptions) => Promise<Record<string, unknown> | null>;
21
+ export declare const applyTool: ({ ctx, io, toolName, input, fallback, yes, confirm, idempotencyKey: suppliedKey, quiet, }: ApplyOptions) => Promise<Record<string, unknown> | null>;
package/dist/actions.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { promptConfirm, writeLine } from './io.js';
3
+ import { ApiError } from './api.js';
4
+ import { getTool } from './manifest.js';
3
5
  import { asRecord, asText, callTool, errorMessage } from './toolSurface.js';
4
6
  /**
5
7
  * Runs a tool, handling the two-step confirm contract when the tool requires it.
@@ -8,23 +10,30 @@ import { asRecord, asText, callTool, errorMessage } from './toolSurface.js';
8
10
  * - Tools that require confirmation: the first call is a preflight (summary + token), the user is asked
9
11
  * once, then the change is applied with the token and an idempotency key.
10
12
  */
11
- export const applyTool = async ({ ctx, io, toolName, input, fallback, yes = false, confirm, }) => {
13
+ export const applyTool = async ({ ctx, io, toolName, input, fallback, yes = false, confirm, idempotencyKey: suppliedKey, quiet = false, }) => {
12
14
  const ask = confirm ?? ((question) => promptConfirm(io, question));
13
- const idempotencyKey = randomUUID();
15
+ const idempotencyKey = suppliedKey ?? randomUUID();
16
+ const tool = await getTool(ctx, toolName);
17
+ if (tool.effect !== 'write')
18
+ throw new Error('Expected a write operation.');
14
19
  const first = await callTool(ctx, toolName, input, { idempotencyKey });
15
20
  if (first.status !== 'ok')
16
- throw new Error(errorMessage(first, fallback));
21
+ throw new ApiError(errorMessage(first, fallback), 400, asText(asRecord(first.error).code), asRecord(first.error).details, first);
17
22
  const confirmation = asRecord(asRecord(first.meta).confirmation);
18
23
  const token = asText(confirmation.token);
19
- if (!token)
24
+ if (tool.confirmation === 'none')
20
25
  return first;
21
- writeLine(io.stdout, asText(confirmation.summary, 'Apply this change?'));
26
+ if (!token)
27
+ throw new Error('The server did not return the required confirmation token. No change was applied.');
28
+ if (!quiet)
29
+ writeLine(io.stdout, asText(confirmation.summary, 'Apply this change?'));
22
30
  if (!yes && !(await ask('Apply this change?'))) {
23
- writeLine(io.stdout, 'Cancelled.');
31
+ if (!quiet)
32
+ writeLine(io.stdout, 'Cancelled.');
24
33
  return null;
25
34
  }
26
35
  const result = await callTool(ctx, toolName, input, { confirmationToken: token, idempotencyKey });
27
36
  if (result.status !== 'ok')
28
- throw new Error(errorMessage(result, fallback));
37
+ throw new ApiError(errorMessage(result, fallback), 400, asText(asRecord(result.error).code), asRecord(result.error).details, result);
29
38
  return result;
30
39
  };
package/dist/api.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export declare class ApiError extends Error {
2
2
  readonly status: number;
3
3
  readonly code: string | undefined;
4
- constructor(message: string, status: number, code?: string | undefined);
4
+ readonly details: unknown;
5
+ readonly envelope: unknown;
6
+ constructor(message: string, status: number, code?: string | undefined, details?: unknown, envelope?: unknown);
5
7
  }
6
8
  export interface RequestOptions {
7
9
  method?: string;
package/dist/api.js CHANGED
@@ -1,11 +1,15 @@
1
1
  export class ApiError extends Error {
2
2
  status;
3
3
  code;
4
- constructor(message, status, code = undefined) {
4
+ details;
5
+ envelope;
6
+ constructor(message, status, code = undefined, details = undefined, envelope = undefined) {
5
7
  super(message);
6
8
  this.name = 'ApiError';
7
9
  this.status = status;
8
10
  this.code = code;
11
+ this.details = details;
12
+ this.envelope = envelope;
9
13
  }
10
14
  }
11
15
  /** Default time allowed for an API request, including reading its response body. */
@@ -104,7 +108,7 @@ const errorPayload = (data) => {
104
108
  : typeof nested?.code === 'string'
105
109
  ? nested.code
106
110
  : undefined;
107
- const details = Array.isArray(nested?.details) ? nested?.details : [];
111
+ const details = nested?.details;
108
112
  return { message, code, details };
109
113
  };
110
114
  /**
@@ -172,7 +176,7 @@ export const createApiClient = (baseUrl, limits = {}) => ({
172
176
  }
173
177
  if (!response.ok) {
174
178
  const { message, code, details } = errorPayload(data);
175
- throw new ApiError(withDetails(message || `Request failed with status ${response.status}`, code, details), response.status, code);
179
+ throw new ApiError(withDetails(message || `Request failed with status ${response.status}`, code, Array.isArray(details) ? details : []), response.status, code, details, data);
176
180
  }
177
181
  return data;
178
182
  });
package/dist/cli.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Command, CommanderError } from 'commander';
2
+ import { registerCapabilities, RenderedCommandError } from './commands/capabilities.js';
3
+ import { asRecord } from './toolSurface.js';
2
4
  import { formatBanner } from './banner.js';
3
5
  import { loginCommand } from './commands/login.js';
4
6
  import { logoutCommand } from './commands/logout.js';
@@ -247,7 +249,9 @@ export const createProgram = (io = createDefaultIO()) => {
247
249
  json: readBooleanOption(options, 'json'),
248
250
  });
249
251
  });
250
- const testRun = test.command('run').description('Create, result and delete runs');
252
+ const testRun = test
253
+ .command('run')
254
+ .description('Run lifecycle, human results/notes, linked evidence references and advisory assessments');
251
255
  testRun
252
256
  .command('create <name>')
253
257
  .description('Create a new pending run')
@@ -266,17 +270,21 @@ export const createProgram = (io = createDefaultIO()) => {
266
270
  testRun
267
271
  .command('result <name>')
268
272
  .description('Record a run result: passing | failing | skipped | pending (reopen)')
269
- .requiredOption('--status <status>', 'passing | failing | skipped | pending')
273
+ .option('--status <status>', 'human-decided result: passing | failing | skipped | pending; omit for note-only changes')
270
274
  .option('--note <text>', 'note for the run')
275
+ .option('--clear-note', 'clear the note without changing the result; conflicts with --note')
271
276
  .option('--run <id>', 'a specific run')
272
277
  .option('--api-url <url>', 'override the API base URL for this command')
273
278
  .option('--json', 'output raw JSON')
274
279
  .action(async (name, options) => {
280
+ if (readBooleanOption(options, 'clearNote') &&
281
+ readStringOption(options, 'note') !== undefined)
282
+ throw new Error('--clear-note conflicts with --note.');
275
283
  await testRunResultCommand({
276
284
  io,
277
285
  name,
278
- status: readStringOption(options, 'status') ?? '',
279
- note: readStringOption(options, 'note'),
286
+ status: readStringOption(options, 'status'),
287
+ note: readBooleanOption(options, 'clearNote') ? null : readStringOption(options, 'note'),
280
288
  runId: readStringOption(options, 'run'),
281
289
  apiUrl: readStringOption(options, 'apiUrl'),
282
290
  json: readBooleanOption(options, 'json'),
@@ -305,6 +313,8 @@ export const createProgram = (io = createDefaultIO()) => {
305
313
  .requiredOption('--type <type>', 'item type: native_doc or check')
306
314
  .option('--run <id>', 'a specific run')
307
315
  .option('--search <text>', 'filter by item name')
316
+ .option('--page <n>', 'page number')
317
+ .option('--limit <n>', 'page size')
308
318
  .option('--api-url <url>', 'override the API base URL for this command')
309
319
  .option('--json', 'output raw JSON')
310
320
  .action(async (name, options) => {
@@ -314,6 +324,8 @@ export const createProgram = (io = createDefaultIO()) => {
314
324
  runId: readStringOption(options, 'run'),
315
325
  itemType: readStringOption(options, 'type') ?? 'native_doc',
316
326
  search: readStringOption(options, 'search'),
327
+ page: readNumberOption(options, 'page'),
328
+ limit: readNumberOption(options, 'limit'),
317
329
  apiUrl: readStringOption(options, 'apiUrl'),
318
330
  json: readBooleanOption(options, 'json'),
319
331
  });
@@ -454,6 +466,8 @@ export const createProgram = (io = createDefaultIO()) => {
454
466
  .description('List items that can be mapped to a clause')
455
467
  .requiredOption('--type <type>', 'item type: test or document')
456
468
  .option('--search <text>', 'filter by item name')
469
+ .option('--page <n>', 'page number')
470
+ .option('--limit <n>', 'page size')
457
471
  .option('--api-url <url>', 'override the API base URL for this command')
458
472
  .option('--json', 'output raw JSON')
459
473
  .action(async (frameworkName, clause, options) => {
@@ -463,6 +477,8 @@ export const createProgram = (io = createDefaultIO()) => {
463
477
  clause,
464
478
  itemType: readStringOption(options, 'type') ?? 'test',
465
479
  search: readStringOption(options, 'search'),
480
+ page: readNumberOption(options, 'page'),
481
+ limit: readNumberOption(options, 'limit'),
466
482
  apiUrl: readStringOption(options, 'apiUrl'),
467
483
  json: readBooleanOption(options, 'json'),
468
484
  });
@@ -523,10 +539,13 @@ export const createProgram = (io = createDefaultIO()) => {
523
539
  .command('list')
524
540
  .description('List assets with their code, status and classification')
525
541
  .option('--name <text>', 'filter by name')
526
- .option('--category <value>', 'filter by category')
542
+ .option('--category <csv>', 'filter by category')
543
+ .option('--sub-category <csv>', 'filter by sub-category')
527
544
  .option('--status <csv>', 'filter by status')
528
- .option('--business-impact <value>', 'filter by business impact')
529
- .option('--data-classification <value>', 'filter by data classification')
545
+ .option('--business-impact <csv>', 'filter by business impact')
546
+ .option('--data-classification <csv>', 'filter by data classification')
547
+ .option('--source <csv>', 'filter by discovery source (provider, e.g. aws, azure, gcp)')
548
+ .option('--technical-owner-email <email>', 'filter by technical owner email')
530
549
  .option('--page <n>', 'page number')
531
550
  .option('--limit <n>', 'page size')
532
551
  .option('--api-url <url>', 'override the API base URL for this command')
@@ -536,9 +555,12 @@ export const createProgram = (io = createDefaultIO()) => {
536
555
  io,
537
556
  name: readStringOption(options, 'name'),
538
557
  category: readStringOption(options, 'category'),
558
+ subCategory: readStringOption(options, 'subCategory'),
539
559
  status: readStringOption(options, 'status'),
540
560
  businessImpact: readStringOption(options, 'businessImpact'),
541
561
  dataClassification: readStringOption(options, 'dataClassification'),
562
+ source: readStringOption(options, 'source'),
563
+ technicalOwnerEmail: readStringOption(options, 'technicalOwnerEmail'),
542
564
  page: readNumberOption(options, 'page'),
543
565
  limit: readNumberOption(options, 'limit'),
544
566
  apiUrl: readStringOption(options, 'apiUrl'),
@@ -756,7 +778,10 @@ export const createProgram = (io = createDefaultIO()) => {
756
778
  .description('Create a vendor')
757
779
  .requiredOption('--name <name>', 'vendor name')
758
780
  .option('--category <value>', 'category')
781
+ .option('--subcategory <value>', 'sub-category')
759
782
  .option('--criticality <value>', 'criticality')
783
+ .option('--data-sensitivity <value>', 'data sensitivity')
784
+ .option('--service-availability <value>', 'service availability')
760
785
  .option('--description <text>', 'description')
761
786
  .option('--api-url <url>', 'override the API base URL for this command')
762
787
  .option('--json', 'output raw JSON')
@@ -765,7 +790,10 @@ export const createProgram = (io = createDefaultIO()) => {
765
790
  io,
766
791
  name: readStringOption(options, 'name') ?? '',
767
792
  category: readStringOption(options, 'category'),
793
+ subcategory: readStringOption(options, 'subcategory'),
768
794
  criticality: readStringOption(options, 'criticality'),
795
+ dataSensitivity: readStringOption(options, 'dataSensitivity'),
796
+ serviceAvailability: readStringOption(options, 'serviceAvailability'),
769
797
  description: readStringOption(options, 'description'),
770
798
  apiUrl: readStringOption(options, 'apiUrl'),
771
799
  json: readBooleanOption(options, 'json'),
@@ -828,14 +856,16 @@ export const createProgram = (io = createDefaultIO()) => {
828
856
  });
829
857
  const control = program
830
858
  .command('control')
831
- .description('Controls: status, applicability, owner and design requirements');
859
+ .description('Controls: status, applicability, ownership, design requirements and advisory assessments');
832
860
  control
833
861
  .command('list')
834
862
  .description('List controls with their status, applicability and owner')
863
+ .option('--framework <code>', 'filter by catalogue framework code; repeat for multiple values', (value, previous = []) => [...previous, value])
835
864
  .option('--search <text>', 'filter by name or code')
836
865
  .option('--domain <value>', 'filter by control domain')
837
866
  .option('--status <csv>', 'filter by status')
838
867
  .option('--applicability <value>', 'filter by applicability (in_scope|out_of_scope)')
868
+ .option('--owner <csv>', 'filter by owner user id')
839
869
  .option('--page <n>', 'page number')
840
870
  .option('--limit <n>', 'page size')
841
871
  .option('--api-url <url>', 'override the API base URL for this command')
@@ -843,10 +873,14 @@ export const createProgram = (io = createDefaultIO()) => {
843
873
  .action(async (options) => {
844
874
  await controlListCommand({
845
875
  io,
876
+ framework: Array.isArray(asRecord(options).framework)
877
+ ? asRecord(options).framework
878
+ : undefined,
846
879
  search: readStringOption(options, 'search'),
847
880
  domain: readStringOption(options, 'domain'),
848
881
  status: readStringOption(options, 'status'),
849
882
  applicability: readStringOption(options, 'applicability'),
883
+ ownerId: readStringOption(options, 'owner'),
850
884
  page: readNumberOption(options, 'page'),
851
885
  limit: readNumberOption(options, 'limit'),
852
886
  apiUrl: readStringOption(options, 'apiUrl'),
@@ -914,6 +948,7 @@ export const createProgram = (io = createDefaultIO()) => {
914
948
  .option('--justification <text>', 'why the applicability changed')
915
949
  .option('--design-requirements <state>', 'not_assessed | not_needed | not_met | partial | met')
916
950
  .option('--notes <text>', 'notes (Markdown)')
951
+ .option('--description <text>', 'description (custom controls only)')
917
952
  .option('--rename <text>', 'new name (custom controls only)')
918
953
  .option('--yes', 'apply without prompting')
919
954
  .option('--api-url <url>', 'override the API base URL for this command')
@@ -928,6 +963,7 @@ export const createProgram = (io = createDefaultIO()) => {
928
963
  ['justification', 'justification'],
929
964
  ['designRequirements', 'designRequirements'],
930
965
  ['notes', 'notes'],
966
+ ['description', 'description'],
931
967
  ['rename', 'name'],
932
968
  ]) {
933
969
  const value = readStringOption(options, flag);
@@ -1543,6 +1579,7 @@ export const createProgram = (io = createDefaultIO()) => {
1543
1579
  json: readBooleanOption(options, 'json'),
1544
1580
  });
1545
1581
  });
1582
+ registerCapabilities(program, io);
1546
1583
  // A bare `ciphrix` shows the banner and help; commands never print the banner.
1547
1584
  program.action(() => {
1548
1585
  writeBlock(io.stdout, `${formatBanner(io.theme)}\n\n`);
@@ -1566,6 +1603,8 @@ export const main = async (argv = process.argv, io = createDefaultIO()) => {
1566
1603
  return 0;
1567
1604
  }
1568
1605
  catch (error) {
1606
+ if (error instanceof RenderedCommandError)
1607
+ return 1;
1569
1608
  if (error instanceof CommanderError) {
1570
1609
  return error.exitCode;
1571
1610
  }
@@ -6,12 +6,15 @@ export interface AssetCommonOptions {
6
6
  json?: boolean | undefined;
7
7
  credentialStore?: CredentialStore | undefined;
8
8
  }
9
- export declare const assetListCommand: ({ io, apiUrl, json, credentialStore, name, category, status, businessImpact, dataClassification, page, limit, }: AssetCommonOptions & {
9
+ export declare const assetListCommand: ({ io, apiUrl, json, credentialStore, name, category, subCategory, status, businessImpact, dataClassification, source, technicalOwnerEmail, page, limit, }: AssetCommonOptions & {
10
10
  name?: string | undefined;
11
11
  category?: string | undefined;
12
+ subCategory?: string | undefined;
12
13
  status?: string | undefined;
13
14
  businessImpact?: string | undefined;
14
15
  dataClassification?: string | undefined;
16
+ source?: string | undefined;
17
+ technicalOwnerEmail?: string | undefined;
15
18
  page?: number | undefined;
16
19
  limit?: number | undefined;
17
20
  }) => Promise<void>;
@@ -2,22 +2,29 @@ import { applyTool } from '../actions.js';
2
2
  import { writeLine, writeNextHint, writePageFooter, writeTable, clip } from '../io.js';
3
3
  import { asRecord, asText, callTool, errorMessage, resolveToolContext } from '../toolSurface.js';
4
4
  const text = (value, fallback = '-') => typeof value === 'string' ? value : typeof value === 'number' ? String(value) : fallback;
5
- export const assetListCommand = async ({ io, apiUrl, json = false, credentialStore, name, category, status, businessImpact, dataClassification, page, limit, }) => {
5
+ export const assetListCommand = async ({ io, apiUrl, json = false, credentialStore, name, category, subCategory, status, businessImpact, dataClassification, source, technicalOwnerEmail, page, limit, }) => {
6
6
  const ctx = await resolveToolContext(apiUrl, credentialStore);
7
+ const csv = (value) => value
8
+ .split(',')
9
+ .map((entry) => entry.trim())
10
+ .filter(Boolean);
7
11
  const input = {};
8
12
  if (name)
9
13
  input.name = name;
10
14
  if (category)
11
- input.category = [category];
15
+ input.category = csv(category);
16
+ if (subCategory)
17
+ input.subCategory = csv(subCategory);
12
18
  if (status)
13
- input.status = status
14
- .split(',')
15
- .map((value) => value.trim())
16
- .filter(Boolean);
19
+ input.status = csv(status);
17
20
  if (businessImpact)
18
- input.businessImpact = [businessImpact];
21
+ input.businessImpact = csv(businessImpact);
19
22
  if (dataClassification)
20
- input.dataClassification = [dataClassification];
23
+ input.dataClassification = csv(dataClassification);
24
+ if (source)
25
+ input.source = csv(source);
26
+ if (technicalOwnerEmail)
27
+ input.technicalOwnerEmail = technicalOwnerEmail;
21
28
  if (page)
22
29
  input.page = page;
23
30
  if (limit)
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+ import { type CliIO } from '../io.js';
3
+ import { type ToolContext } from '../toolSurface.js';
4
+ export declare class RenderedCommandError extends Error {
5
+ }
6
+ export declare const registerCapabilities: (program: Command, io: CliIO, context?: (apiUrl?: string) => Promise<ToolContext>) => void;