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

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 (45) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +384 -282
  3. package/dist/commands/cicd/data/template/save-detail.js +2 -2
  4. package/dist/commands/cicd/repository/auth/https/configure.js +21 -0
  5. package/dist/commands/cicd/repository/auth/oauth/login.js +56 -0
  6. package/dist/commands/cicd/repository/auth/setup.js +28 -0
  7. package/dist/commands/cicd/repository/auth/ssh/configure.js +22 -0
  8. package/dist/commands/cicd/repository/create.js +2 -2
  9. package/dist/commands/cicd/repository/reset.js +17 -5
  10. package/dist/commands/cicd/repository/update.js +6 -14
  11. package/dist/commands/cicd/work/environment-sync.js +66 -61
  12. package/dist/commands/cicd/work/get.js +5 -9
  13. package/dist/commands/cicd/work/submit.js +1 -1
  14. package/dist/commands/runtime.js +26 -0
  15. package/dist/hooks/finally/ide-command-event.js +1 -1
  16. package/dist/hooks/init/plugin-host.js +6 -0
  17. package/dist/lib/agentia-command.js +16 -0
  18. package/dist/lib/cursor-mcp-config.js +3 -1
  19. package/dist/lib/data-types.js +3 -1
  20. package/dist/lib/git-worktree.js +2 -1
  21. package/dist/lib/mcp/data-tools.js +2 -2
  22. package/dist/lib/mcp/server.js +33 -7
  23. package/dist/lib/mcp/tool-annotations.js +3 -0
  24. package/dist/lib/mcp/tools.js +55 -12
  25. package/dist/lib/mcp/work-tools.js +3 -6
  26. package/dist/lib/plugin-host/auth.js +4 -0
  27. package/dist/lib/plugin-host/format.js +3 -0
  28. package/dist/lib/plugin-host/index.js +7 -0
  29. package/dist/lib/plugin-host/organization.js +4 -0
  30. package/dist/lib/plugin-host/projection-utils.js +6 -0
  31. package/dist/lib/plugin-host/registry.js +6 -0
  32. package/dist/lib/plugin-host/request-scope.js +4 -0
  33. package/dist/lib/plugin-host/work.js +4 -0
  34. package/dist/lib/project-root.js +3 -1
  35. package/dist/lib/repository-auth.js +8 -0
  36. package/dist/lib/repository-flags.js +2 -2
  37. package/dist/lib/repository-types.js +6 -1
  38. package/dist/lib/repository-url-prefill.js +4 -0
  39. package/dist/lib/runtime-invoke-context.js +6 -0
  40. package/dist/lib/runtime-rpc.js +14 -0
  41. package/dist/lib/runtime.js +1 -1
  42. package/dist/lib/work-environment-sync.js +1 -2
  43. package/dist/lib/work-get.js +3 -0
  44. package/oclif.manifest.json +244 -126
  45. package/package.json +4 -3
@@ -2,7 +2,7 @@ import { Args } from '@oclif/core';
2
2
  import { AgentiaCommand } from '../../../../lib/agentia-command.js';
3
3
  import { CicdGateway } from '../../../../lib/cicd-gateway.js';
4
4
  import { assertJsonObject, jsonBodyFlags, readJsonBodyFromFileOrStdin, } from '../../../../lib/data-flags.js';
5
- import { dataTemplateDetailEndpoint } from '../../../../lib/data-types.js';
5
+ import { dataTemplateDetailEndpoint, normalizeDataTemplateSavePayload, } from '../../../../lib/data-types.js';
6
6
  export default class DataTemplateSaveDetail extends AgentiaCommand {
7
7
  static args = {
8
8
  id: Args.string({
@@ -26,7 +26,7 @@ export default class DataTemplateSaveDetail extends AgentiaCommand {
26
26
  required: true,
27
27
  stdin: flags.stdin,
28
28
  });
29
- const body = assertJsonObject(raw, 'template detail');
29
+ const body = normalizeDataTemplateSavePayload(assertJsonObject(raw, 'template detail'));
30
30
  const result = await this.withSpinner('Saving data template detail', () => CicdGateway.call({
31
31
  body,
32
32
  endpoint: dataTemplateDetailEndpoint(args.id),
@@ -0,0 +1,21 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { AgentiaCommand } from '../../../../../lib/agentia-command.js';
3
+ import { configureHttps, promptHttpsPayload, readHttpsAuthPayloadFromStdin } from '../../../../../lib/repository-auth.js';
4
+ export default class RepositoryAuthHttpsConfigure extends AgentiaCommand {
5
+ static args = { id: Args.string({ description: 'Copado Git repository ID', required: true }) };
6
+ static description = 'Configure HTTPS authentication. Use a personal access token as the password when required by the provider.';
7
+ static flags = {
8
+ 'input-stdin': Flags.boolean({ description: 'Read the complete HTTPS authentication JSON payload from stdin' }),
9
+ overwrite: Flags.boolean({ description: 'Replace an existing validated HTTPS credential' }),
10
+ };
11
+ async run() {
12
+ const { args, flags } = await this.parse(RepositoryAuthHttpsConfigure);
13
+ if (this.jsonEnabled() && !flags['input-stdin'])
14
+ this.error('--json requires --input-stdin so secrets cannot be prompted.', { exit: 2 });
15
+ const payload = flags['input-stdin'] ? await readHttpsAuthPayloadFromStdin() : await promptHttpsPayload(args.id);
16
+ await this.withSpinner('Configuring HTTPS authentication', () => configureHttps(args.id, payload, flags.overwrite, !flags['input-stdin']));
17
+ this.display(() => this.log(`Configured HTTPS authentication for repository ${args.id}.`));
18
+ return { id: args.id };
19
+ }
20
+ }
21
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1,56 @@
1
+ import { select } from '@inquirer/prompts';
2
+ import { Args, Flags } from '@oclif/core';
3
+ import { AgentiaCommand } from '../../../../../lib/agentia-command.js';
4
+ import { CicdGateway } from '../../../../../lib/cicd-gateway.js';
5
+ import { createLoopbackCallbackServer } from '../../../../../lib/loopback-callback-server.js';
6
+ import { isHeadlessEnvironment, openUrl } from '../../../../../lib/open-browser.js';
7
+ import { repositoryOAuthEndpoint, repositoryOAuthInitEndpoint, repositoryOAuthRepositoriesEndpoint, } from '../../../../../lib/repository-types.js';
8
+ export default class RepositoryAuthOAuthLogin extends AgentiaCommand {
9
+ static args = { id: Args.string({ description: 'Copado Git repository ID', required: true }) };
10
+ static description = 'Authenticate a repository through browser OAuth and select a remote repository';
11
+ static flags = {
12
+ port: Flags.integer({ description: 'Local callback port (default: scan 8080-8090)' }),
13
+ timeout: Flags.integer({ default: 300, description: 'Seconds to wait for browser authentication' }),
14
+ };
15
+ async run() {
16
+ const { args, flags } = await this.parse(RepositoryAuthOAuthLogin);
17
+ if (this.jsonEnabled())
18
+ this.error('OAuth login requires an interactive terminal to select a repository.', { exit: 2 });
19
+ const callback = await createLoopbackCallbackServer({ port: flags.port });
20
+ if (!callback)
21
+ this.error('Could not bind a local OAuth callback port.', { exit: 2 });
22
+ try {
23
+ const init = await this.withSpinner('Generating OAuth authorization URL', () => CicdGateway.call({
24
+ endpoint: repositoryOAuthInitEndpoint(args.id, callback.callbackUrl),
25
+ method: 'POST',
26
+ }));
27
+ if (isHeadlessEnvironment()) {
28
+ this.log(`Complete authentication in your browser by opening this URL:\n${init.authUrl}`);
29
+ }
30
+ else {
31
+ this.log('Complete authentication in the browser window that opens.');
32
+ await openUrl(init.authUrl);
33
+ }
34
+ const result = await callback.waitForCallback({ timeoutMs: flags.timeout * 1000 });
35
+ if (!result.ok)
36
+ this.error(result.errorDescription ?? result.errorCode ?? 'OAuth authentication failed.', { exit: 1 });
37
+ const available = await CicdGateway.call({ endpoint: repositoryOAuthRepositoriesEndpoint(args.id), method: 'GET' });
38
+ const remoteRepositoryId = await select({
39
+ choices: available.repositories.map((repository) => ({ name: repository.name, value: repository.id })),
40
+ message: `Select ${available.provider} repository`,
41
+ });
42
+ await this.withSpinner('Saving selected repository', () => CicdGateway.call({
43
+ body: { remoteRepositoryId },
44
+ endpoint: repositoryOAuthEndpoint(args.id),
45
+ headers: { 'Content-Type': 'application/json' },
46
+ method: 'POST',
47
+ }));
48
+ this.log(`Configured OAuth authentication for repository ${args.id}.`);
49
+ return { id: args.id, remoteRepositoryId };
50
+ }
51
+ finally {
52
+ await callback.close().catch(() => { });
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1,28 @@
1
+ import { select } from '@inquirer/prompts';
2
+ import { Args, Flags } from '@oclif/core';
3
+ import { AgentiaCommand } from '../../../../lib/agentia-command.js';
4
+ export default class RepositoryAuthSetup extends AgentiaCommand {
5
+ static args = { id: Args.string({ description: 'Copado Git repository ID', required: true }) };
6
+ static description = 'Interactively configure repository authentication; OAuth is selected by default';
7
+ static flags = { overwrite: Flags.boolean({ description: 'Replace an existing HTTPS or SSH credential' }) };
8
+ async run() {
9
+ const { args, flags } = await this.parse(RepositoryAuthSetup);
10
+ if (this.jsonEnabled())
11
+ this.error('Repository auth setup requires an interactive terminal.', { exit: 2 });
12
+ const authType = await select({
13
+ choices: [
14
+ { name: 'OAuth (recommended)', value: 'oauth' },
15
+ { name: 'HTTPS', value: 'https' },
16
+ { name: 'SSH', value: 'ssh' },
17
+ ],
18
+ default: 'oauth',
19
+ message: 'Authentication type',
20
+ });
21
+ const command = authType === 'oauth'
22
+ ? 'cicd:repository:auth:oauth:login'
23
+ : `cicd:repository:auth:${authType}:configure`;
24
+ const argv = [args.id, ...(flags.overwrite && authType !== 'oauth' ? ['--overwrite'] : [])];
25
+ await this.config.runCommand(command, argv);
26
+ }
27
+ }
28
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1,22 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { AgentiaCommand } from '../../../../../lib/agentia-command.js';
3
+ import { configureSsh, promptConnection } from '../../../../../lib/repository-auth.js';
4
+ export default class RepositoryAuthSshConfigure extends AgentiaCommand {
5
+ static args = { id: Args.string({ description: 'Copado Git repository ID', required: true }) };
6
+ static description = 'Generate SSH authentication and a public key. Add the key to your Git provider as a deploy, access, or SSH key.';
7
+ static flags = {
8
+ overwrite: Flags.boolean({ description: 'Replace an existing SSH key after confirmation' }),
9
+ };
10
+ async run() {
11
+ const { args, flags } = await this.parse(RepositoryAuthSshConfigure);
12
+ if (this.jsonEnabled())
13
+ this.error('SSH authentication requires an interactive terminal to collect connection details.', { exit: 2 });
14
+ const payload = await promptConnection(args.id, 'SSH');
15
+ const publicKey = await this.withSpinner('Generating SSH key', () => configureSsh(args.id, payload, flags.overwrite));
16
+ this.display(() => {
17
+ this.log(`Add this public key to your Git provider's repository deploy/access/SSH keys, then retain it for this repository:\n${publicKey}`);
18
+ });
19
+ return { id: args.id, publicKey };
20
+ }
21
+ }
22
+ //# sourceMappingURL=configure.js.map
@@ -10,9 +10,9 @@ export default class RepositoryCreate extends AgentiaCommand {
10
10
  required: true,
11
11
  }),
12
12
  };
13
- static description = 'Create a Copado Git repository';
13
+ static description = 'Create a Copado Git repository shell';
14
14
  static examples = [
15
- '<%= config.bin %> <%= command.id %> my-repo --source-format DX --provider Github --uri https://github.com/org/repo.git',
15
+ '<%= config.bin %> <%= command.id %> my-repo --source-format DX --provider Github',
16
16
  '<%= config.bin %> <%= command.id %> my-repo --source-format DX --provider GitLab --json',
17
17
  ];
18
18
  static flags = repositoryCreateFlags;
@@ -1,4 +1,5 @@
1
- import { Args } from '@oclif/core';
1
+ import { confirm } from '@inquirer/prompts';
2
+ import { Args, Flags } from '@oclif/core';
2
3
  import { AgentiaCommand } from '../../../lib/agentia-command.js';
3
4
  import { CicdGateway } from '../../../lib/cicd-gateway.js';
4
5
  import { repositoryResetEndpoint } from '../../../lib/repository-types.js';
@@ -9,13 +10,24 @@ export default class RepositoryReset extends AgentiaCommand {
9
10
  required: true,
10
11
  }),
11
12
  };
12
- static description = 'Reset a Copado Git repository';
13
+ static description = 'Reset a Copado Git repository and its authentication configuration';
13
14
  static examples = [
14
- '<%= config.bin %> <%= command.id %> repo-001',
15
- '<%= config.bin %> <%= command.id %> repo-001 --json',
15
+ '<%= config.bin %> <%= command.id %> repo-001 --yes',
16
+ '<%= config.bin %> <%= command.id %> repo-001 --yes --json',
16
17
  ];
18
+ static flags = {
19
+ yes: Flags.boolean({ char: 'y', default: false, description: 'Confirm reset without prompting' }),
20
+ };
17
21
  async run() {
18
- const { args } = await this.parse(RepositoryReset);
22
+ const { args, flags } = await this.parse(RepositoryReset);
23
+ if (!flags.yes) {
24
+ if (this.jsonEnabled() || process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
25
+ this.error('Repository reset requires confirmation. Pass --yes for headless or JSON execution.', { exit: 2 });
26
+ }
27
+ if (!(await confirm({ default: false, message: `Reset repository ${args.id} and remove its authentication configuration?` }))) {
28
+ this.error('Repository reset canceled.', { exit: 1 });
29
+ }
30
+ }
19
31
  await this.withSpinner('Resetting repository', () => CicdGateway.call({
20
32
  endpoint: repositoryResetEndpoint(args.id),
21
33
  method: 'PUT',
@@ -2,7 +2,7 @@ import { Args } from '@oclif/core';
2
2
  import { AgentiaCommand } from '../../../lib/agentia-command.js';
3
3
  import { CicdGateway } from '../../../lib/cicd-gateway.js';
4
4
  import { buildRepositoryMutationPayload, collectRepositoryMutationFlagKeys, REPOSITORY_STRING_VALIDATION_KEYS, repositoryUpdateFlags, validateNonEmptyStringFields, validateRepositoryMutationFlags, } from '../../../lib/repository-flags.js';
5
- import { normalizeRepository, repositoryEndpoint, withRequiredRepositoryFields, } from '../../../lib/repository-types.js';
5
+ import { normalizeRepository, repositoryEndpoint } from '../../../lib/repository-types.js';
6
6
  export default class RepositoryUpdate extends AgentiaCommand {
7
7
  static args = {
8
8
  id: Args.string({
@@ -13,7 +13,7 @@ export default class RepositoryUpdate extends AgentiaCommand {
13
13
  static description = 'Update a Copado Git repository by ID';
14
14
  static examples = [
15
15
  '<%= config.bin %> <%= command.id %> repo-001 --uri https://github.com/org/repo.git',
16
- '<%= config.bin %> <%= command.id %> repo-001 --clear-password',
16
+ '<%= config.bin %> <%= command.id %> repo-001 --clear-uri',
17
17
  '<%= config.bin %> <%= command.id %> repo-001 --provider GitLab --json',
18
18
  ];
19
19
  static flags = repositoryUpdateFlags;
@@ -25,7 +25,7 @@ export default class RepositoryUpdate extends AgentiaCommand {
25
25
  exit: 2,
26
26
  suggestions: [
27
27
  `${this.config.bin} cicd repository update ${args.id} --uri <uri>`,
28
- `${this.config.bin} cicd repository update ${args.id} --clear-password`,
28
+ `${this.config.bin} cicd repository update ${args.id} --clear-uri`,
29
29
  ],
30
30
  });
31
31
  }
@@ -33,18 +33,10 @@ export default class RepositoryUpdate extends AgentiaCommand {
33
33
  if (flagError !== undefined) {
34
34
  this.error(flagError, { exit: 2 });
35
35
  }
36
- const currentRepository = await this.withSpinner('Fetching repository', () => CicdGateway.call({
37
- endpoint: repositoryEndpoint(args.id),
38
- method: 'GET',
39
- }));
40
- const current = normalizeRepository(currentRepository);
41
- const payload = withRequiredRepositoryFields(buildRepositoryMutationPayload(flags), current);
36
+ const payload = buildRepositoryMutationPayload(flags);
42
37
  const validationError = validateNonEmptyStringFields(payload, REPOSITORY_STRING_VALIDATION_KEYS);
43
- if (payload.name === undefined ||
44
- payload.sourceFormat === undefined ||
45
- payload.provider === undefined ||
46
- validationError !== undefined) {
47
- this.error('Repository name, source format, and provider are required to update a repository.', { exit: 2 });
38
+ if (validationError !== undefined) {
39
+ this.error(validationError, { exit: 2 });
48
40
  }
49
41
  const response = await this.withSpinner('Updating repository', () => CicdGateway.call({
50
42
  body: payload,
@@ -1,5 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { AgentiaCommand } from '../../../lib/agentia-command.js';
3
+ import { commandStdioCaptureOptions, withCapturedCommandOutput } from '../../../lib/command-output-buffer.js';
3
4
  import { assertEnvironmentSyncDestOrgSelector, formatWorkEnvironmentSyncResult, performWorkEnvironmentSync, WorkEnvironmentSyncConflictError, WorkEnvironmentSyncValidationError, } from '../../../lib/work-environment-sync.js';
4
5
  export default class WorkEnvironmentSync extends AgentiaCommand {
5
6
  static description = [
@@ -28,7 +29,7 @@ export default class WorkEnvironmentSync extends AgentiaCommand {
28
29
  }),
29
30
  continue: Flags.boolean({
30
31
  default: false,
31
- description: 'Commit resolved merge conflicts and finish the remaining environment-sync git steps.',
32
+ description: 'Finish after merge conflicts. If the merge is still in progress (conflicts fixed and staged, but not committed), commits it; if you already committed, continues from there through the remaining git steps.',
32
33
  exclusive: ['abort'],
33
34
  }),
34
35
  'destination-credential': Flags.string({
@@ -51,70 +52,74 @@ export default class WorkEnvironmentSync extends AgentiaCommand {
51
52
  };
52
53
  async run() {
53
54
  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
- });
55
+ return withCapturedCommandOutput(async () => {
56
+ const startedAt = Date.now();
57
+ try {
58
+ await assertEnvironmentSyncDestOrgSelector({
59
+ abort: flags.abort === true,
60
+ continue: flags.continue === true,
61
+ destinationEnvironment: flags['destination-environment'],
62
+ devOrgBranch: flags['dev-org-branch'],
63
+ promotion: flags.promotion,
64
+ });
65
+ const result = await performWorkEnvironmentSync({
66
+ abort: flags.abort === true,
67
+ cliBin: this.config.bin,
68
+ continue: flags.continue === true,
69
+ hooks: {
70
+ log: (message) => {
71
+ this.display(() => {
72
+ this.log(message);
73
+ });
74
+ },
75
+ withSpinner: (message, action) => this.withSpinner(message, action),
71
76
  },
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 });
77
+ json: this.jsonEnabled(),
78
+ ...(flags['destination-credential'] === undefined
79
+ ? {}
80
+ : { destinationCredentialId: flags['destination-credential'] }),
81
+ ...(flags['destination-environment'] === undefined
82
+ ? {}
83
+ : { destinationEnvironment: flags['destination-environment'] }),
84
+ ...(flags['dev-org-branch'] === undefined ? {} : { devOrgBranch: flags['dev-org-branch'] }),
85
+ ...(flags['pipeline-id'] === undefined ? {} : { pipelineId: flags['pipeline-id'] }),
86
+ ...(flags.promotion === undefined ? {} : { promotion: flags.promotion }),
87
+ });
88
+ this.display(() => {
89
+ const formatted = formatWorkEnvironmentSyncResult(result);
90
+ if (formatted.length > 0) {
91
+ this.log(formatted);
92
+ }
93
+ });
94
+ this.logElapsed(startedAt);
95
+ return result;
96
+ }
97
+ catch (error) {
98
+ if (error instanceof WorkEnvironmentSyncConflictError) {
99
+ const { suggestions } = error;
100
+ const suggestionOptions = suggestions === undefined ? {} : { suggestions };
101
+ if (this.jsonEnabled()) {
102
+ const message = error.gitStatus === undefined || error.gitStatus.length === 0
103
+ ? error.message
104
+ : `${error.message}\n\n${error.gitStatus}`;
105
+ this.error(message, { exit: error.exit, ...suggestionOptions });
106
+ }
107
+ this.error(error.message, { exit: false, ...suggestionOptions });
108
+ if (error.gitStatus !== undefined && error.gitStatus.length > 0) {
109
+ this.log('');
110
+ this.log(error.gitStatus);
111
+ }
112
+ this.exit(error.exit);
102
113
  }
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);
114
+ if (error instanceof WorkEnvironmentSyncValidationError) {
115
+ this.error(error.message, {
116
+ exit: error.exit,
117
+ ...(error.suggestions === undefined ? {} : { suggestions: error.suggestions }),
118
+ });
107
119
  }
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
- });
120
+ throw error;
115
121
  }
116
- throw error;
117
- }
122
+ }, commandStdioCaptureOptions(this));
118
123
  }
119
124
  }
120
125
  //# sourceMappingURL=environment-sync.js.map
@@ -1,9 +1,9 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { AgentiaCommand } from '../../../lib/agentia-command.js';
3
- import { CicdGateway } from '../../../lib/cicd-gateway.js';
4
- import { resolveWorkItemId, WorkActiveResolutionError } from '../../../lib/work-active.js';
3
+ import { WorkActiveResolutionError } from '../../../lib/work-active.js';
4
+ import { fetchUserStory } from '../../../lib/work-get.js';
5
5
  import { formatUserStoriesAheadBehindLines, reportUserStoriesAheadBehindForStory, } from '../../../lib/work-submit-ahead-behind.js';
6
- import { formatUserStoryDetail, workEndpoint, } from '../../../lib/work-types.js';
6
+ import { formatUserStoryDetail, } from '../../../lib/work-types.js';
7
7
  export default class WorkGet extends AgentiaCommand {
8
8
  static args = {
9
9
  id: Args.string({
@@ -27,9 +27,9 @@ export default class WorkGet extends AgentiaCommand {
27
27
  };
28
28
  async run() {
29
29
  const { args, flags } = await this.parse(WorkGet);
30
- let workItemId;
30
+ let userStory;
31
31
  try {
32
- workItemId = await resolveWorkItemId(args.id);
32
+ userStory = await this.withSpinner('Fetching user story', () => fetchUserStory(args.id));
33
33
  }
34
34
  catch (error) {
35
35
  if (error instanceof WorkActiveResolutionError) {
@@ -40,10 +40,6 @@ export default class WorkGet extends AgentiaCommand {
40
40
  }
41
41
  throw error;
42
42
  }
43
- const userStory = await this.withSpinner('Fetching user story', () => CicdGateway.call({
44
- endpoint: workEndpoint(workItemId),
45
- method: 'GET',
46
- }));
47
43
  let userStoriesAheadBehind;
48
44
  if (flags['user-stories-ahead-behind']) {
49
45
  userStoriesAheadBehind = await reportUserStoriesAheadBehindForStory(userStory, {
@@ -20,7 +20,7 @@ export default class WorkSubmit extends AgentiaCommand {
20
20
  'pushes and registers commits when the branch is ahead of origin, then calls headless work/submit',
21
21
  '(Promote Action with validate=true).',
22
22
  'When the last pending related job template name contains Commit, waits up to 5 minutes for that job before submitting.',
23
- 'Before submit or promote, counts user stories ahead and behind between the source environment and the next pipeline environment, and warns when stories behind might cause promotion conflicts.',
23
+ 'Before submit or promote, counts user stories ahead and behind between the source environment and the next pipeline environment, and warns when stories behind might cause promotion conflicts (advising `agentia cicd work environment-sync`).',
24
24
  'With --done or --deploy, matches Copado UI Submit when promoting (validate=false / headless work/done).',
25
25
  'cicd work done is a compatibility alias of this command with --done.',
26
26
  ].join(' ');
@@ -0,0 +1,26 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { AGENTIA_RUNTIME_DEFAULT_IDLE_MS, serveRuntimeRpc } from '../lib/runtime-rpc.js';
3
+ /** Long-lived stdio NDJSON server so the IDE can reuse one Agentia process. */
4
+ export default class Runtime extends Command {
5
+ static description = 'Start a stdio NDJSON runtime for in-process Agentia command invokes';
6
+ static enableJsonFlag = false;
7
+ static flags = {
8
+ 'idle-ms': Flags.integer({
9
+ default: AGENTIA_RUNTIME_DEFAULT_IDLE_MS,
10
+ description: 'Exit after this many idle milliseconds with no in-flight requests',
11
+ min: 1000,
12
+ }),
13
+ };
14
+ static hidden = true;
15
+ async run() {
16
+ const { flags: { 'idle-ms': idleMs } } = await this.parse(Runtime);
17
+ const { config } = this;
18
+ await serveRuntimeRpc({
19
+ cliVersion: config.version,
20
+ finder: config,
21
+ idleMs,
22
+ runCommand: (id, argv) => config.runCommand(id, argv),
23
+ });
24
+ }
25
+ }
26
+ //# sourceMappingURL=runtime.js.map
@@ -2,7 +2,7 @@ import { consumeIdeCommandError, publishIdeCommandCompleted } from '../../lib/id
2
2
  const hook = async function ({ config, error, id }) {
3
3
  try {
4
4
  const rememberedError = consumeIdeCommandError();
5
- if (id.trim().length === 0) {
5
+ if (id.trim().length === 0 || id === 'runtime') {
6
6
  return;
7
7
  }
8
8
  await publishIdeCommandCompleted({
@@ -0,0 +1,6 @@
1
+ import { createPluginHost, registerPluginHost } from '../../lib/plugin-host/index.js';
2
+ const hook = async function () {
3
+ registerPluginHost(createPluginHost({ cliVersion: this.config.version }));
4
+ };
5
+ export default hook;
6
+ //# sourceMappingURL=plugin-host.js.map
@@ -3,6 +3,7 @@ import { format } from 'node:util';
3
3
  import { createTransactionId, runWithGatewayTransactionContext } from './cicd-gateway.js';
4
4
  import { isCommandOutputCaptureActive } from './command-output-buffer.js';
5
5
  import { rememberIdeCommandError } from './ide-command-events.js';
6
+ const TRANSACTION_ID_REPORTED = Symbol('agentiaTransactionIdReported');
6
7
  export function elapsedSecondsSince(startedAtMs, nowMs = Date.now()) {
7
8
  return Math.max(0, Math.floor((nowMs - startedAtMs) / 1000));
8
9
  }
@@ -17,6 +18,10 @@ export class AgentiaCommand extends Command {
17
18
  this.commandTransactionId = transactionId;
18
19
  return runWithGatewayTransactionContext({ origin: 'cli', referer: this.id, transactionId }, () => super._run());
19
20
  }
21
+ async catch(error) {
22
+ this.reportHumanErrorTransactionId(error);
23
+ return super.catch(error);
24
+ }
20
25
  display(render) {
21
26
  if (!this.jsonEnabled() || isCommandOutputCaptureActive()) {
22
27
  render();
@@ -64,5 +69,16 @@ export class AgentiaCommand extends Command {
64
69
  throw error;
65
70
  }
66
71
  }
72
+ reportHumanErrorTransactionId(error) {
73
+ if (this.jsonEnabled() || this.commandTransactionId === undefined) {
74
+ return;
75
+ }
76
+ const reportedError = error;
77
+ if (reportedError[TRANSACTION_ID_REPORTED]) {
78
+ return;
79
+ }
80
+ reportedError[TRANSACTION_ID_REPORTED] = true;
81
+ this.logToStderr(`Transaction ID: ${this.commandTransactionId}`);
82
+ }
67
83
  }
68
84
  //# sourceMappingURL=agentia-command.js.map
@@ -3,14 +3,16 @@ const AGENTIA_MCP_ENTRY = modules["cursor-mcp-config.js"]["AGENTIA_MCP_ENTRY"]
3
3
  const AGENTIA_MCP_SERVER_NAME = modules["cursor-mcp-config.js"]["AGENTIA_MCP_SERVER_NAME"]
4
4
  const detectCursorInstall = modules["cursor-mcp-config.js"]["detectCursorInstall"]
5
5
  const findAgentiaMcpServer = modules["cursor-mcp-config.js"]["findAgentiaMcpServer"]
6
+ const findCurrentAgentiaMcpServer = modules["cursor-mcp-config.js"]["findCurrentAgentiaMcpServer"]
6
7
  const getCursorMcpStatus = modules["cursor-mcp-config.js"]["getCursorMcpStatus"]
7
8
  const globalCursorMcpPath = modules["cursor-mcp-config.js"]["globalCursorMcpPath"]
8
9
  const hasConflictingAgentiaEntry = modules["cursor-mcp-config.js"]["hasConflictingAgentiaEntry"]
9
10
  const inspectCursorMcpStatus = modules["cursor-mcp-config.js"]["inspectCursorMcpStatus"]
10
11
  const isAgentiaMcpEntry = modules["cursor-mcp-config.js"]["isAgentiaMcpEntry"]
12
+ const isCurrentAgentiaMcpEntry = modules["cursor-mcp-config.js"]["isCurrentAgentiaMcpEntry"]
11
13
  const localCursorMcpPath = modules["cursor-mcp-config.js"]["localCursorMcpPath"]
12
14
  const readCursorMcpDocument = modules["cursor-mcp-config.js"]["readCursorMcpDocument"]
13
15
  const resetCursorMcpDepsForTests = modules["cursor-mcp-config.js"]["resetCursorMcpDepsForTests"]
14
16
  const setCursorMcpDepsForTests = modules["cursor-mcp-config.js"]["setCursorMcpDepsForTests"]
15
17
  const upsertAgentiaCursorMcp = modules["cursor-mcp-config.js"]["upsertAgentiaCursorMcp"]
16
- export {AGENTIA_MCP_ENTRY, AGENTIA_MCP_SERVER_NAME, detectCursorInstall, findAgentiaMcpServer, getCursorMcpStatus, globalCursorMcpPath, hasConflictingAgentiaEntry, inspectCursorMcpStatus, isAgentiaMcpEntry, localCursorMcpPath, readCursorMcpDocument, resetCursorMcpDepsForTests, setCursorMcpDepsForTests, upsertAgentiaCursorMcp};
18
+ export {AGENTIA_MCP_ENTRY, AGENTIA_MCP_SERVER_NAME, detectCursorInstall, findAgentiaMcpServer, findCurrentAgentiaMcpServer, getCursorMcpStatus, globalCursorMcpPath, hasConflictingAgentiaEntry, inspectCursorMcpStatus, isAgentiaMcpEntry, isCurrentAgentiaMcpEntry, localCursorMcpPath, readCursorMcpDocument, resetCursorMcpDepsForTests, setCursorMcpDepsForTests, upsertAgentiaCursorMcp};
@@ -1,5 +1,6 @@
1
1
  import {modules} from './runtime.js';
2
2
  const DATA_TEMPLATES_BASE = modules["data-types.js"]["DATA_TEMPLATES_BASE"]
3
+ const DEFAULT_DATA_TEMPLATE_QUERY_LIMIT = modules["data-types.js"]["DEFAULT_DATA_TEMPLATE_QUERY_LIMIT"]
3
4
  const advancedFilterEndpoint = modules["data-types.js"]["advancedFilterEndpoint"]
4
5
  const advancedFiltersEndpoint = modules["data-types.js"]["advancedFiltersEndpoint"]
5
6
  const dataTemplateConvertOldEndpoint = modules["data-types.js"]["dataTemplateConvertOldEndpoint"]
@@ -21,10 +22,11 @@ const formatDatasetFileTable = modules["data-types.js"]["formatDatasetFileTable"
21
22
  const formatRecordMatchingFormulaTable = modules["data-types.js"]["formatRecordMatchingFormulaTable"]
22
23
  const formatSyncResultTable = modules["data-types.js"]["formatSyncResultTable"]
23
24
  const formatUserStoryDataCommitTable = modules["data-types.js"]["formatUserStoryDataCommitTable"]
25
+ const normalizeDataTemplateSavePayload = modules["data-types.js"]["normalizeDataTemplateSavePayload"]
24
26
  const normalizeDataTemplates = modules["data-types.js"]["normalizeDataTemplates"]
25
27
  const recordMatchingFormulaEndpoint = modules["data-types.js"]["recordMatchingFormulaEndpoint"]
26
28
  const recordMatchingFormulasByObjectEndpoint = modules["data-types.js"]["recordMatchingFormulasByObjectEndpoint"]
27
29
  const sobjectFieldsEndpoint = modules["data-types.js"]["sobjectFieldsEndpoint"]
28
30
  const sobjectRelationshipsEndpoint = modules["data-types.js"]["sobjectRelationshipsEndpoint"]
29
31
  const userStoryDataCommitsEndpoint = modules["data-types.js"]["userStoryDataCommitsEndpoint"]
30
- export {DATA_TEMPLATES_BASE, advancedFilterEndpoint, advancedFiltersEndpoint, dataTemplateConvertOldEndpoint, dataTemplateDetailEndpoint, dataTemplateEndpoint, dataTemplateRecordMatchingFormulasEndpoint, dataTemplateUpdatesEndpoint, dataTemplatesCommitEndpoint, dataTemplatesListEndpoint, dataTemplatesRecordsEndpoint, dataTemplatesRecordsMatchEndpoint, dataTemplatesSyncEndpoint, datasetFileContentEndpoint, datasetFilesEndpoint, formatAdvancedFilterTable, formatDataRecordTable, formatDataTemplateTable, formatDatasetFileTable, formatRecordMatchingFormulaTable, formatSyncResultTable, formatUserStoryDataCommitTable, normalizeDataTemplates, recordMatchingFormulaEndpoint, recordMatchingFormulasByObjectEndpoint, sobjectFieldsEndpoint, sobjectRelationshipsEndpoint, userStoryDataCommitsEndpoint};
32
+ export {DATA_TEMPLATES_BASE, DEFAULT_DATA_TEMPLATE_QUERY_LIMIT, advancedFilterEndpoint, advancedFiltersEndpoint, dataTemplateConvertOldEndpoint, dataTemplateDetailEndpoint, dataTemplateEndpoint, dataTemplateRecordMatchingFormulasEndpoint, dataTemplateUpdatesEndpoint, dataTemplatesCommitEndpoint, dataTemplatesListEndpoint, dataTemplatesRecordsEndpoint, dataTemplatesRecordsMatchEndpoint, dataTemplatesSyncEndpoint, datasetFileContentEndpoint, datasetFilesEndpoint, formatAdvancedFilterTable, formatDataRecordTable, formatDataTemplateTable, formatDatasetFileTable, formatRecordMatchingFormulaTable, formatSyncResultTable, formatUserStoryDataCommitTable, normalizeDataTemplateSavePayload, normalizeDataTemplates, recordMatchingFormulaEndpoint, recordMatchingFormulasByObjectEndpoint, sobjectFieldsEndpoint, sobjectRelationshipsEndpoint, userStoryDataCommitsEndpoint};
@@ -13,6 +13,7 @@ const createAndCheckoutBranch = modules["git-worktree.js"]["createAndCheckoutBra
13
13
  const fetchAndCheckoutBranch = modules["git-worktree.js"]["fetchAndCheckoutBranch"]
14
14
  const fetchOriginBranch = modules["git-worktree.js"]["fetchOriginBranch"]
15
15
  const fetchOriginBranchIfExists = modules["git-worktree.js"]["fetchOriginBranchIfExists"]
16
+ const formatGitExecError = modules["git-worktree.js"]["formatGitExecError"]
16
17
  const getCommitsNameStatus = modules["git-worktree.js"]["getCommitsNameStatus"]
17
18
  const getCurrentBranch = modules["git-worktree.js"]["getCurrentBranch"]
18
19
  const getFileAtRef = modules["git-worktree.js"]["getFileAtRef"]
@@ -48,4 +49,4 @@ const restoreFeatureBranch = modules["git-worktree.js"]["restoreFeatureBranch"]
48
49
  const restoreHeadPaths = modules["git-worktree.js"]["restoreHeadPaths"]
49
50
  const setGitExecForTests = modules["git-worktree.js"]["setGitExecForTests"]
50
51
  const stageAndCommit = modules["git-worktree.js"]["stageAndCommit"]
51
- export {NAME_STATUS_PRETTY_FORMAT, abortMerge, checkoutBranch, checkoutFeatureBranch, checkoutPreferredBranch, commitAllTrackedChanges, commitAllowEmpty, commitCurrentMerge, commitStagedChanges, countCommitsSince, createAndCheckoutBranch, fetchAndCheckoutBranch, fetchOriginBranch, fetchOriginBranchIfExists, getCommitsNameStatus, getCurrentBranch, getFileAtRef, getGitRepoRoot, getHeadCommitId, getHumanGitStatus, getMergeBase, getRemoteUrl, hasUnpushedCommits, isAncestor, isInsideGitWorkTree, isMergeInProgress, isSafeGitBranchName, listDiffNamesVsHead, listPendingChanges, listStagedChanges, listUnmergedFiles, localBranchExists, mergeFfOnly, mergeNoFf, mergeNoFfNoCommit, mergeTheirsIgnoreSpaceChange, originBranchRef, originFetchRefspec, parseNameOnly, parsePorcelainPaths, pullOriginBranch, pushOriginBranch, pushOriginBranches, resetGitExecForTests, resetHardHead, restoreFeatureBranch, restoreHeadPaths, setGitExecForTests, stageAndCommit};
52
+ export {NAME_STATUS_PRETTY_FORMAT, abortMerge, checkoutBranch, checkoutFeatureBranch, checkoutPreferredBranch, commitAllTrackedChanges, commitAllowEmpty, commitCurrentMerge, commitStagedChanges, countCommitsSince, createAndCheckoutBranch, fetchAndCheckoutBranch, fetchOriginBranch, fetchOriginBranchIfExists, formatGitExecError, getCommitsNameStatus, getCurrentBranch, getFileAtRef, getGitRepoRoot, getHeadCommitId, getHumanGitStatus, getMergeBase, getRemoteUrl, hasUnpushedCommits, isAncestor, isInsideGitWorkTree, isMergeInProgress, isSafeGitBranchName, listDiffNamesVsHead, listPendingChanges, listStagedChanges, listUnmergedFiles, localBranchExists, mergeFfOnly, mergeNoFf, mergeNoFfNoCommit, mergeTheirsIgnoreSpaceChange, originBranchRef, originFetchRefspec, parseNameOnly, parsePorcelainPaths, pullOriginBranch, pushOriginBranch, pushOriginBranches, resetGitExecForTests, resetHardHead, restoreFeatureBranch, restoreHeadPaths, setGitExecForTests, stageAndCommit};
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { assertRequired } from '../assert.js';
3
3
  import { CicdGateway } from '../cicd-gateway.js';
4
- import { advancedFilterEndpoint, advancedFiltersEndpoint, DATA_TEMPLATES_BASE, datasetFileContentEndpoint, datasetFilesEndpoint, dataTemplateConvertOldEndpoint, dataTemplateDetailEndpoint, dataTemplateEndpoint, dataTemplateRecordMatchingFormulasEndpoint, dataTemplatesCommitEndpoint, dataTemplatesListEndpoint, dataTemplatesRecordsEndpoint, dataTemplatesRecordsMatchEndpoint, dataTemplatesSyncEndpoint, dataTemplateUpdatesEndpoint, normalizeDataTemplates, recordMatchingFormulaEndpoint, recordMatchingFormulasByObjectEndpoint, sobjectFieldsEndpoint, sobjectRelationshipsEndpoint, userStoryDataCommitsEndpoint, } from '../data-types.js';
4
+ import { advancedFilterEndpoint, advancedFiltersEndpoint, DATA_TEMPLATES_BASE, datasetFileContentEndpoint, datasetFilesEndpoint, dataTemplateConvertOldEndpoint, dataTemplateDetailEndpoint, dataTemplateEndpoint, dataTemplateRecordMatchingFormulasEndpoint, dataTemplatesCommitEndpoint, dataTemplatesListEndpoint, dataTemplatesRecordsEndpoint, dataTemplatesRecordsMatchEndpoint, dataTemplatesSyncEndpoint, dataTemplateUpdatesEndpoint, normalizeDataTemplateSavePayload, normalizeDataTemplates, recordMatchingFormulaEndpoint, recordMatchingFormulasByObjectEndpoint, sobjectFieldsEndpoint, sobjectRelationshipsEndpoint, userStoryDataCommitsEndpoint, } from '../data-types.js';
5
5
  import { safelyHandleTool } from './result.js';
6
6
  async function dataTemplateList(args = {}) {
7
7
  const response = await CicdGateway.call({
@@ -55,7 +55,7 @@ async function dataTemplateSaveDetail(args) {
55
55
  throw new Error('Requires `body`.');
56
56
  }
57
57
  const result = await CicdGateway.call({
58
- body: args.body,
58
+ body: normalizeDataTemplateSavePayload(args.body),
59
59
  endpoint: dataTemplateDetailEndpoint(id),
60
60
  headers: { 'Content-Type': 'application/json' },
61
61
  method: 'PUT',