@copado/agentia-cli 1.0.0 → 1.1.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/CHANGELOG.md +17 -0
- package/README.md +481 -278
- package/dist/commands/cicd/cloud/commit.js +7 -2
- package/dist/commands/cicd/extension/create.js +53 -0
- package/dist/commands/cicd/extension/install.js +127 -0
- package/dist/commands/cicd/extension/package.js +35 -0
- package/dist/commands/cicd/extension/publish.js +61 -0
- package/dist/commands/cicd/extension/uninstall.js +99 -0
- package/dist/commands/cicd/extension/validate.js +47 -0
- package/dist/commands/cicd/work/get.js +18 -2
- package/dist/commands/setup/skills/create.js +1 -1
- package/dist/commands/setup/skills/update.js +1 -1
- package/dist/commands/setup.js +1 -1
- package/dist/lib/extension/assets.js +4 -0
- package/dist/lib/extension/create.js +3 -0
- package/dist/lib/extension/git.js +17 -0
- package/dist/lib/extension/index.js +27 -0
- package/dist/lib/extension/install.js +4 -0
- package/dist/lib/extension/manifest.js +21 -0
- package/dist/lib/extension/package.js +3 -0
- package/dist/lib/extension/paths.js +5 -0
- package/dist/lib/extension/publish.js +3 -0
- package/dist/lib/extension/sf-deploy.js +8 -0
- package/dist/lib/extension/source.js +7 -0
- package/dist/lib/extension/uninstall.js +3 -0
- package/dist/lib/function-types.js +3 -1
- package/dist/lib/mcp/work-tools.js +19 -6
- package/dist/lib/project-setup.js +5 -4
- package/dist/lib/runtime.js +1 -1
- package/dist/lib/setup-skills.js +4 -1
- package/dist/lib/setup-templates.js +1 -4
- package/dist/lib/template-types.js +3 -1
- package/dist/lib/work-committed-metadata.js +4 -0
- package/dist/lib/work-get.js +2 -1
- package/oclif.manifest.json +414 -7
- package/package.json +1 -1
- package/templates/rules/cursor/agentia.mdc +29 -0
- package/templates/skills/agentia/SKILL.md +29 -0
- package/templates/skills/agentia-cicd/SKILL.md +3 -1
- package/templates/skills/agentia-cicd/references/extensions.md +49 -0
- package/templates/skills/agentia-cicd/references/work.md +6 -4
- package/templates/AGENTS.md +0 -24
|
@@ -90,7 +90,10 @@ async function commitInputFromFlags(flags) {
|
|
|
90
90
|
return jsonInputFromFlags(flags);
|
|
91
91
|
}
|
|
92
92
|
if (!simpleMode) {
|
|
93
|
-
throw new WorkCommitValidationError('Requires simple metadata flags such as --metadata-name and --metadata-
|
|
93
|
+
throw new WorkCommitValidationError('Requires simple metadata flags such as --metadata-name, --metadata-type, and --metadata-category, or JSON input with --file/--stdin.');
|
|
94
|
+
}
|
|
95
|
+
if (flags['metadata-category'] === undefined || flags['metadata-category'].trim().length === 0) {
|
|
96
|
+
throw new WorkCommitValidationError('--metadata-category is required.');
|
|
94
97
|
}
|
|
95
98
|
return simpleInputFromFlags(flags);
|
|
96
99
|
}
|
|
@@ -105,7 +108,9 @@ export default class CloudCommit extends AgentiaCommand {
|
|
|
105
108
|
'Create a Copado cloud metadata commit for a user story.',
|
|
106
109
|
'Preflights the story, submits POST /release/commit, and polls the job monitor by default.',
|
|
107
110
|
'For local Git work, use git add/commit then `cicd work publish`. Permission and full-metadata selections belong on `cicd work publish`.',
|
|
108
|
-
|
|
111
|
+
'',
|
|
112
|
+
'NOTE: Do not use this command along with local development (git operations, cicd work push/submit).',
|
|
113
|
+
].join('\n');
|
|
109
114
|
static examples = [
|
|
110
115
|
'<%= config.bin %> <%= command.id %> --message "Commit AccountService" --metadata-name AccountService --metadata-type ApexClass --metadata-category SFDX --action Add',
|
|
111
116
|
'<%= config.bin %> <%= command.id %> a07XXXXXXXXXXXXXXX --message "Update Aura files" --metadata-name accountEditor --metadata-type AuraDefinitionBundle --metadata-category SFDX --action SelectiveCommit --selection accountEditor/accountEditor.cmp --selection accountEditor/accountEditorController.js',
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { AgentiaCommand } from '../../../lib/agentia-command.js';
|
|
3
|
+
import { createExtension } from '../../../lib/extension/create.js';
|
|
4
|
+
export default class ExtensionCreate extends AgentiaCommand {
|
|
5
|
+
static description = 'Create or refresh a Copado extension manifest at copado/extension_manifest.json (scans copado/functions and copado/templates)';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %> --name my-extension --version 1.0.0',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --name my-extension --overwrite --json',
|
|
9
|
+
];
|
|
10
|
+
static experimental = true;
|
|
11
|
+
static flags = {
|
|
12
|
+
name: Flags.string({
|
|
13
|
+
char: 'n',
|
|
14
|
+
description: 'Extension name (alphanumeric, underscore, hyphen)',
|
|
15
|
+
required: true,
|
|
16
|
+
}),
|
|
17
|
+
overwrite: Flags.boolean({
|
|
18
|
+
description: 'Overwrite an existing extension_manifest.json',
|
|
19
|
+
default: false,
|
|
20
|
+
}),
|
|
21
|
+
version: Flags.string({
|
|
22
|
+
description: 'Initial semver version written to the manifest (X.Y.Z)',
|
|
23
|
+
required: false,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(ExtensionCreate);
|
|
28
|
+
let result;
|
|
29
|
+
try {
|
|
30
|
+
result = await createExtension({
|
|
31
|
+
name: flags.name,
|
|
32
|
+
overwrite: flags.overwrite,
|
|
33
|
+
version: flags.version,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
this.error(error instanceof Error ? error.message : String(error), { exit: 2 });
|
|
38
|
+
}
|
|
39
|
+
this.display(() => {
|
|
40
|
+
this.log(`Created extension manifest at ${result.manifestPath}`);
|
|
41
|
+
this.log(`Name: ${result.manifest.name}`);
|
|
42
|
+
if (result.manifest.version !== undefined)
|
|
43
|
+
this.log(`Version: ${result.manifest.version}`);
|
|
44
|
+
this.log(`Functions: ${(result.manifest.functions ?? []).join(', ') || '(none)'}`);
|
|
45
|
+
this.log(`Templates: ${(result.manifest.templates ?? []).join(', ') || '(none)'}`);
|
|
46
|
+
if (result.createdDirs.length > 0) {
|
|
47
|
+
this.log(`Created directories: ${result.createdDirs.join(', ')}`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return { manifest: result.manifest, manifestPath: result.manifestPath };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
|
+
import { AgentiaCommand } from '../../../lib/agentia-command.js';
|
|
4
|
+
import { installExtension } from '../../../lib/extension/install.js';
|
|
5
|
+
import { parseVersionFromRef, versionTagName } from '../../../lib/extension/manifest.js';
|
|
6
|
+
export default class ExtensionInstall extends AgentiaCommand {
|
|
7
|
+
static args = {
|
|
8
|
+
source: Args.string({
|
|
9
|
+
description: 'Git repository URL, owner/repo slug, or local directory path',
|
|
10
|
+
required: false,
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
13
|
+
static description = 'Install a Copado extension from a git URL or local folder: upsert functions and job templates via the CICD Gateway; deploy force-app with Salesforce CLI unless excluded';
|
|
14
|
+
static examples = [
|
|
15
|
+
'<%= config.bin %> <%= command.id %> https://gitlab.com/org/my-ext.git --version 1.2.0 --yes',
|
|
16
|
+
'<%= config.bin %> <%= command.id %> ./my-extension --version 0.1.0 --exclude-sf-deploy --yes',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> /tmp/my-extension.git --ref v0.1.0 --sf-alias destOrg --yes',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> ./my-extension --version 1.2.0 --exclude-sf-deploy --yes --json',
|
|
19
|
+
];
|
|
20
|
+
static experimental = true;
|
|
21
|
+
static flags = {
|
|
22
|
+
'dry-run': Flags.boolean({
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'Materialize and validate, print the install plan, but do not upsert or deploy',
|
|
25
|
+
}),
|
|
26
|
+
'exclude-sf-deploy': Flags.boolean({
|
|
27
|
+
default: false,
|
|
28
|
+
description: 'Skip Salesforce metadata deploy even when force-app is present',
|
|
29
|
+
}),
|
|
30
|
+
'ignore-unknown-assets': Flags.boolean({
|
|
31
|
+
default: false,
|
|
32
|
+
description: 'Ignore unsupported manifest asset arrays instead of failing',
|
|
33
|
+
}),
|
|
34
|
+
ref: Flags.string({
|
|
35
|
+
description: 'Git ref to install (tag, branch, or commit). Prefer --version for semver tags.',
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
38
|
+
'sf-alias': Flags.string({
|
|
39
|
+
description: 'Salesforce CLI org alias or username for force-app deploy (required when force-app is present unless --exclude-sf-deploy)',
|
|
40
|
+
required: false,
|
|
41
|
+
}),
|
|
42
|
+
source: Flags.string({
|
|
43
|
+
description: 'Git URL or local path (alternative to the positional source argument)',
|
|
44
|
+
required: false,
|
|
45
|
+
}),
|
|
46
|
+
url: Flags.string({
|
|
47
|
+
description: 'Deprecated alias of --source',
|
|
48
|
+
hidden: true,
|
|
49
|
+
required: false,
|
|
50
|
+
}),
|
|
51
|
+
version: Flags.string({
|
|
52
|
+
description: 'Semver version to install (checks out tag vX.Y.Z)',
|
|
53
|
+
required: false,
|
|
54
|
+
}),
|
|
55
|
+
yes: Flags.boolean({
|
|
56
|
+
char: 'y',
|
|
57
|
+
default: false,
|
|
58
|
+
description: 'Skip the interactive confirmation prompt (required for non-interactive / --json installs that are not --dry-run)',
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
async run() {
|
|
62
|
+
const { args, flags } = await this.parse(ExtensionInstall);
|
|
63
|
+
const source = flags.source ?? flags.url ?? args.source;
|
|
64
|
+
if (source === undefined || source.trim().length === 0) {
|
|
65
|
+
this.error('Provide a git URL or local path as a positional argument or --source.', { exit: 2 });
|
|
66
|
+
}
|
|
67
|
+
await this.confirmInstallIfNeeded(source.trim(), {
|
|
68
|
+
dryRun: flags['dry-run'],
|
|
69
|
+
ref: flags.ref,
|
|
70
|
+
version: flags.version,
|
|
71
|
+
yes: flags.yes,
|
|
72
|
+
});
|
|
73
|
+
let result;
|
|
74
|
+
try {
|
|
75
|
+
result = await this.withSpinner('Installing extension', () => installExtension({
|
|
76
|
+
dryRun: flags['dry-run'],
|
|
77
|
+
excludeSfDeploy: flags['exclude-sf-deploy'],
|
|
78
|
+
ignoreUnknownAssets: flags['ignore-unknown-assets'],
|
|
79
|
+
ref: flags.ref,
|
|
80
|
+
sfAlias: flags['sf-alias'],
|
|
81
|
+
source,
|
|
82
|
+
version: flags.version,
|
|
83
|
+
// Confirmation already handled above (or skipped for --yes / --dry-run).
|
|
84
|
+
yes: true,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
this.error(error instanceof Error ? error.message : String(error), { exit: 1 });
|
|
89
|
+
}
|
|
90
|
+
this.display(() => {
|
|
91
|
+
this.log(`${result.dryRun ? 'Dry-run install plan for' : 'Installed'} ${result.manifest.name}${result.manifest.version === undefined ? '' : `@${result.manifest.version}`}`);
|
|
92
|
+
this.log(`Source: ${result.source} (${result.ref})`);
|
|
93
|
+
this.log(`Functions: ${result.functions.map((item) => item.apiName).join(', ') || '(none)'}`);
|
|
94
|
+
this.log(`Templates: ${result.templates.map((item) => item.apiName).join(', ') || '(none)'}`);
|
|
95
|
+
if (result.forceAppDeployed) {
|
|
96
|
+
this.log('Salesforce source: deployed');
|
|
97
|
+
}
|
|
98
|
+
else if (result.salesforceSkipped) {
|
|
99
|
+
this.log('Salesforce source: skipped');
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
async confirmInstallIfNeeded(source, flags) {
|
|
105
|
+
if (flags.yes === true || flags.dryRun === true)
|
|
106
|
+
return;
|
|
107
|
+
const refLabel = this.resolveRefLabel(flags.ref, flags.version);
|
|
108
|
+
const message = `Install Copado extension from untrusted source ${source} (${refLabel})?`;
|
|
109
|
+
if (this.jsonEnabled()) {
|
|
110
|
+
this.error(`${message} Re-run with --yes to confirm.`, { exit: 1 });
|
|
111
|
+
}
|
|
112
|
+
const accepted = await confirm({ default: false, message });
|
|
113
|
+
if (!accepted) {
|
|
114
|
+
this.error('Extension install cancelled.', { exit: 1 });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
resolveRefLabel(ref, version) {
|
|
118
|
+
if (ref !== undefined && ref.trim().length > 0)
|
|
119
|
+
return ref.trim();
|
|
120
|
+
if (version !== undefined && version.trim().length > 0) {
|
|
121
|
+
const parsed = parseVersionFromRef(version) ?? version.trim();
|
|
122
|
+
return versionTagName(parsed);
|
|
123
|
+
}
|
|
124
|
+
return 'default branch / latest tag / local folder';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { AgentiaCommand } from '../../../lib/agentia-command.js';
|
|
3
|
+
import { packageExtension } from '../../../lib/extension/package.js';
|
|
4
|
+
export default class ExtensionPackage extends AgentiaCommand {
|
|
5
|
+
static description = 'Validate a Copado extension for publish (requires semver version; optional --version bump)';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --version 1.2.0 --json',
|
|
9
|
+
];
|
|
10
|
+
static experimental = true;
|
|
11
|
+
static flags = {
|
|
12
|
+
version: Flags.string({
|
|
13
|
+
description: 'Set or bump the manifest semver version before validating',
|
|
14
|
+
required: false,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags } = await this.parse(ExtensionPackage);
|
|
19
|
+
let result;
|
|
20
|
+
try {
|
|
21
|
+
result = await packageExtension({ version: flags.version });
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
this.error(error instanceof Error ? error.message : String(error), { exit: 1 });
|
|
25
|
+
}
|
|
26
|
+
this.display(() => {
|
|
27
|
+
this.log(`Packaged extension ${result.manifest.name}@${result.manifest.version}`);
|
|
28
|
+
this.log(`Functions: ${(result.manifest.functions ?? []).length}`);
|
|
29
|
+
this.log(`Templates: ${(result.manifest.templates ?? []).length}`);
|
|
30
|
+
this.log(`Salesforce source: ${result.validation.forceAppPresent ? result.validation.salesforceSourceDir : '(none)'}`);
|
|
31
|
+
});
|
|
32
|
+
return { manifest: result.manifest };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=package.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { AgentiaCommand } from '../../../lib/agentia-command.js';
|
|
3
|
+
import { publishExtension } from '../../../lib/extension/publish.js';
|
|
4
|
+
export default class ExtensionPublish extends AgentiaCommand {
|
|
5
|
+
static description = 'Push the current extension commit and create an annotated semver git tag (vX.Y.Z) for install pinning';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --version 1.2.0 --remote origin --json',
|
|
9
|
+
'<%= config.bin %> <%= command.id %> --version 0.1.0 --local --force',
|
|
10
|
+
];
|
|
11
|
+
static experimental = true;
|
|
12
|
+
static flags = {
|
|
13
|
+
'allow-dirty': Flags.boolean({
|
|
14
|
+
description: 'Allow publish with a dirty working tree',
|
|
15
|
+
default: false,
|
|
16
|
+
}),
|
|
17
|
+
force: Flags.boolean({
|
|
18
|
+
description: 'Recreate the local tag and push even if the tag already exists',
|
|
19
|
+
default: false,
|
|
20
|
+
}),
|
|
21
|
+
local: Flags.boolean({
|
|
22
|
+
description: 'Create the annotated tag locally without pushing to a remote (offline / local e2e)',
|
|
23
|
+
default: false,
|
|
24
|
+
}),
|
|
25
|
+
remote: Flags.string({
|
|
26
|
+
description: 'Git remote to push (default origin; ignored with --local)',
|
|
27
|
+
default: 'origin',
|
|
28
|
+
}),
|
|
29
|
+
version: Flags.string({
|
|
30
|
+
description: 'Require this semver in the manifest before tagging (writes it only if already committed; fails if the bump is uncommitted)',
|
|
31
|
+
required: false,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
async run() {
|
|
35
|
+
const { flags } = await this.parse(ExtensionPublish);
|
|
36
|
+
let result;
|
|
37
|
+
try {
|
|
38
|
+
result = await this.withSpinner('Publishing extension', () => publishExtension({
|
|
39
|
+
allowDirty: flags['allow-dirty'],
|
|
40
|
+
force: flags.force,
|
|
41
|
+
local: flags.local,
|
|
42
|
+
remote: flags.remote,
|
|
43
|
+
version: flags.version,
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
this.error(error instanceof Error ? error.message : String(error), { exit: 1 });
|
|
48
|
+
}
|
|
49
|
+
this.display(() => {
|
|
50
|
+
this.log(`Published ${result.manifest.name}@${result.manifest.version}${result.localOnly ? ' (local tag only)' : ''}`);
|
|
51
|
+
if (result.remote !== undefined)
|
|
52
|
+
this.log(`Remote: ${result.remote}`);
|
|
53
|
+
this.log(`Branch: ${result.branch}`);
|
|
54
|
+
this.log(`Commit: ${result.commit}`);
|
|
55
|
+
this.log(`Tag: ${result.tag}`);
|
|
56
|
+
this.log(`Install: ${result.installCommand}`);
|
|
57
|
+
});
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
|
+
import { AgentiaCommand } from '../../../lib/agentia-command.js';
|
|
4
|
+
import { parseVersionFromRef, versionTagName } from '../../../lib/extension/manifest.js';
|
|
5
|
+
import { uninstallExtension } from '../../../lib/extension/uninstall.js';
|
|
6
|
+
export default class ExtensionUninstall extends AgentiaCommand {
|
|
7
|
+
static args = {
|
|
8
|
+
source: Args.string({
|
|
9
|
+
description: 'Git repository URL, owner/repo slug, or local directory path',
|
|
10
|
+
required: false,
|
|
11
|
+
}),
|
|
12
|
+
};
|
|
13
|
+
static description = 'Uninstall a Copado extension by deleting the functions and job templates listed in its manifest from the authenticated Copado org';
|
|
14
|
+
static examples = [
|
|
15
|
+
'<%= config.bin %> <%= command.id %> ./my-extension --version 0.1.0 --yes',
|
|
16
|
+
'<%= config.bin %> <%= command.id %> https://gitlab.com/org/my-ext.git --ref v0.1.0 --yes --json',
|
|
17
|
+
];
|
|
18
|
+
static experimental = true;
|
|
19
|
+
static flags = {
|
|
20
|
+
'dry-run': Flags.boolean({
|
|
21
|
+
default: false,
|
|
22
|
+
description: 'Show which assets would be deleted without calling the gateway',
|
|
23
|
+
}),
|
|
24
|
+
ref: Flags.string({
|
|
25
|
+
description: 'Git ref to read the manifest from',
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
28
|
+
source: Flags.string({
|
|
29
|
+
description: 'Git URL or local path (alternative to the positional source argument)',
|
|
30
|
+
required: false,
|
|
31
|
+
}),
|
|
32
|
+
version: Flags.string({
|
|
33
|
+
description: 'Semver version whose tag (vX.Y.Z) supplies the manifest',
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
yes: Flags.boolean({
|
|
37
|
+
char: 'y',
|
|
38
|
+
default: false,
|
|
39
|
+
description: 'Skip the interactive confirmation prompt (required for non-interactive / --json uninstalls that are not --dry-run)',
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
async run() {
|
|
43
|
+
const { args, flags } = await this.parse(ExtensionUninstall);
|
|
44
|
+
const source = flags.source ?? args.source;
|
|
45
|
+
if (source === undefined || source.trim().length === 0) {
|
|
46
|
+
this.error('Provide a git URL or local path as a positional argument or --source.', { exit: 2 });
|
|
47
|
+
}
|
|
48
|
+
await this.confirmUninstallIfNeeded(source.trim(), {
|
|
49
|
+
dryRun: flags['dry-run'],
|
|
50
|
+
ref: flags.ref,
|
|
51
|
+
version: flags.version,
|
|
52
|
+
yes: flags.yes,
|
|
53
|
+
});
|
|
54
|
+
let result;
|
|
55
|
+
try {
|
|
56
|
+
result = await this.withSpinner('Uninstalling extension', () => uninstallExtension({
|
|
57
|
+
dryRun: flags['dry-run'],
|
|
58
|
+
ref: flags.ref,
|
|
59
|
+
source,
|
|
60
|
+
version: flags.version,
|
|
61
|
+
// Confirmation already handled above (or skipped for --yes / --dry-run).
|
|
62
|
+
yes: true,
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.error(error instanceof Error ? error.message : String(error), { exit: 1 });
|
|
67
|
+
}
|
|
68
|
+
this.display(() => {
|
|
69
|
+
this.log(`${result.dryRun ? 'Dry-run uninstall plan for' : 'Uninstalled'} ${result.manifest.name}${result.manifest.version === undefined ? '' : `@${result.manifest.version}`}`);
|
|
70
|
+
this.log(`Source: ${result.source} (${result.ref})`);
|
|
71
|
+
this.log(`Functions: ${result.functions.map((item) => `${item.apiName}${item.deleted ? '' : ' (failed)'}`).join(', ') || '(none)'}`);
|
|
72
|
+
this.log(`Templates: ${result.templates.map((item) => `${item.apiName}${item.deleted ? '' : ' (failed)'}`).join(', ') || '(none)'}`);
|
|
73
|
+
});
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
async confirmUninstallIfNeeded(source, flags) {
|
|
77
|
+
if (flags.yes === true || flags.dryRun === true)
|
|
78
|
+
return;
|
|
79
|
+
const refLabel = this.resolveRefLabel(flags.ref, flags.version);
|
|
80
|
+
const message = `Uninstall Copado extension from ${source} (${refLabel})? This deletes listed functions and job templates from the authenticated Copado org.`;
|
|
81
|
+
if (this.jsonEnabled()) {
|
|
82
|
+
this.error(`${message} Re-run with --yes to confirm.`, { exit: 1 });
|
|
83
|
+
}
|
|
84
|
+
const accepted = await confirm({ default: false, message });
|
|
85
|
+
if (!accepted) {
|
|
86
|
+
this.error('Extension uninstall cancelled.', { exit: 1 });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
resolveRefLabel(ref, version) {
|
|
90
|
+
if (ref !== undefined && ref.trim().length > 0)
|
|
91
|
+
return ref.trim();
|
|
92
|
+
if (version !== undefined && version.trim().length > 0) {
|
|
93
|
+
const parsed = parseVersionFromRef(version) ?? version.trim();
|
|
94
|
+
return versionTagName(parsed);
|
|
95
|
+
}
|
|
96
|
+
return 'default branch / latest tag / local folder';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { AgentiaCommand } from '../../../lib/agentia-command.js';
|
|
3
|
+
import { validateExtension } from '../../../lib/extension/manifest.js';
|
|
4
|
+
export default class ExtensionValidate extends AgentiaCommand {
|
|
5
|
+
static description = 'Validate a Copado extension manifest against copado/functions and copado/templates on disk';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --require-version --json',
|
|
9
|
+
];
|
|
10
|
+
static experimental = true;
|
|
11
|
+
static flags = {
|
|
12
|
+
'require-version': Flags.boolean({
|
|
13
|
+
description: 'Require a semver version field (needed for package/publish)',
|
|
14
|
+
default: false,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags } = await this.parse(ExtensionValidate);
|
|
19
|
+
let result;
|
|
20
|
+
try {
|
|
21
|
+
result = await validateExtension(process.cwd(), { requireVersion: flags['require-version'] });
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
this.error(error instanceof Error ? error.message : String(error), { exit: 2 });
|
|
25
|
+
}
|
|
26
|
+
this.display(() => {
|
|
27
|
+
this.log(`Extension: ${result.manifest.name}`);
|
|
28
|
+
if (result.manifest.version !== undefined)
|
|
29
|
+
this.log(`Version: ${result.manifest.version}`);
|
|
30
|
+
this.log(`Salesforce source present: ${result.forceAppPresent ? 'yes' : 'no'} (${result.salesforceSourceDir})`);
|
|
31
|
+
if (result.ok) {
|
|
32
|
+
this.log('Validation passed.');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
this.log('Validation failed:');
|
|
36
|
+
for (const issue of result.issues) {
|
|
37
|
+
this.log(`- ${issue.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (!result.ok) {
|
|
42
|
+
this.error('Extension validation failed.', { exit: 1 });
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { AgentiaCommand } from '../../../lib/agentia-command.js';
|
|
3
3
|
import { WorkActiveResolutionError } from '../../../lib/work-active.js';
|
|
4
|
-
import {
|
|
4
|
+
import { formatCommittedMetadataSection, } from '../../../lib/work-committed-metadata.js';
|
|
5
|
+
import { fetchWorkItemWithOptions } from '../../../lib/work-get.js';
|
|
5
6
|
import { formatUserStoriesAheadBehindLines, reportUserStoriesAheadBehindForStory, } from '../../../lib/work-submit-ahead-behind.js';
|
|
6
7
|
import { formatUserStoryDetail, } from '../../../lib/work-types.js';
|
|
7
8
|
export default class WorkGet extends AgentiaCommand {
|
|
@@ -16,10 +17,16 @@ export default class WorkGet extends AgentiaCommand {
|
|
|
16
17
|
'<%= config.bin %> <%= command.id %>',
|
|
17
18
|
'<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU',
|
|
18
19
|
'<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --json',
|
|
20
|
+
'<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --committed-metadata',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --committed-metadata --json',
|
|
19
22
|
'<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --user-stories-ahead-behind',
|
|
20
23
|
'<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU --user-stories-ahead-behind --json',
|
|
21
24
|
];
|
|
22
25
|
static flags = {
|
|
26
|
+
'committed-metadata': Flags.boolean({
|
|
27
|
+
default: false,
|
|
28
|
+
description: 'Also include the Story committed metadata list in output',
|
|
29
|
+
}),
|
|
23
30
|
'user-stories-ahead-behind': Flags.boolean({
|
|
24
31
|
default: false,
|
|
25
32
|
description: 'When the user story has a pipeline and source environment, compare user stories ahead of and behind the next pipeline environment',
|
|
@@ -28,8 +35,13 @@ export default class WorkGet extends AgentiaCommand {
|
|
|
28
35
|
async run() {
|
|
29
36
|
const { args, flags } = await this.parse(WorkGet);
|
|
30
37
|
let userStory;
|
|
38
|
+
let committedMetadata;
|
|
31
39
|
try {
|
|
32
|
-
|
|
40
|
+
const fetched = await this.withSpinner('Fetching user story', () => fetchWorkItemWithOptions(args.id, {
|
|
41
|
+
committedMetadata: flags['committed-metadata'],
|
|
42
|
+
}));
|
|
43
|
+
userStory = fetched.userStory;
|
|
44
|
+
committedMetadata = fetched.committedMetadata;
|
|
33
45
|
}
|
|
34
46
|
catch (error) {
|
|
35
47
|
if (error instanceof WorkActiveResolutionError) {
|
|
@@ -48,12 +60,16 @@ export default class WorkGet extends AgentiaCommand {
|
|
|
48
60
|
}
|
|
49
61
|
this.display(() => {
|
|
50
62
|
this.log(formatUserStoryDetail(userStory));
|
|
63
|
+
if (committedMetadata !== undefined) {
|
|
64
|
+
this.log(formatCommittedMetadataSection(committedMetadata));
|
|
65
|
+
}
|
|
51
66
|
if (userStoriesAheadBehind !== undefined) {
|
|
52
67
|
this.log(formatUserStoriesAheadBehindLines(userStoriesAheadBehind));
|
|
53
68
|
}
|
|
54
69
|
});
|
|
55
70
|
return {
|
|
56
71
|
...userStory,
|
|
72
|
+
...(committedMetadata === undefined ? {} : { committedMetadata }),
|
|
57
73
|
...(userStoriesAheadBehind === undefined ? {} : { userStoriesAheadBehind }),
|
|
58
74
|
};
|
|
59
75
|
}
|
|
@@ -23,7 +23,7 @@ export default class SetupSkillsCreate extends AgentiaCommand {
|
|
|
23
23
|
}),
|
|
24
24
|
force: Flags.boolean({
|
|
25
25
|
default: false,
|
|
26
|
-
description: 'Replace conflicting unmanaged Agentia skill files',
|
|
26
|
+
description: 'Replace conflicting unmanaged Agentia skill or rules files',
|
|
27
27
|
}),
|
|
28
28
|
'no-prompt': Flags.boolean({
|
|
29
29
|
char: 'n',
|
|
@@ -23,7 +23,7 @@ export default class SetupSkillsUpdate extends AgentiaCommand {
|
|
|
23
23
|
}),
|
|
24
24
|
force: Flags.boolean({
|
|
25
25
|
default: false,
|
|
26
|
-
description: 'Replace conflicting unmanaged Agentia skill files',
|
|
26
|
+
description: 'Replace conflicting unmanaged Agentia skill or rules files',
|
|
27
27
|
}),
|
|
28
28
|
'no-prompt': Flags.boolean({
|
|
29
29
|
char: 'n',
|
package/dist/commands/setup.js
CHANGED
|
@@ -6,7 +6,7 @@ import { runSetupWizard } from '../lib/setup-wizard.js';
|
|
|
6
6
|
export default class Setup extends AgentiaCommand {
|
|
7
7
|
static description = [
|
|
8
8
|
'Prepare this machine and the current project for Agentia.',
|
|
9
|
-
'Guides CICD, AI, and CRT authentication, Cursor MCP, local quality gates, and AGENTS.md.',
|
|
9
|
+
'Guides CICD, AI, and CRT authentication, Cursor MCP, local quality gates, Agent Skills, agent rules, and removal of a legacy AGENTS.md section.',
|
|
10
10
|
'Use --skip-auth to skip authentication and continue with Cursor MCP and project setup.',
|
|
11
11
|
'Git is optional: .gitignore is updated only inside a repository, and generated files are not committed.',
|
|
12
12
|
].join(' ');
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import {modules} from '../runtime.js';
|
|
2
|
+
const loadFunctionUpsertPayload = modules["extension/assets.js"]["loadFunctionUpsertPayload"]
|
|
3
|
+
const loadTemplateUpsertPayload = modules["extension/assets.js"]["loadTemplateUpsertPayload"]
|
|
4
|
+
export {loadFunctionUpsertPayload, loadTemplateUpsertPayload};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {modules} from '../runtime.js';
|
|
2
|
+
const assertSafeGitRemote = modules["extension/git.js"]["assertSafeGitRemote"]
|
|
3
|
+
const cloneExtensionRepo = modules["extension/git.js"]["cloneExtensionRepo"]
|
|
4
|
+
const createAnnotatedTag = modules["extension/git.js"]["createAnnotatedTag"]
|
|
5
|
+
const deleteLocalTag = modules["extension/git.js"]["deleteLocalTag"]
|
|
6
|
+
const extensionGit = modules["extension/git.js"]["extensionGit"]
|
|
7
|
+
const formatGitError = modules["extension/git.js"]["formatGitError"]
|
|
8
|
+
const getCurrentBranchName = modules["extension/git.js"]["getCurrentBranchName"]
|
|
9
|
+
const getHeadSha = modules["extension/git.js"]["getHeadSha"]
|
|
10
|
+
const isWorkingTreeClean = modules["extension/git.js"]["isWorkingTreeClean"]
|
|
11
|
+
const localTagExists = modules["extension/git.js"]["localTagExists"]
|
|
12
|
+
const pushBranchAndTag = modules["extension/git.js"]["pushBranchAndTag"]
|
|
13
|
+
const remoteTagExists = modules["extension/git.js"]["remoteTagExists"]
|
|
14
|
+
const resetExtensionGitExecForTests = modules["extension/git.js"]["resetExtensionGitExecForTests"]
|
|
15
|
+
const resolveLatestSemverTag = modules["extension/git.js"]["resolveLatestSemverTag"]
|
|
16
|
+
const setExtensionGitExecForTests = modules["extension/git.js"]["setExtensionGitExecForTests"]
|
|
17
|
+
export {assertSafeGitRemote, cloneExtensionRepo, createAnnotatedTag, deleteLocalTag, extensionGit, formatGitError, getCurrentBranchName, getHeadSha, isWorkingTreeClean, localTagExists, pushBranchAndTag, remoteTagExists, resetExtensionGitExecForTests, resolveLatestSemverTag, setExtensionGitExecForTests};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {modules} from '../runtime.js';
|
|
2
|
+
const EXTENSION_MANIFEST_RELATIVE_PATH = modules["extension/index.js"]["EXTENSION_MANIFEST_RELATIVE_PATH"]
|
|
3
|
+
const assertSafeGitUrl = modules["extension/index.js"]["assertSafeGitUrl"]
|
|
4
|
+
const assertValidExtensionName = modules["extension/index.js"]["assertValidExtensionName"]
|
|
5
|
+
const assertValidSemver = modules["extension/index.js"]["assertValidSemver"]
|
|
6
|
+
const cloneExtensionRepo = modules["extension/index.js"]["cloneExtensionRepo"]
|
|
7
|
+
const createExtension = modules["extension/index.js"]["createExtension"]
|
|
8
|
+
const deployExtensionSalesforceSource = modules["extension/index.js"]["deployExtensionSalesforceSource"]
|
|
9
|
+
const extensionGit = modules["extension/index.js"]["extensionGit"]
|
|
10
|
+
const installExtension = modules["extension/index.js"]["installExtension"]
|
|
11
|
+
const loadFunctionUpsertPayload = modules["extension/index.js"]["loadFunctionUpsertPayload"]
|
|
12
|
+
const loadTemplateUpsertPayload = modules["extension/index.js"]["loadTemplateUpsertPayload"]
|
|
13
|
+
const materializeExtensionSource = modules["extension/index.js"]["materializeExtensionSource"]
|
|
14
|
+
const packageExtension = modules["extension/index.js"]["packageExtension"]
|
|
15
|
+
const parseExtensionManifest = modules["extension/index.js"]["parseExtensionManifest"]
|
|
16
|
+
const publishExtension = modules["extension/index.js"]["publishExtension"]
|
|
17
|
+
const readExtensionManifest = modules["extension/index.js"]["readExtensionManifest"]
|
|
18
|
+
const resetExtensionGitExecForTests = modules["extension/index.js"]["resetExtensionGitExecForTests"]
|
|
19
|
+
const resetExtensionSfDeployExecForTests = modules["extension/index.js"]["resetExtensionSfDeployExecForTests"]
|
|
20
|
+
const resolveLocalExtensionPath = modules["extension/index.js"]["resolveLocalExtensionPath"]
|
|
21
|
+
const setExtensionGitExecForTests = modules["extension/index.js"]["setExtensionGitExecForTests"]
|
|
22
|
+
const setExtensionSfDeployExecForTests = modules["extension/index.js"]["setExtensionSfDeployExecForTests"]
|
|
23
|
+
const uninstallExtension = modules["extension/index.js"]["uninstallExtension"]
|
|
24
|
+
const validateExtension = modules["extension/index.js"]["validateExtension"]
|
|
25
|
+
const versionTagName = modules["extension/index.js"]["versionTagName"]
|
|
26
|
+
const writeExtensionManifest = modules["extension/index.js"]["writeExtensionManifest"]
|
|
27
|
+
export {EXTENSION_MANIFEST_RELATIVE_PATH, assertSafeGitUrl, assertValidExtensionName, assertValidSemver, cloneExtensionRepo, createExtension, deployExtensionSalesforceSource, extensionGit, installExtension, loadFunctionUpsertPayload, loadTemplateUpsertPayload, materializeExtensionSource, packageExtension, parseExtensionManifest, publishExtension, readExtensionManifest, resetExtensionGitExecForTests, resetExtensionSfDeployExecForTests, resolveLocalExtensionPath, setExtensionGitExecForTests, setExtensionSfDeployExecForTests, uninstallExtension, validateExtension, versionTagName, writeExtensionManifest};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {modules} from '../runtime.js';
|
|
2
|
+
const DEFAULT_SALESFORCE_SOURCE_DIR = modules["extension/manifest.js"]["DEFAULT_SALESFORCE_SOURCE_DIR"]
|
|
3
|
+
const EXTENSION_FUNCTIONS_DIR = modules["extension/manifest.js"]["EXTENSION_FUNCTIONS_DIR"]
|
|
4
|
+
const EXTENSION_MANIFEST_RELATIVE_PATH = modules["extension/manifest.js"]["EXTENSION_MANIFEST_RELATIVE_PATH"]
|
|
5
|
+
const EXTENSION_NAME_REGEX = modules["extension/manifest.js"]["EXTENSION_NAME_REGEX"]
|
|
6
|
+
const EXTENSION_TEMPLATES_DIR = modules["extension/manifest.js"]["EXTENSION_TEMPLATES_DIR"]
|
|
7
|
+
const SEMVER_REGEX = modules["extension/manifest.js"]["SEMVER_REGEX"]
|
|
8
|
+
const assertValidExtensionName = modules["extension/manifest.js"]["assertValidExtensionName"]
|
|
9
|
+
const assertValidSemver = modules["extension/manifest.js"]["assertValidSemver"]
|
|
10
|
+
const extensionManifestPath = modules["extension/manifest.js"]["extensionManifestPath"]
|
|
11
|
+
const metadataJsonPath = modules["extension/manifest.js"]["metadataJsonPath"]
|
|
12
|
+
const parseExtensionManifest = modules["extension/manifest.js"]["parseExtensionManifest"]
|
|
13
|
+
const parseVersionFromRef = modules["extension/manifest.js"]["parseVersionFromRef"]
|
|
14
|
+
const pathExists = modules["extension/manifest.js"]["pathExists"]
|
|
15
|
+
const readExtensionManifest = modules["extension/manifest.js"]["readExtensionManifest"]
|
|
16
|
+
const resolveSalesforceSourceDir = modules["extension/manifest.js"]["resolveSalesforceSourceDir"]
|
|
17
|
+
const scanExtensionAssets = modules["extension/manifest.js"]["scanExtensionAssets"]
|
|
18
|
+
const validateExtension = modules["extension/manifest.js"]["validateExtension"]
|
|
19
|
+
const versionTagName = modules["extension/manifest.js"]["versionTagName"]
|
|
20
|
+
const writeExtensionManifest = modules["extension/manifest.js"]["writeExtensionManifest"]
|
|
21
|
+
export {DEFAULT_SALESFORCE_SOURCE_DIR, EXTENSION_FUNCTIONS_DIR, EXTENSION_MANIFEST_RELATIVE_PATH, EXTENSION_NAME_REGEX, EXTENSION_TEMPLATES_DIR, SEMVER_REGEX, assertValidExtensionName, assertValidSemver, extensionManifestPath, metadataJsonPath, parseExtensionManifest, parseVersionFromRef, pathExists, readExtensionManifest, resolveSalesforceSourceDir, scanExtensionAssets, validateExtension, versionTagName, writeExtensionManifest};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import {modules} from '../runtime.js';
|
|
2
|
+
const assertPathInsideRoot = modules["extension/paths.js"]["assertPathInsideRoot"]
|
|
3
|
+
const assertSafeExtensionFile = modules["extension/paths.js"]["assertSafeExtensionFile"]
|
|
4
|
+
const assertSafeExtensionTree = modules["extension/paths.js"]["assertSafeExtensionTree"]
|
|
5
|
+
export {assertPathInsideRoot, assertSafeExtensionFile, assertSafeExtensionTree};
|