@sanity/workflow-cli 0.9.0 → 0.11.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 (71) hide show
  1. package/CHANGELOG.md +516 -0
  2. package/README.md +71 -19
  3. package/bin/run.js +0 -4
  4. package/dist/commands/abort.d.ts +4 -6
  5. package/dist/commands/abort.js +13 -24
  6. package/dist/commands/definition/delete.d.ts +2 -2
  7. package/dist/commands/definition/delete.js +14 -27
  8. package/dist/commands/definition/diff.d.ts +2 -2
  9. package/dist/commands/definition/diff.js +3 -12
  10. package/dist/commands/definition/list.d.ts +3 -4
  11. package/dist/commands/definition/list.js +41 -47
  12. package/dist/commands/definition/show.d.ts +35 -3
  13. package/dist/commands/definition/show.js +44 -24
  14. package/dist/commands/deploy.d.ts +11 -5
  15. package/dist/commands/deploy.js +54 -57
  16. package/dist/commands/diagnose.d.ts +22 -4
  17. package/dist/commands/diagnose.js +60 -56
  18. package/dist/commands/fire-action.d.ts +2 -4
  19. package/dist/commands/fire-action.js +39 -62
  20. package/dist/commands/list.d.ts +18 -10
  21. package/dist/commands/list.js +77 -69
  22. package/dist/commands/reset-activity.js +0 -1
  23. package/dist/commands/set-stage.d.ts +2 -2
  24. package/dist/commands/set-stage.js +14 -30
  25. package/dist/commands/show.d.ts +2 -2
  26. package/dist/commands/show.js +12 -24
  27. package/dist/commands/start.d.ts +4 -28
  28. package/dist/commands/start.js +19 -80
  29. package/dist/commands/tail.d.ts +2 -2
  30. package/dist/commands/tail.js +34 -36
  31. package/dist/hooks/finally/telemetry.d.ts +8 -0
  32. package/dist/hooks/finally/telemetry.js +13 -0
  33. package/dist/hooks/prerun/telemetry.d.ts +8 -0
  34. package/dist/hooks/prerun/telemetry.js +9 -0
  35. package/dist/index.js +0 -3
  36. package/dist/lib/base-command.d.ts +14 -0
  37. package/dist/lib/base-command.js +10 -0
  38. package/dist/lib/client.js +13 -30
  39. package/dist/lib/context.d.ts +49 -13
  40. package/dist/lib/context.js +49 -48
  41. package/dist/lib/definitions.d.ts +10 -0
  42. package/dist/lib/definitions.js +11 -24
  43. package/dist/lib/diff.d.ts +5 -2
  44. package/dist/lib/diff.js +61 -27
  45. package/dist/lib/env.d.ts +4 -0
  46. package/dist/lib/env.js +4 -3
  47. package/dist/lib/fail.d.ts +19 -6
  48. package/dist/lib/fail.js +26 -26
  49. package/dist/lib/flags.d.ts +0 -7
  50. package/dist/lib/flags.js +0 -17
  51. package/dist/lib/load-config.d.ts +11 -7
  52. package/dist/lib/load-config.js +40 -20
  53. package/dist/lib/operation-args.d.ts +14 -23
  54. package/dist/lib/operation-args.js +6 -38
  55. package/dist/lib/ops-report.d.ts +18 -10
  56. package/dist/lib/ops-report.js +20 -16
  57. package/dist/lib/params.js +0 -8
  58. package/dist/lib/read-fanout.d.ts +22 -0
  59. package/dist/lib/read-fanout.js +28 -0
  60. package/dist/lib/select-deployment.js +0 -21
  61. package/dist/lib/share-definitions.d.ts +120 -0
  62. package/dist/lib/share-definitions.js +187 -0
  63. package/dist/lib/stub.js +0 -7
  64. package/dist/lib/telemetry-setup.d.ts +70 -0
  65. package/dist/lib/telemetry-setup.js +99 -0
  66. package/dist/lib/telemetry.d.ts +131 -0
  67. package/dist/lib/telemetry.js +94 -0
  68. package/dist/lib/ui.d.ts +33 -1
  69. package/dist/lib/ui.js +32 -22
  70. package/oclif.manifest.json +21 -52
  71. package/package.json +14 -8
@@ -1,12 +1,14 @@
1
1
  import { styleText } from 'node:util';
2
- import { Args, Command, Flags } from '@oclif/core';
2
+ import { Args, Flags } from '@oclif/core';
3
3
  import { isTerminalStage, } from '@sanity/workflow-engine';
4
4
  import logSymbols from 'log-symbols';
5
- import { resolveReadContext } from "../../lib/context.js";
6
- import { buildDefinitionShowQuery } from "../../lib/definitions.js";
5
+ import { WorkflowCommand } from "../../lib/base-command.js";
6
+ import { resolveReadTargets, soleHitOrFail } from "../../lib/context.js";
7
+ import { buildDefinitionShowQuery, buildDefinitionTagsQuery } from "../../lib/definitions.js";
8
+ import { fail } from "../../lib/fail.js";
7
9
  import { tagFlags } from "../../lib/flags.js";
8
- import { formatKeyValue, sectionHeader } from "../../lib/ui.js";
9
- export default class DefinitionShow extends Command {
10
+ import { formatKeyValue, resourceLabel, sectionHeader } from "../../lib/ui.js";
11
+ export default class DefinitionShow extends WorkflowCommand {
10
12
  static description = 'Show a deployed workflow definition.';
11
13
  static args = {
12
14
  name: Args.string({ required: true, description: 'Workflow definition name.' }),
@@ -17,18 +19,17 @@ export default class DefinitionShow extends Command {
17
19
  };
18
20
  async run() {
19
21
  const { args, flags } = await this.parse(DefinitionShow);
20
- const { client } = await resolveReadContext(flags);
21
- const { groq, params } = buildDefinitionShowQuery({
22
+ const targets = await resolveReadTargets(flags);
23
+ const def = await resolveShownDefinition({
24
+ targets,
22
25
  name: args.name,
23
26
  tag: flags.tag,
24
27
  version: flags.version,
25
28
  });
26
- // A deployed document: the authored {@link WorkflowDefinition} plus the
27
- // deploy-stamped envelope (`version`, `contentHash`, `tag`).
28
- const def = await client.fetch(groq, params);
29
29
  if (!def) {
30
- const at = flags.version ? `v${flags.version}` : 'latest';
31
- this.error(`${logSymbols.error} no definition "${args.name}" (${at})`, { exit: 1 });
30
+ this.error(`${logSymbols.error} no definition "${args.name}" (${versionLabel(flags.version)})`, {
31
+ exit: 1,
32
+ });
32
33
  }
33
34
  this.log(definitionHeader(def, def.tag));
34
35
  this.log('');
@@ -37,11 +38,37 @@ export default class DefinitionShow extends Command {
37
38
  }
38
39
  }
39
40
  }
40
- const HEADER_PAD = 11; // "Description" the longest label in the block
41
- /** The summary block at the top of `definition show`: a bold name + version
42
- * title, then the human title, description, and deploy `tag` as aligned
43
- * detail rows. `tag` is deployment metadata (the partition), not part of the
44
- * authored definition, so it's passed separately and omitted when unknown. */
41
+ export async function resolveShownDefinition({ targets, name, tag, version, }) {
42
+ if (tag === undefined) {
43
+ await assertSingleTagPartition({ targets, name });
44
+ }
45
+ const { groq, params } = buildDefinitionShowQuery({ name, tag, version });
46
+ const hits = await fetchDefinitionHits({ targets, groq, params });
47
+ return soleHitOrFail(hits, `Definition "${name}"`)?.def;
48
+ }
49
+ export function versionLabel(version) {
50
+ return version === undefined ? 'latest' : `v${version}`;
51
+ }
52
+ async function assertSingleTagPartition({ targets, name, }) {
53
+ const { groq, params } = buildDefinitionTagsQuery(name);
54
+ await Promise.all(targets.map(async (target) => {
55
+ const tags = await target.client.fetch(groq, params, { tag: 'definition.show' });
56
+ if (tags.length > 1) {
57
+ fail(`Definition "${name}" exists in several tag partitions in ` +
58
+ `${resourceLabel(target.resource)} — pass --tag to choose one.`, tags.toSorted().join('\n'));
59
+ }
60
+ }));
61
+ }
62
+ export async function fetchDefinitionHits({ targets, groq, params, }) {
63
+ const probes = await Promise.all(targets.map(async (target) => {
64
+ const def = await target.client.fetch(groq, params, {
65
+ tag: 'definition.show',
66
+ });
67
+ return def === null ? undefined : { resource: target.resource, def };
68
+ }));
69
+ return probes.filter((hit) => hit !== undefined);
70
+ }
71
+ const HEADER_PAD = 11;
45
72
  export function definitionHeader(def, tag) {
46
73
  const rows = [
47
74
  formatKeyValue({ key: 'Title', value: def.title, padTo: HEADER_PAD }),
@@ -56,18 +83,15 @@ export function definitionHeader(def, tag) {
56
83
  }
57
84
  return [styleText('bold', `${def.name} v${def.version}`), ...rows].join('\n');
58
85
  }
59
- /** One `· action` line, annotated with the terminal status its `status.set` op sets, if any. */
60
86
  function actionLine(action) {
61
87
  const statusOp = (action.ops ?? []).find((op) => op.type === 'status.set');
62
88
  const status = statusOp ? styleText('dim', ` → ${statusOp.status}`) : '';
63
89
  return ` · ${action.name}${status}`;
64
90
  }
65
- /** A `- activity` line followed by one line per action it exposes. */
66
91
  function activityBlock(activity) {
67
92
  const title = activity.title ? styleText('dim', ` — ${activity.title}`) : '';
68
93
  return [` - ${activity.name}${title}`, ...(activity.actions ?? []).map(actionLine)];
69
94
  }
70
- /** A `• stage` line (with initial/terminal markers) followed by its activity blocks. */
71
95
  function stageBlock(stage, initialStage) {
72
96
  const title = stage.title ? styleText('dim', ` — ${stage.title}`) : '';
73
97
  const terminal = isTerminalStage(stage) ? styleText('dim', ' (terminal)') : '';
@@ -77,10 +101,6 @@ function stageBlock(stage, initialStage) {
77
101
  ...(stage.activities ?? []).flatMap(activityBlock),
78
102
  ];
79
103
  }
80
- /**
81
- * The body lines for `definition show`: every stage with its activities and each
82
- * activity's actions. Pure so it can be asserted without driving the oclif command.
83
- */
84
104
  export function describeDefinition(def) {
85
105
  return [
86
106
  sectionHeader('Stages'),
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
1
  import { type DeployDefinitionResult, type WorkflowDefinition, type WorkflowDeployment } from '@sanity/workflow-engine';
2
+ import { WorkflowCommand } from '../lib/base-command.ts';
3
3
  /** A deployment paired with the definitions selected + validated for it. */
4
4
  interface DeployBatch {
5
5
  deployment: WorkflowDeployment;
@@ -10,7 +10,7 @@ interface DeployFailure {
10
10
  deployment: Pick<WorkflowDeployment, 'name' | 'tag'>;
11
11
  message: string;
12
12
  }
13
- export default class Deploy extends Command {
13
+ export default class Deploy extends WorkflowCommand {
14
14
  static description: string;
15
15
  static examples: string[];
16
16
  static flags: {
@@ -18,9 +18,13 @@ export default class Deploy extends Command {
18
18
  'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
19
  check: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
20
  only: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ 'share-defs': import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
22
  tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
23
  };
23
24
  run(): Promise<void>;
25
+ /** Deploy (or diff, for `--dry-run`) one deployment's definitions.
26
+ * Returns the per-definition results for a real deploy; `undefined`
27
+ * for a dry run, which writes nothing. */
24
28
  private processDeployment;
25
29
  private executeDeploy;
26
30
  private runDryRun;
@@ -42,6 +46,8 @@ export declare function buildBatches(deployments: WorkflowDeployment[], only: st
42
46
  * to their message for {@link deploySummary}. A single-batch run rethrows
43
47
  * instead: it has no summary, so a throw from before the deploy spinner starts
44
48
  * would otherwise surface nowhere — and propagating keeps the stack visible.
49
+ * An auth rejection also rethrows regardless of batch count: a dead token is
50
+ * never batch-local, so continuing would just repeat the same failure.
45
51
  */
46
52
  export declare function reconcileBatches({ batches, log, processBatch, }: {
47
53
  batches: DeployBatch[];
@@ -62,14 +68,14 @@ export declare function deploySummary({ total, failures, }: {
62
68
  /**
63
69
  * The banner printed above a deployment's output so a multi-deployment run is
64
70
  * attributable. `undefined` for a single-target run — it keeps its original
65
- * unprefixed output. A leading blank line separates every deployment after the
66
- * first.
71
+ * unprefixed output.
67
72
  */
68
73
  export declare function deploymentBanner({ deployment, index, total, }: {
69
74
  deployment: Pick<WorkflowDeployment, 'name' | 'tag'>;
70
75
  index: number;
71
76
  total: number;
72
77
  }): string | undefined;
73
- /** One line per deployed definition, symbol-tagged by its result status. */
78
+ /** One line per deployed definition, symbol-tagged by its result status,
79
+ * followed by any advisory effect-output lint warnings indented beneath it. */
74
80
  export declare function deployResultLines(results: DeployDefinitionResult[]): string[];
75
81
  export {};
@@ -1,16 +1,20 @@
1
1
  import { styleText } from 'node:util';
2
- import { Command, Flags } from '@oclif/core';
3
- import { computeDiffEntries, workflow, } from '@sanity/workflow-engine';
2
+ import { Flags } from '@oclif/core';
3
+ import { computeDiffEntries, errorMessage, workflow, } from '@sanity/workflow-engine';
4
4
  import logSymbols from 'log-symbols';
5
5
  import ora from 'ora';
6
+ import { WorkflowCommand } from "../lib/base-command.js";
6
7
  import { clientFor, resolveTokenOrFail } from "../lib/client.js";
7
8
  import { selectDefinitions, validateOrFail } from "../lib/definitions.js";
8
9
  import { diffReport } from "../lib/diff.js";
9
- import { errorMessage, fail } from "../lib/fail.js";
10
+ import { fail, isAuthRejection } from "../lib/fail.js";
10
11
  import { tagFlags } from "../lib/flags.js";
11
12
  import { loadWorkflowConfig } from "../lib/load-config.js";
12
13
  import { deploymentToTarget, selectDeployments } from "../lib/select-deployment.js";
13
- export default class Deploy extends Command {
14
+ import { shareDefinitionsAfterDeploy } from "../lib/share-definitions.js";
15
+ import { cliTelemetry } from "../lib/telemetry.js";
16
+ import { groupBanner } from "../lib/ui.js";
17
+ export default class Deploy extends WorkflowCommand {
14
18
  static description = 'Validate, diff, and deploy workflow definitions to the resource bound by the selected deployment.';
15
19
  static examples = [
16
20
  '<%= config.bin %> deploy --tag prod',
@@ -37,17 +41,17 @@ export default class Deploy extends Command {
37
41
  only: Flags.string({
38
42
  description: 'Limit deploy/check/diff to a single workflow definition by name. Every targeted deployment must contain it.',
39
43
  }),
44
+ 'share-defs': Flags.boolean({
45
+ allowNo: true,
46
+ description: 'Share the definition documents newly created by this deploy with Sanity — the full document, verbatim (structure, names, filters, effect configuration, seeded values), plus its deployment coordinates (project and dataset, or resource id); never content documents, instances, or your Sanity auth token. Opt-out: an interactive terminal is asked once (the answer is remembered), while unattended runs (CI / non-TTY / DO_NOT_TRACK) share nothing unless --share-defs is passed. Use --no-share-defs to opt out. An explicit --share-defs also sends telemetry for this deploy regardless of CI / DO_NOT_TRACK.',
47
+ }),
40
48
  };
41
49
  async run() {
42
50
  const { flags } = await this.parse(Deploy);
43
51
  validateModeFlags(flags.check, flags['dry-run']);
44
52
  const config = await loadWorkflowConfig();
45
- // Validate every deployment up front (below), so a bad definition fails
46
- // before any write — never a partial reconcile from a validation error.
47
53
  const batches = buildBatches(selectDeployments(config, { tag: flags.tag, allTags: flags['all-tags'] }), flags.only);
48
54
  const log = (line) => this.log(line);
49
- // buildBatches already validated everything offline — --check only has to
50
- // report that pass, so it never resolves a token or builds a client.
51
55
  if (flags.check) {
52
56
  await reconcileBatches({
53
57
  batches,
@@ -58,36 +62,63 @@ export default class Deploy extends Command {
58
62
  });
59
63
  return;
60
64
  }
61
- // One token up front, so an --all-tags run authenticates once, not per deployment.
62
65
  const token = await resolveTokenOrFail();
66
+ const candidates = [];
63
67
  const failures = await reconcileBatches({
64
68
  batches,
65
69
  log,
66
- processBatch: ({ deployment, defs }) => this.processDeployment({ deployment, defs, dryRun: flags['dry-run'], token }),
70
+ processBatch: async ({ deployment, defs }) => {
71
+ const client = clientFor(deployment.workflowResource, token);
72
+ const deployed = await this.processDeployment({
73
+ deployment,
74
+ defs,
75
+ dryRun: flags['dry-run'],
76
+ client,
77
+ });
78
+ if (deployed !== undefined) {
79
+ candidates.push({
80
+ client,
81
+ tag: deployment.tag,
82
+ resource: deployment.workflowResource,
83
+ results: deployed.results,
84
+ deployId: deployed.deployId,
85
+ });
86
+ }
87
+ },
67
88
  });
68
89
  for (const line of deploySummary({ total: batches.length, failures })) {
69
90
  log(line);
70
91
  }
92
+ await shareDefinitionsAfterDeploy({
93
+ flag: flags['share-defs'],
94
+ candidates,
95
+ warn: (message) => this.warn(message),
96
+ });
71
97
  if (failures.length > 0) {
72
98
  this.exit(1);
73
99
  }
74
100
  }
75
- async processDeployment({ deployment, defs, dryRun, token, }) {
101
+ async processDeployment({ deployment, defs, dryRun, client, }) {
76
102
  const target = deploymentToTarget(deployment);
77
- const client = clientFor(deployment.workflowResource, token);
78
103
  if (dryRun) {
79
104
  await this.runDryRun({ client, defs, target });
80
- return;
105
+ return undefined;
81
106
  }
82
- await this.executeDeploy({ client, defs, target });
107
+ return this.executeDeploy({ client, defs, target });
83
108
  }
84
109
  async executeDeploy({ client, defs, target, }) {
85
110
  const spinner = ora(`Deploying ${defs.length} definition(s)…`).start();
86
111
  try {
87
- const { results } = await workflow.deployDefinitions({ ...target, client, definitions: defs });
88
- spinner.succeed(`Processed ${results.length} definition(s)`);
89
- for (const line of deployResultLines(results))
112
+ const deployed = await workflow.deployDefinitions({
113
+ ...target,
114
+ client,
115
+ definitions: defs,
116
+ telemetry: cliTelemetry().logger,
117
+ });
118
+ spinner.succeed(`Processed ${deployed.results.length} definition(s)`);
119
+ for (const line of deployResultLines(deployed.results))
90
120
  this.log(line);
121
+ return deployed;
91
122
  }
92
123
  catch (error) {
93
124
  spinner.fail(`Deploy failed — ${errorMessage(error)}`);
@@ -108,24 +139,14 @@ export default class Deploy extends Command {
108
139
  }
109
140
  }
110
141
  }
111
- /** Reject the contradictory `--check --dry-run` combination. */
112
142
  export function validateModeFlags(check, dryRun) {
113
143
  if (check && dryRun) {
114
144
  fail('Pass either --check or --dry-run, not both.');
115
145
  }
116
146
  }
117
- /** The one identity format for a deployment in output — banner, failure
118
- * summary, and validation attribution all render it the same way. */
119
147
  function deploymentLabel({ name, tag }) {
120
148
  return `${name} (${tag})`;
121
149
  }
122
- /**
123
- * Select and validate every deployment's definitions up front. Validation runs
124
- * before any network write ({@link validateOrFail} exits on a bad definition),
125
- * so a whole-config deploy can never leave a partial reconcile behind a broken
126
- * batch. In a multi-deployment run the failure names its deployment — it exits
127
- * before any banner could attribute it.
128
- */
129
150
  export function buildBatches(deployments, only) {
130
151
  return deployments.map((deployment) => {
131
152
  const context = deployments.length > 1 ? `${deploymentLabel(deployment)} — ` : '';
@@ -134,14 +155,6 @@ export function buildBatches(deployments, only) {
134
155
  return { deployment, defs };
135
156
  });
136
157
  }
137
- /**
138
- * Run `processBatch` over each batch, continuing past a failure so one bad
139
- * deployment can't strand the rest — deployments are independent, and deploys
140
- * are idempotent, so a partial run is safe to re-run. Failures are flattened
141
- * to their message for {@link deploySummary}. A single-batch run rethrows
142
- * instead: it has no summary, so a throw from before the deploy spinner starts
143
- * would otherwise surface nowhere — and propagating keeps the stack visible.
144
- */
145
158
  export async function reconcileBatches({ batches, log, processBatch, }) {
146
159
  const failures = [];
147
160
  for (const [index, batch] of batches.entries()) {
@@ -153,7 +166,7 @@ export async function reconcileBatches({ batches, log, processBatch, }) {
153
166
  await processBatch(batch);
154
167
  }
155
168
  catch (error) {
156
- if (batches.length === 1) {
169
+ if (batches.length === 1 || isAuthRejection(error)) {
157
170
  throw error;
158
171
  }
159
172
  failures.push({ deployment: batch.deployment, message: errorMessage(error) });
@@ -161,13 +174,6 @@ export async function reconcileBatches({ batches, log, processBatch, }) {
161
174
  }
162
175
  return failures;
163
176
  }
164
- /**
165
- * The closing summary for a multi-deployment run that had failures: the
166
- * succeeded/failed tally and each failed deployment. Empty for a clean run.
167
- * The wording is mode-neutral because a `--dry-run` failure lands here too,
168
- * where nothing was deployed. Single-batch runs never reach it — their lone
169
- * failure rethrows out of {@link reconcileBatches}.
170
- */
171
177
  export function deploySummary({ total, failures, }) {
172
178
  if (failures.length === 0) {
173
179
  return [];
@@ -178,26 +184,17 @@ export function deploySummary({ total, failures, }) {
178
184
  ...failures.map((f) => styleText('red', ` ${logSymbols.error} ${deploymentLabel(f.deployment)} — ${f.message}`)),
179
185
  ];
180
186
  }
181
- /**
182
- * The banner printed above a deployment's output so a multi-deployment run is
183
- * attributable. `undefined` for a single-target run — it keeps its original
184
- * unprefixed output. A leading blank line separates every deployment after the
185
- * first.
186
- */
187
187
  export function deploymentBanner({ deployment, index, total, }) {
188
- if (total <= 1) {
189
- return undefined;
190
- }
191
- const gap = index > 0 ? '\n' : '';
192
- return `${gap}${styleText('bold', `▸ ${deploymentLabel(deployment)}`)}`;
188
+ return groupBanner({ label: deploymentLabel(deployment), index, total });
193
189
  }
194
- /** One line per deployed definition, symbol-tagged by its result status. */
195
190
  export function deployResultLines(results) {
196
191
  const symbols = {
197
192
  created: logSymbols.success,
198
193
  };
199
- return results.map((r) => {
194
+ return results.flatMap((r) => {
200
195
  const symbol = symbols[r.status] ?? logSymbols.warning;
201
- return ` ${symbol} ${r.status.padEnd(9)} ${r.name} v${r.version}`;
196
+ const head = ` ${symbol} ${r.status.padEnd(9)} ${r.name} v${r.version}`;
197
+ const warnings = (r.warnings ?? []).map((w) => ` ${logSymbols.warning} ${w}`);
198
+ return [head, ...warnings];
202
199
  });
203
200
  }
@@ -1,5 +1,20 @@
1
- import { Command } from '@oclif/core';
2
- import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation } from '@sanity/workflow-engine';
1
+ import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation, type WorkflowEvaluation } from '@sanity/workflow-engine';
2
+ import { WorkflowCommand } from '../lib/base-command.ts';
3
+ /** The `--json` transitions payload: raw derived state (atom GROQ, negation,
4
+ * pivotality, solvable requirement) — structure for scripts, never baked
5
+ * English. Pure so the shape is a testable contract. */
6
+ export declare function rawTransitionProjection(evaluation: WorkflowEvaluation): {
7
+ name: string;
8
+ to: string;
9
+ whenSatisfied: boolean;
10
+ unevaluable: boolean;
11
+ blockedBy: {
12
+ requirement?: import("@sanity/groq-condition-describe").AtomRequirement;
13
+ groq: string;
14
+ negated: boolean;
15
+ pivotal: boolean;
16
+ }[];
17
+ }[];
3
18
  /**
4
19
  * The diagnosis body: the verdict line, the current stage's activities +
5
20
  * transitions as evidence (for any in-flight state), and the cause block with
@@ -8,12 +23,15 @@ import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation } from '@
8
23
  * {@link SuggestedRemediation}s rather than re-deriving them, so the rendered
9
24
  * fix block and the `--json` output stay one computation.
10
25
  */
11
- export declare function renderDiagnosis({ diagnosis, input, remediations, }: {
26
+ export declare function renderDiagnosis({ diagnosis, input, remediations, explanations, }: {
12
27
  diagnosis: Diagnosis;
13
28
  input: DiagnoseInput;
14
29
  remediations: SuggestedRemediation[];
30
+ /** Insight summaries per unsatisfied transition name — rendered under the
31
+ * transition line so "stuck" reads as "stuck until X". */
32
+ explanations?: ReadonlyMap<string, string> | undefined;
15
33
  }): string[];
16
- export default class Diagnose extends Command {
34
+ export default class Diagnose extends WorkflowCommand {
17
35
  static description: string;
18
36
  static examples: string[];
19
37
  static args: {