@copado/agentia-cli 1.0.0-beta.1 → 1.0.0-beta.2

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 (39) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +499 -323
  3. package/dist/commands/cicd/cloud/commit.js +252 -0
  4. package/dist/commands/cicd/cloud/promote.js +139 -0
  5. package/dist/commands/cicd/health-check.js +77 -0
  6. package/dist/commands/cicd/work/commit.js +10 -324
  7. package/dist/commands/cicd/work/environment-sync.js +120 -0
  8. package/dist/commands/cicd/work/get.js +25 -4
  9. package/dist/commands/cicd/work/promote.js +93 -97
  10. package/dist/commands/cicd/work/set.js +4 -2
  11. package/dist/commands/cicd/work/submit.js +7 -0
  12. package/dist/lib/defaults-store.js +2 -1
  13. package/dist/lib/git-sizer.js +5 -0
  14. package/dist/lib/git-worktree.js +16 -1
  15. package/dist/lib/health-check.js +3 -0
  16. package/dist/lib/mcp/command-id.js +1 -0
  17. package/dist/lib/mcp/health-check-tools.js +16 -0
  18. package/dist/lib/mcp/server.js +2 -1
  19. package/dist/lib/mcp/tool-annotations.js +265 -0
  20. package/dist/lib/mcp/tools.js +3 -0
  21. package/dist/lib/mcp/work-tools.js +96 -78
  22. package/dist/lib/metadata-pathspecs.js +4 -0
  23. package/dist/lib/metadataRegistry.json +5427 -0
  24. package/dist/lib/promotion-types.js +4 -1
  25. package/dist/lib/runtime.js +1 -1
  26. package/dist/lib/work-environment-sync-errors.js +4 -0
  27. package/dist/lib/work-environment-sync-ready.js +18 -0
  28. package/dist/lib/work-environment-sync.js +42 -0
  29. package/dist/lib/work-promote-deploy.js +13 -0
  30. package/dist/lib/work-promote-errors.js +5 -0
  31. package/dist/lib/work-promote.js +12 -0
  32. package/dist/lib/work-set-selection.js +2 -1
  33. package/dist/lib/work-submit-ahead-behind.js +8 -0
  34. package/dist/lib/work-types.js +2 -1
  35. package/oclif.manifest.json +1118 -675
  36. package/package.json +5 -2
  37. package/templates/skills/agentia-cicd/SKILL.md +2 -2
  38. package/templates/skills/agentia-cicd/references/promotions.md +8 -8
  39. package/templates/skills/agentia-cicd/references/work.md +16 -14
@@ -1,332 +1,18 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import { readFileSync } from 'node:fs';
3
- import { AgentiaCommand } from '../../../lib/agentia-command.js';
4
- import { resolveWorkItemId, WorkActiveResolutionError } from '../../../lib/work-active.js';
5
- import { buildWorkCommitRequest, formatWorkCommitResult, normalizeWorkCommitInput, performWorkCommit, resolveWorkCommitPlatformExperience, WORK_COMMIT_ACTIONS, WORK_COMMIT_DEFAULT_TIMEOUT_SECONDS, WORK_COMMIT_METADATA_CATEGORY_HELP, WorkCommitJobFailedError, WorkCommitTimeoutError, WorkCommitValidationError, } from '../../../lib/work-commit.js';
6
- import { formatWorkLocalCommitResult, performWorkLocalCommit, WorkLocalCommitValidationError, } from '../../../lib/work-local-commit.js';
7
- const CLOUD_ONLY_FLAGS = [
8
- 'action',
9
- 'base-branch',
10
- 'commit-id',
11
- 'execute-commit',
12
- 'file',
13
- 'metadata-category',
14
- 'metadata-message',
15
- 'metadata-name',
16
- 'metadata-raw-json',
17
- 'metadata-type',
18
- 'recreate-feature-branch',
19
- 'selection',
20
- 'stdin',
21
- 'wait',
22
- 'wait-timeout',
23
- ];
24
- const LOCAL_ONLY_FLAGS = ['all', 'apex-test-classes', 'full-metadata', 'permissions'];
25
- /** Explicitly provided flags only; values that came from an oclif default do not count. */
26
- function providedFlags(names, flags, metadata) {
27
- return names.filter((name) => flags[name] !== undefined && metadata.flags[name]?.setFromDefault !== true);
28
- }
29
- function readStdin() {
30
- if (process.stdin.isTTY) {
31
- throw new WorkCommitValidationError('Cannot read --stdin from an interactive terminal. Pipe JSON input or use --file <path>.');
32
- }
33
- return new Promise((resolve, reject) => {
34
- let data = '';
35
- process.stdin.setEncoding('utf8');
36
- process.stdin.on('data', (chunk) => {
37
- data += chunk;
38
- });
39
- process.stdin.on('end', () => resolve(data));
40
- process.stdin.on('error', reject);
41
- });
42
- }
43
- function readJsonFile(path) {
44
- try {
45
- return readFileSync(path, 'utf8');
46
- }
47
- catch (error) {
48
- throw new WorkCommitValidationError(`Cannot read JSON request file ${path}: ${error.message}`);
49
- }
50
- }
51
- function parseJsonValue(raw, label) {
52
- try {
53
- return JSON.parse(raw);
54
- }
55
- catch (error) {
56
- throw new WorkCommitValidationError(`${label} must contain valid JSON: ${error.message}`);
57
- }
58
- }
59
- function hasSimpleChangeFlags(flags) {
60
- return (flags.action !== undefined ||
61
- flags['metadata-category'] !== undefined ||
62
- flags['metadata-message'] !== undefined ||
63
- flags['metadata-name'] !== undefined ||
64
- flags['metadata-raw-json'] !== undefined ||
65
- flags['metadata-type'] !== undefined ||
66
- flags.selection !== undefined);
67
- }
68
- function simpleInputFromFlags(flags) {
69
- const rawJson = flags['metadata-raw-json'] === undefined
70
- ? undefined
71
- : parseJsonValue(flags['metadata-raw-json'], '--metadata-raw-json');
72
- return {
73
- baseBranch: flags['base-branch'] ?? null,
74
- changes: [
75
- {
76
- ...(flags.action === undefined ? {} : { a: flags.action }),
77
- ...(flags['metadata-category'] === undefined ? {} : { c: flags['metadata-category'] }),
78
- ...(rawJson === undefined ? {} : { j: rawJson }),
79
- ...(flags['metadata-message'] === undefined ? {} : { m: flags['metadata-message'] }),
80
- n: flags['metadata-name'],
81
- ...(flags.selection === undefined ? {} : { selections: flags.selection }),
82
- t: flags['metadata-type'],
83
- },
84
- ],
85
- commitId: flags['commit-id'],
86
- executeCommit: flags['execute-commit'],
87
- message: flags.message,
88
- recreateFeatureBranch: flags['recreate-feature-branch'],
89
- };
90
- }
91
- async function jsonInputFromFlags(flags) {
92
- const raw = flags.file === undefined ? await readStdin() : readJsonFile(flags.file);
93
- const input = normalizeWorkCommitInput(parseJsonValue(raw, flags.file === undefined ? 'stdin' : flags.file));
94
- return {
95
- ...input,
96
- ...(flags['base-branch'] === undefined ? {} : { baseBranch: flags['base-branch'] }),
97
- ...(flags['commit-id'] === undefined ? {} : { commitId: flags['commit-id'] }),
98
- ...(flags['execute-commit'] ? {} : { executeCommit: false }),
99
- ...(flags.message === undefined ? {} : { message: flags.message }),
100
- ...(flags['recreate-feature-branch'] ? { recreateFeatureBranch: true } : {}),
101
- };
102
- }
103
- async function commitInputFromFlags(flags) {
104
- const jsonModes = [flags.file !== undefined, flags.stdin].filter(Boolean).length;
105
- const simpleMode = hasSimpleChangeFlags(flags);
106
- if (jsonModes > 1) {
107
- throw new WorkCommitValidationError('Use only one JSON input mode: --file or --stdin.');
108
- }
109
- if (jsonModes > 0 && simpleMode) {
110
- throw new WorkCommitValidationError('Use JSON input or simple metadata flags, not both.');
111
- }
112
- if (jsonModes > 0) {
113
- return jsonInputFromFlags(flags);
114
- }
115
- if (!simpleMode) {
116
- throw new WorkCommitValidationError('Requires simple metadata flags such as --metadata-name and --metadata-type, or JSON input with --file/--stdin.');
117
- }
118
- return simpleInputFromFlags(flags);
119
- }
120
- export default class WorkCommit extends AgentiaCommand {
121
- static args = {
122
- id: Args.string({
123
- description: 'User story ID (defaults to the active work item from `work set`)',
124
- required: false,
125
- }),
126
- };
1
+ import CloudCommit from '../cloud/commit.js';
2
+ /**
3
+ * Compatibility alias of `cicd cloud commit`.
4
+ * Prefer `agentia cicd cloud commit` in scripts and docs.
5
+ */
6
+ export default class WorkCommit extends CloudCommit {
127
7
  static description = [
8
+ 'Compatibility alias of cicd cloud commit.',
128
9
  'Create a Copado cloud metadata commit for a user story.',
129
- 'Requires --cloud: preflights the story, submits POST /release/commit, and polls the job monitor by default.',
130
- 'For local Git work, use git add/commit then `cicd work publish`. Permission and full-metadata selections belong on `cicd work publish`.',
10
+ 'Prefer: agentia cicd cloud commit.',
131
11
  ].join(' ');
132
12
  static examples = [
13
+ '<%= config.bin %> cicd cloud commit --message "Commit AccountService" --metadata-name AccountService --metadata-type ApexClass --metadata-category SFDX --action Add',
133
14
  '<%= config.bin %> <%= command.id %> --cloud --message "Commit AccountService" --metadata-name AccountService --metadata-type ApexClass --metadata-category SFDX --action Add',
134
- '<%= config.bin %> <%= command.id %> a07XXXXXXXXXXXXXXX --cloud --message "Update Aura files" --metadata-name accountEditor --metadata-type AuraDefinitionBundle --metadata-category SFDX --action SelectiveCommit --selection accountEditor/accountEditor.cmp --selection accountEditor/accountEditorController.js',
135
- '<%= config.bin %> <%= command.id %> --cloud --file commit-request.json # {"message":"Commit AccountService","changes":[{"a":"Add","c":"SFDX","n":"AccountService","t":"ApexClass"}]}',
136
- 'cat commit-request.json | <%= config.bin %> <%= command.id %> --cloud --stdin --json',
137
15
  ];
138
- static flags = {
139
- action: Flags.string({
140
- description: 'Metadata action for simple flag input. Defaults to Add.',
141
- options: [...WORK_COMMIT_ACTIONS],
142
- }),
143
- 'base-branch': Flags.string({
144
- description: 'Optional base branch to include in the commit request.',
145
- }),
146
- cloud: Flags.boolean({
147
- default: false,
148
- description: 'Create a Copado cloud metadata commit (POST /release/commit). Required. For local Git work, use git commit then `cicd work publish`.',
149
- }),
150
- 'commit-id': Flags.string({
151
- description: 'Existing local Git commit identifier to register when --no-execute-commit is used.',
152
- }),
153
- 'execute-commit': Flags.boolean({
154
- allowNo: true,
155
- default: true,
156
- description: 'Execute the Copado commit job. Use --no-execute-commit only with --commit-id.',
157
- }),
158
- file: Flags.string({
159
- char: 'f',
160
- description: 'Path to a JSON commit request body.',
161
- }),
162
- message: Flags.string({
163
- char: 'm',
164
- description: 'Commit message. Required unless provided by JSON input.',
165
- }),
166
- 'metadata-category': Flags.string({
167
- description: WORK_COMMIT_METADATA_CATEGORY_HELP,
168
- }),
169
- 'metadata-message': Flags.string({
170
- description: 'Optional per-change message for simple flag input.',
171
- }),
172
- 'metadata-name': Flags.string({
173
- description: 'Metadata full name for simple flag input.',
174
- }),
175
- 'metadata-raw-json': Flags.string({
176
- description: 'Optional raw metadata JSON value for simple flag input.',
177
- }),
178
- 'metadata-type': Flags.string({
179
- description: 'Metadata type for simple flag input, for example ApexClass.',
180
- }),
181
- 'recreate-feature-branch': Flags.boolean({
182
- default: false,
183
- description: 'Ask Copado to recreate the feature branch before committing.',
184
- }),
185
- selection: Flags.string({
186
- description: 'Selected file inside a bundle for SelectiveCommit. Can be provided multiple times.',
187
- multiple: true,
188
- }),
189
- stdin: Flags.boolean({
190
- default: false,
191
- description: 'Read a JSON commit request body from stdin.',
192
- }),
193
- wait: Flags.boolean({
194
- allowNo: true,
195
- default: true,
196
- description: 'Poll the commit job to a terminal status when a jobExecution Id is returned.',
197
- }),
198
- 'wait-timeout': Flags.integer({
199
- default: WORK_COMMIT_DEFAULT_TIMEOUT_SECONDS,
200
- description: 'Maximum seconds to wait for the commit job.',
201
- }),
202
- };
203
- async run() {
204
- const { args, flags, metadata } = await this.parse(WorkCommit);
205
- const commitFlags = flags;
206
- const flagMetadata = metadata;
207
- // Local git commit (`--local`, `--permissions`, `--full-metadata`) is not exposed.
208
- // Implementation remains in runLocalCommit / src/lib/work-local-commit.ts.
209
- if (!commitFlags.cloud) {
210
- this.error('This command creates Copado cloud metadata commits. For local Git work, use git commit then `cicd work publish`. Pass --cloud for a Copado cloud commit. Permission and full-metadata selections belong on `cicd work publish`.', {
211
- exit: 2,
212
- suggestions: [
213
- 'git add -A && git commit',
214
- `${this.config.bin} cicd work publish`,
215
- `${this.config.bin} cicd work commit --cloud --message "<message>" --metadata-name <name> --metadata-type <type> --metadata-category SFDX`,
216
- ],
217
- });
218
- }
219
- const localOnly = providedFlags(LOCAL_ONLY_FLAGS, flags, flagMetadata);
220
- if (localOnly.length > 0) {
221
- this.error(`Cloud commit does not support ${localOnly.map((name) => `--${name}`).join(', ')}.`, {
222
- exit: 2,
223
- suggestions: [`${this.config.bin} cicd work publish`],
224
- });
225
- }
226
- if (!Number.isInteger(commitFlags['wait-timeout']) || commitFlags['wait-timeout'] <= 0) {
227
- this.error('--wait-timeout must be a positive integer.', { exit: 2 });
228
- }
229
- let workItemId;
230
- try {
231
- workItemId = await resolveWorkItemId(args.id);
232
- }
233
- catch (error) {
234
- if (error instanceof WorkActiveResolutionError) {
235
- this.error(error.message, {
236
- exit: 1,
237
- suggestions: [`${this.config.bin} cicd work set <id>`],
238
- });
239
- }
240
- throw error;
241
- }
242
- try {
243
- const input = await commitInputFromFlags(commitFlags);
244
- const request = buildWorkCommitRequest(input, { message: commitFlags.message, userStoryId: workItemId });
245
- const initialPlatformExperience = await resolveWorkCommitPlatformExperience({
246
- userStoryId: workItemId,
247
- });
248
- const result = await performWorkCommit({
249
- hooks: {
250
- log: (message) => this.display(() => this.log(message)),
251
- withSpinner: (message, action) => this.withSpinner(message, action),
252
- },
253
- initialPlatformExperience,
254
- request,
255
- timeoutSeconds: commitFlags['wait-timeout'],
256
- wait: commitFlags.wait,
257
- });
258
- this.display(() => {
259
- this.log(formatWorkCommitResult(result));
260
- });
261
- return result;
262
- }
263
- catch (error) {
264
- if (error instanceof WorkCommitValidationError) {
265
- this.error(error.message, { exit: 2 });
266
- }
267
- if (error instanceof WorkCommitJobFailedError || error instanceof WorkCommitTimeoutError) {
268
- this.display(() => {
269
- if (error.result !== undefined) {
270
- this.log(formatWorkCommitResult(error.result));
271
- }
272
- });
273
- error.oclif = { exit: 1 };
274
- throw error;
275
- }
276
- throw error;
277
- }
278
- }
279
- toErrorJson(err) {
280
- const base = super.toErrorJson(err);
281
- if (err instanceof WorkCommitJobFailedError || err instanceof WorkCommitTimeoutError) {
282
- return {
283
- ...base,
284
- error: {
285
- ...(base.error && typeof base.error === 'object' ? base.error : {}),
286
- ...(err.jobMonitor === undefined ? {} : { jobMonitor: err.jobMonitor }),
287
- ...(err.result === undefined ? {} : { result: err.result }),
288
- },
289
- };
290
- }
291
- return base;
292
- }
293
- // Local git commit path remains implemented but is not exposed on this command.
294
- async runLocalCommit(id, commitFlags, flagMetadata) {
295
- const cloudOnly = providedFlags(CLOUD_ONLY_FLAGS, commitFlags, flagMetadata);
296
- if (id !== undefined) {
297
- cloudOnly.unshift('<id>');
298
- }
299
- if (cloudOnly.length > 0) {
300
- const unsupported = cloudOnly.map((name) => (name === '<id>' ? name : `--${name}`)).join(', ');
301
- this.error(`Local commit does not support ${unsupported}.`, {
302
- exit: 2,
303
- suggestions: [`${this.config.bin} cicd work commit --cloud`],
304
- });
305
- }
306
- try {
307
- const result = await performWorkLocalCommit({
308
- all: commitFlags.all === true,
309
- ...(commitFlags['apex-test-classes'] === undefined
310
- ? {}
311
- : { apexTestClasses: commitFlags['apex-test-classes'] }),
312
- ...(commitFlags['full-metadata'] === undefined ? {} : { fullMetadata: commitFlags['full-metadata'] }),
313
- ...(commitFlags.message === undefined ? {} : { message: commitFlags.message }),
314
- ...(commitFlags.permissions === undefined ? {} : { permissions: commitFlags.permissions }),
315
- });
316
- this.display(() => {
317
- this.log(formatWorkLocalCommitResult(result));
318
- });
319
- return result;
320
- }
321
- catch (error) {
322
- if (error instanceof WorkLocalCommitValidationError) {
323
- this.error(error.message, {
324
- exit: error.exit,
325
- ...(error.suggestions === undefined ? {} : { suggestions: error.suggestions }),
326
- });
327
- }
328
- throw error;
329
- }
330
- }
16
+ static hidden = true;
331
17
  }
332
18
  //# sourceMappingURL=commit.js.map
@@ -0,0 +1,120 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { AgentiaCommand } from '../../../lib/agentia-command.js';
3
+ import { assertEnvironmentSyncDestOrgSelector, formatWorkEnvironmentSyncResult, performWorkEnvironmentSync, WorkEnvironmentSyncConflictError, WorkEnvironmentSyncValidationError, } from '../../../lib/work-environment-sync.js';
4
+ export default class WorkEnvironmentSync extends AgentiaCommand {
5
+ static description = [
6
+ 'Sync committed metadata from the next environment onto the destination environment (or current feature branch).',
7
+ 'Back-promotes every user story behind the destination environment from the next environment branch.',
8
+ 'GET user stories ahead and behind. If none are behind, log that and stop. Otherwise create a back-promotion and POST those behind stories onto the promotion.',
9
+ 'Resolves the next-environment Git branch from --destination-environment or --dev-org-branch (the dest pipeline connection destinationBranch, or that destination environment\'s outbound branch).',
10
+ 'Merges origin/<next-env> with --no-ff --no-commit, restores every other path from HEAD, commits the guessed pathspecs,',
11
+ 'and pushes the dest org branch (and the feature branch when HEAD starts with feature/).',
12
+ 'If guessed pathspecs still conflict, PATCH the back-promotion status to Merge Conflict, resolve the files, and re-run with --promotion and --continue. The promotion destination environment supplies the dest org branch and the next-env branch.',
13
+ 'To discard a conflicted merge, re-run with --promotion and --abort (runs git merge --abort and sets the back-promotion status to Draft).',
14
+ 'After a successful sync, PATCH the back-promotion status to Completed.',
15
+ ].join(' ');
16
+ static examples = [
17
+ '<%= config.bin %> <%= command.id %> --destination-environment=dev1',
18
+ '<%= config.bin %> <%= command.id %> --dev-org-branch=dev1-sfp',
19
+ '<%= config.bin %> <%= command.id %> --promotion=P29334 --continue',
20
+ '<%= config.bin %> <%= command.id %> --promotion=P29334 --abort',
21
+ '<%= config.bin %> <%= command.id %> --destination-environment=dev1 --json',
22
+ ];
23
+ static flags = {
24
+ abort: Flags.boolean({
25
+ default: false,
26
+ description: 'Abort an in-progress merge (`git merge --abort`) and set the back-promotion status to Draft. Requires --promotion. Exclusive with --continue.',
27
+ exclusive: ['continue'],
28
+ }),
29
+ continue: Flags.boolean({
30
+ default: false,
31
+ description: 'Commit resolved merge conflicts and finish the remaining environment-sync git steps.',
32
+ exclusive: ['abort'],
33
+ }),
34
+ 'destination-credential': Flags.string({
35
+ description: 'Credential ID posted as destinationCredentialId when creating the back-promotion. Copado requires a credential on the next/source environment (PRO-033 if a dest/dev credential is sent) and then stores the dest/dev credential. Defaults to that source environment defaultCredential, otherwise the first source credential.',
36
+ }),
37
+ 'destination-environment': Flags.string({
38
+ description: 'Destination org environment name or Id. Resolves to that environment\'s pipeline connection Git branch.',
39
+ exclusive: ['dev-org-branch'],
40
+ }),
41
+ 'dev-org-branch': Flags.string({
42
+ description: 'Destination org Git branch. Defaults to lastDevOrgBranch from `cicd work set`.',
43
+ exclusive: ['destination-environment'],
44
+ }),
45
+ 'pipeline-id': Flags.string({
46
+ description: 'Pipeline ID. Defaults to the active work item from `cicd work set`.',
47
+ }),
48
+ promotion: Flags.string({
49
+ description: 'Back-promotion Id or name to PATCH when this run stops on merge conflicts, finishes successfully, continues, or aborts. Included in the conflict continue and abort commands. Defaults to the back-promotion created in this run.',
50
+ }),
51
+ };
52
+ async run() {
53
+ const { flags } = await this.parse(WorkEnvironmentSync);
54
+ try {
55
+ await assertEnvironmentSyncDestOrgSelector({
56
+ abort: flags.abort === true,
57
+ continue: flags.continue === true,
58
+ destinationEnvironment: flags['destination-environment'],
59
+ devOrgBranch: flags['dev-org-branch'],
60
+ promotion: flags.promotion,
61
+ });
62
+ const result = await performWorkEnvironmentSync({
63
+ abort: flags.abort === true,
64
+ cliBin: this.config.bin,
65
+ continue: flags.continue === true,
66
+ hooks: {
67
+ log: (message) => {
68
+ this.display(() => {
69
+ this.log(message);
70
+ });
71
+ },
72
+ withSpinner: (message, action) => this.withSpinner(message, action),
73
+ },
74
+ json: this.jsonEnabled(),
75
+ ...(flags['destination-credential'] === undefined
76
+ ? {}
77
+ : { destinationCredentialId: flags['destination-credential'] }),
78
+ ...(flags['destination-environment'] === undefined
79
+ ? {}
80
+ : { destinationEnvironment: flags['destination-environment'] }),
81
+ ...(flags['dev-org-branch'] === undefined ? {} : { devOrgBranch: flags['dev-org-branch'] }),
82
+ ...(flags['pipeline-id'] === undefined ? {} : { pipelineId: flags['pipeline-id'] }),
83
+ ...(flags.promotion === undefined ? {} : { promotion: flags.promotion }),
84
+ });
85
+ this.display(() => {
86
+ const formatted = formatWorkEnvironmentSyncResult(result);
87
+ if (formatted.length > 0) {
88
+ this.log(formatted);
89
+ }
90
+ });
91
+ return result;
92
+ }
93
+ catch (error) {
94
+ if (error instanceof WorkEnvironmentSyncConflictError) {
95
+ const { suggestions } = error;
96
+ const suggestionOptions = suggestions === undefined ? {} : { suggestions };
97
+ if (this.jsonEnabled()) {
98
+ const message = error.gitStatus === undefined || error.gitStatus.length === 0
99
+ ? error.message
100
+ : `${error.message}\n\n${error.gitStatus}`;
101
+ this.error(message, { exit: error.exit, ...suggestionOptions });
102
+ }
103
+ this.error(error.message, { exit: false, ...suggestionOptions });
104
+ if (error.gitStatus !== undefined && error.gitStatus.length > 0) {
105
+ this.log('');
106
+ this.log(error.gitStatus);
107
+ }
108
+ this.exit(error.exit);
109
+ }
110
+ if (error instanceof WorkEnvironmentSyncValidationError) {
111
+ this.error(error.message, {
112
+ exit: error.exit,
113
+ ...(error.suggestions === undefined ? {} : { suggestions: error.suggestions }),
114
+ });
115
+ }
116
+ throw error;
117
+ }
118
+ }
119
+ }
120
+ //# sourceMappingURL=environment-sync.js.map
@@ -1,8 +1,9 @@
1
- import { Args } from '@oclif/core';
1
+ import { Args, Flags } from '@oclif/core';
2
2
  import { AgentiaCommand } from '../../../lib/agentia-command.js';
3
3
  import { CicdGateway } from '../../../lib/cicd-gateway.js';
4
4
  import { resolveWorkItemId, WorkActiveResolutionError } from '../../../lib/work-active.js';
5
- import { formatUserStoryDetail, workEndpoint } from '../../../lib/work-types.js';
5
+ import { formatUserStoriesAheadBehindLines, reportUserStoriesAheadBehindForStory, } from '../../../lib/work-submit-ahead-behind.js';
6
+ import { formatUserStoryDetail, workEndpoint, } from '../../../lib/work-types.js';
6
7
  export default class WorkGet extends AgentiaCommand {
7
8
  static args = {
8
9
  id: Args.string({
@@ -15,9 +16,17 @@ export default class WorkGet extends AgentiaCommand {
15
16
  '<%= config.bin %> <%= command.id %>',
16
17
  '<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU',
17
18
  '<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --json',
19
+ '<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --user-stories-ahead-behind',
20
+ '<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --user-stories-ahead-behind --json',
18
21
  ];
22
+ static flags = {
23
+ 'user-stories-ahead-behind': Flags.boolean({
24
+ default: false,
25
+ description: 'When the user story has a pipeline and source environment, compare user stories ahead of and behind the next pipeline environment',
26
+ }),
27
+ };
19
28
  async run() {
20
- const { args } = await this.parse(WorkGet);
29
+ const { args, flags } = await this.parse(WorkGet);
21
30
  let workItemId;
22
31
  try {
23
32
  workItemId = await resolveWorkItemId(args.id);
@@ -35,10 +44,22 @@ export default class WorkGet extends AgentiaCommand {
35
44
  endpoint: workEndpoint(workItemId),
36
45
  method: 'GET',
37
46
  }));
47
+ let userStoriesAheadBehind;
48
+ if (flags['user-stories-ahead-behind']) {
49
+ userStoriesAheadBehind = await reportUserStoriesAheadBehindForStory(userStory, {
50
+ warn: (message) => this.warn(message),
51
+ });
52
+ }
38
53
  this.display(() => {
39
54
  this.log(formatUserStoryDetail(userStory));
55
+ if (userStoriesAheadBehind !== undefined) {
56
+ this.log(formatUserStoriesAheadBehindLines(userStoriesAheadBehind));
57
+ }
40
58
  });
41
- return userStory;
59
+ return {
60
+ ...userStory,
61
+ ...(userStoriesAheadBehind === undefined ? {} : { userStoriesAheadBehind }),
62
+ };
42
63
  }
43
64
  }
44
65
  //# sourceMappingURL=get.js.map