sf-plugin-preflight 0.0.0-stage → 0.8.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 ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## [0.8.0]
4
+
5
+ - First release (the plugin shares the CLI version): `sf preflight analyze | tests | agents | agent-tests | incidents | rollback | explain | evidence`, passing `--target-org` and `--json` through to the sf-preflight CLI.
package/README.md CHANGED
@@ -1,3 +1,65 @@
1
- # Temporary Holding Version
1
+ # sf-plugin-preflight
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Salesforce CLI plugin for [sf-preflight](https://github.com/visparashar/sf-preflight): see what a
4
+ Salesforce change sets off (flows, triggers, validation rules, roll-ups, permissions, Agentforce
5
+ actions) before it ships. It adds an `sf preflight` topic.
6
+
7
+ The plugin has no analysis of its own. Each command passes your flags to the `sf-preflight` CLI it
8
+ depends on, so `sf preflight analyze` and `npx sf-preflight analyze` always give the same answer.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ sf plugins install sf-plugin-preflight
14
+ ```
15
+
16
+ `sf` asks you to confirm because the plugin is not on Salesforce's signed list. To skip the prompt,
17
+ add it to `~/.config/sf/unsignedPluginAllowList.json`. Needs Node 22.13 or later.
18
+
19
+ ## Use
20
+
21
+ ```sh
22
+ sf preflight analyze --base origin/main # what does this branch set off?
23
+ sf preflight analyze --base origin/main --gate # exit 2 if the quality gate fails
24
+ sf preflight analyze --base origin/main --json # same as --format json
25
+ sf preflight tests --base origin/main --validate --target-org my-sandbox
26
+ sf preflight agents Sales_Agent
27
+ sf preflight incidents --target-org prod --since 7d
28
+ sf preflight rollback a1b2c3d
29
+ sf preflight explain Opportunity
30
+ sf preflight evidence --base origin/main --out preflight-evidence.json
31
+ ```
32
+
33
+ Run `sf preflight <command> --help` for the common flags. Every flag of the sf-preflight CLI works,
34
+ listed or not (for example `--md-out`, `--sarif-out`, `--config`); see the
35
+ [CLI reference](https://github.com/visparashar/sf-preflight#readme).
36
+
37
+ ## How sf's flags map
38
+
39
+ | You type | The CLI receives |
40
+ |---|---|
41
+ | `-o <org>`, `--target-org <org>` | `--org <org>` |
42
+ | `--json` | `--format json` (commands without formats ignore it) |
43
+
44
+ On commands that read an org (`analyze`, `tests`, `agent-tests`, `incidents`, `rollback`), `-o` is
45
+ the org, as everywhere in `sf`. The CLI's own output file flag is spelled `--out` there. On
46
+ `evidence` and `explain`, which never read an org, `-o` stays the CLI's `--out`.
47
+
48
+ ## Safety
49
+
50
+ Same as the CLI. Nothing is deployed. Org commands are read-only, except `tests --validate` (a
51
+ check-only deployment, nothing saved) and `agent-tests` (runs existing Testing Center tests); both
52
+ refuse a production org unless you pass `--allow-production`. Reports never contain usernames or
53
+ record data. The plugin never reads or stores credentials: your org is reached through `sf` itself.
54
+
55
+ ## Development
56
+
57
+ ```sh
58
+ npm ci && npm run build # in the repository root
59
+ cd sf-plugin && npm ci && npm test
60
+ SF_PREFLIGHT_CLI=../dist/cli.js node test/run.mjs preflight analyze --help
61
+ ```
62
+
63
+ `SF_PREFLIGHT_CLI` points the plugin at a local build instead of the installed dependency.
64
+
65
+ Apache-2.0.
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { Command, Flags } from "@oclif/core";
3
+ import { runCli, translate } from "./passthrough.js";
4
+ const targetOrg = Flags.string({
5
+ char: "o",
6
+ summary: "Org to read from, by username or alias (an `sf org login` org).",
7
+ });
8
+ const common = {
9
+ project: Flags.string({ char: "p", summary: "SFDX project directory (default: the current directory)." }),
10
+ };
11
+ export const gitFlags = {
12
+ base: Flags.string({ char: "b", summary: "Git base ref, e.g. origin/main." }),
13
+ head: Flags.string({ summary: "Git head ref (default: the working tree)." }),
14
+ files: Flags.string({ char: "f", summary: "Explicit changed files instead of a git diff.", multiple: true }),
15
+ };
16
+ /**
17
+ * Builds an `sf preflight <name>` command. The plugin adds no analysis of its own: it passes the
18
+ * command line to the sf-preflight CLI, so the two always agree.
19
+ */
20
+ export function preflightCommand(spec) {
21
+ return class extends Command {
22
+ static summary = spec.summary;
23
+ static description = spec.description;
24
+ static examples = spec.examples;
25
+ // Flags not listed below are valid too: they are handed to the sf-preflight CLI as they are.
26
+ static strict = false;
27
+ static enableJsonFlag = false;
28
+ static flags = {
29
+ ...common,
30
+ ...(spec.org ? { "target-org": targetOrg } : {}),
31
+ ...spec.flags,
32
+ json: Flags.boolean({ summary: spec.hasFormat ? "Print JSON (same as --format json)." : "Accepted; no effect." }),
33
+ };
34
+ async run() {
35
+ const code = await runCli(spec.cli, translate(this.argv, { hasFormat: spec.hasFormat, org: spec.org }));
36
+ if (code !== 0)
37
+ this.exit(code);
38
+ }
39
+ };
40
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { Flags } from "@oclif/core";
3
+ import { gitFlags, preflightCommand } from "../../command.js";
4
+ export default preflightCommand({
5
+ cli: "agent-tests",
6
+ summary: "Pick the Agentforce Testing Center tests a change affects, and run them in an org.",
7
+ description: "Runs tests that already exist in the target org (a sandbox, scratch org or Developer Edition org with the change deployed). Production is refused unless --allow-production.",
8
+ examples: [
9
+ "<%= config.bin %> <%= command.id %> --base origin/main --dry-run",
10
+ "<%= config.bin %> <%= command.id %> --base origin/main --target-org my-sandbox",
11
+ ],
12
+ org: true,
13
+ hasFormat: true,
14
+ flags: {
15
+ ...gitFlags,
16
+ "dry-run": Flags.boolean({ summary: "List the tests that would run, without running them." }),
17
+ "allow-production": Flags.boolean({ summary: "Allow running in a production org." }),
18
+ format: Flags.string({ summary: "Output format.", options: ["md", "json"] }),
19
+ },
20
+ });
@@ -0,0 +1,11 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { Flags } from "@oclif/core";
3
+ import { preflightCommand } from "../../command.js";
4
+ export default preflightCommand({
5
+ cli: "agents",
6
+ summary: "List Agentforce agents, or explain what one agent's actions call, save and need.",
7
+ examples: ["<%= config.bin %> <%= command.id %>", "<%= config.bin %> <%= command.id %> Sales_Agent"],
8
+ org: false,
9
+ hasFormat: true,
10
+ flags: { format: Flags.string({ summary: "Output format.", options: ["md", "json"] }) },
11
+ });
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { Flags } from "@oclif/core";
3
+ import { gitFlags, preflightCommand } from "../../command.js";
4
+ export default preflightCommand({
5
+ cli: "analyze",
6
+ summary: "See what a Salesforce change sets off: flows, triggers, validation rules, roll-ups, permissions.",
7
+ description: "Reads the git diff (or the files you name) and follows it through the metadata in your SFDX project. Nothing is deployed and no org is needed. With --target-org, read-only facts from the org (such as active flows) are added.",
8
+ examples: [
9
+ "<%= config.bin %> <%= command.id %> --base origin/main",
10
+ "<%= config.bin %> <%= command.id %> --base origin/main --gate --md-out preflight.md",
11
+ "<%= config.bin %> <%= command.id %> --files force-app/main/default/classes/Foo.cls --json",
12
+ ],
13
+ org: true,
14
+ hasFormat: true,
15
+ flags: {
16
+ ...gitFlags,
17
+ format: Flags.string({ summary: "Output format.", options: ["md", "json", "sarif", "junit"] }),
18
+ gate: Flags.boolean({ summary: "Evaluate the quality gate from .preflight.json; exit 2 when it fails." }),
19
+ },
20
+ });
@@ -0,0 +1,10 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { gitFlags, preflightCommand } from "../../command.js";
3
+ export default preflightCommand({
4
+ cli: "evidence",
5
+ summary: "Write the evidence pack for a change: what changed, findings, tests, approvals, gate.",
6
+ examples: ["<%= config.bin %> <%= command.id %> --base origin/main --out preflight-evidence.json"],
7
+ org: false,
8
+ hasFormat: false,
9
+ flags: { ...gitFlags },
10
+ });
@@ -0,0 +1,10 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { preflightCommand } from "../../command.js";
3
+ export default preflightCommand({
4
+ cli: "explain",
5
+ summary: "Show what runs, in order, when records of an object are saved.",
6
+ examples: ["<%= config.bin %> <%= command.id %> Opportunity"],
7
+ org: false,
8
+ hasFormat: false,
9
+ flags: {},
10
+ });
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { Flags } from "@oclif/core";
3
+ import { preflightCommand } from "../../command.js";
4
+ export default preflightCommand({
5
+ cli: "incidents",
6
+ summary: "Match recent org errors to the recent changes that probably caused them.",
7
+ description: "Reads errors from the target org (read-only; production is fine) and searches git history.",
8
+ examples: ["<%= config.bin %> <%= command.id %> --target-org prod --since 7d"],
9
+ org: true,
10
+ hasFormat: true,
11
+ flags: {
12
+ since: Flags.string({ summary: "Errors since a duration (7d, 24h, 2w) or a date." }),
13
+ format: Flags.string({ summary: "Output format.", options: ["md", "json"] }),
14
+ },
15
+ });
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { Flags } from "@oclif/core";
3
+ import { preflightCommand } from "../../command.js";
4
+ export default preflightCommand({
5
+ cli: "rollback",
6
+ summary: "Plan a partial rollback of a change: restore or deactivate only the components that need it.",
7
+ description: "Prints the commands; nothing is deployed.",
8
+ examples: ["<%= config.bin %> <%= command.id %> a1b2c3d --target-org my-sandbox"],
9
+ org: true,
10
+ hasFormat: true,
11
+ flags: { format: Flags.string({ summary: "Output format.", options: ["md", "json"] }) },
12
+ });
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { Flags } from "@oclif/core";
3
+ import { gitFlags, preflightCommand } from "../../command.js";
4
+ export default preflightCommand({
5
+ cli: "tests",
6
+ summary: "Generate Apex tests for what a change touches (bulk, recursion, idempotency, swallowed errors).",
7
+ description: "Writes test classes to a folder. With --validate and --target-org it runs them in a check-only deployment: nothing is saved in the org. Production orgs are refused unless --allow-production.",
8
+ examples: [
9
+ "<%= config.bin %> <%= command.id %> --base origin/main --dry-run",
10
+ "<%= config.bin %> <%= command.id %> --base origin/main --validate --target-org my-sandbox",
11
+ ],
12
+ org: true,
13
+ hasFormat: true,
14
+ flags: {
15
+ ...gitFlags,
16
+ "dry-run": Flags.boolean({ summary: "Print the summary without writing files." }),
17
+ validate: Flags.boolean({ summary: "Run the tests in the target org with a check-only deployment." }),
18
+ "allow-production": Flags.boolean({ summary: "Allow --validate in a production org." }),
19
+ format: Flags.string({ summary: "Summary format.", options: ["md", "json"] }),
20
+ },
21
+ });
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { spawn } from "node:child_process";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import path from "node:path";
6
+ /** The sf-preflight CLI this plugin runs: `SF_PREFLIGHT_CLI` (for development), else the dependency. */
7
+ export function cliPath() {
8
+ const override = process.env.SF_PREFLIGHT_CLI;
9
+ if (override)
10
+ return path.resolve(override);
11
+ const require = createRequire(import.meta.url);
12
+ const pkgFile = require.resolve("sf-preflight/package.json");
13
+ const pkg = JSON.parse(readFileSync(pkgFile, "utf8"));
14
+ const bin = pkg.bin?.preflight;
15
+ if (!bin)
16
+ throw new Error("The sf-preflight package has no `preflight` binary.");
17
+ const file = path.join(path.dirname(pkgFile), bin);
18
+ if (!existsSync(file))
19
+ throw new Error(`sf-preflight is installed but ${file} is missing.`);
20
+ return file;
21
+ }
22
+ /**
23
+ * Turn `sf` conventions into the preflight CLI's own flags.
24
+ * - `--target-org X` / `-o X` / `--target-org=X` become `--org X`, for commands that read an org
25
+ * (there `-o` is the org, as in every sf command; the CLI's own `-o` is spelled `--out`).
26
+ * - `--json` becomes `--format json` for commands that have formats, and is dropped for the rest.
27
+ *
28
+ * Everything else is passed through untouched and in order, so every flag of the CLI works.
29
+ */
30
+ export function translate(argv, opts) {
31
+ const out = [];
32
+ let json = false;
33
+ let formatGiven = false;
34
+ for (let i = 0; i < argv.length; i++) {
35
+ const a = argv[i];
36
+ if (a === "--") {
37
+ out.push(...argv.slice(i));
38
+ break;
39
+ }
40
+ if (a === "--json") {
41
+ json = true;
42
+ }
43
+ else if (opts.org && (a === "--target-org" || a === "-o")) {
44
+ out.push("--org");
45
+ }
46
+ else if (opts.org && a.startsWith("--target-org=")) {
47
+ out.push("--org", a.slice("--target-org=".length));
48
+ }
49
+ else if (opts.org && a.startsWith("-o=")) {
50
+ out.push("--org", a.slice(3));
51
+ }
52
+ else {
53
+ if (a === "--format" || a.startsWith("--format="))
54
+ formatGiven = true;
55
+ out.push(a);
56
+ }
57
+ }
58
+ if (json && opts.hasFormat && !formatGiven)
59
+ out.push("--format", "json");
60
+ return out;
61
+ }
62
+ /** Run the preflight CLI with the terminal attached, and resolve with its exit code. */
63
+ export function runCli(command, args) {
64
+ return new Promise((resolve, reject) => {
65
+ const child = spawn(process.execPath, [cliPath(), command, ...args], { stdio: "inherit" });
66
+ child.on("error", reject);
67
+ child.on("close", (code, signal) => resolve(code ?? (signal ? 1 : 0)));
68
+ });
69
+ }
@@ -0,0 +1,570 @@
1
+ {
2
+ "commands": {
3
+ "preflight:agent-tests": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "Runs tests that already exist in the target org (a sandbox, scratch org or Developer Edition org with the change deployed). Production is refused unless --allow-production.",
7
+ "examples": [
8
+ "<%= config.bin %> <%= command.id %> --base origin/main --dry-run",
9
+ "<%= config.bin %> <%= command.id %> --base origin/main --target-org my-sandbox"
10
+ ],
11
+ "flags": {
12
+ "project": {
13
+ "char": "p",
14
+ "name": "project",
15
+ "summary": "SFDX project directory (default: the current directory).",
16
+ "hasDynamicHelp": false,
17
+ "multiple": false,
18
+ "type": "option"
19
+ },
20
+ "target-org": {
21
+ "char": "o",
22
+ "name": "target-org",
23
+ "summary": "Org to read from, by username or alias (an `sf org login` org).",
24
+ "hasDynamicHelp": false,
25
+ "multiple": false,
26
+ "type": "option"
27
+ },
28
+ "base": {
29
+ "char": "b",
30
+ "name": "base",
31
+ "summary": "Git base ref, e.g. origin/main.",
32
+ "hasDynamicHelp": false,
33
+ "multiple": false,
34
+ "type": "option"
35
+ },
36
+ "head": {
37
+ "name": "head",
38
+ "summary": "Git head ref (default: the working tree).",
39
+ "hasDynamicHelp": false,
40
+ "multiple": false,
41
+ "type": "option"
42
+ },
43
+ "files": {
44
+ "char": "f",
45
+ "name": "files",
46
+ "summary": "Explicit changed files instead of a git diff.",
47
+ "hasDynamicHelp": false,
48
+ "multiple": true,
49
+ "type": "option"
50
+ },
51
+ "dry-run": {
52
+ "name": "dry-run",
53
+ "summary": "List the tests that would run, without running them.",
54
+ "allowNo": false,
55
+ "type": "boolean"
56
+ },
57
+ "allow-production": {
58
+ "name": "allow-production",
59
+ "summary": "Allow running in a production org.",
60
+ "allowNo": false,
61
+ "type": "boolean"
62
+ },
63
+ "format": {
64
+ "name": "format",
65
+ "summary": "Output format.",
66
+ "hasDynamicHelp": false,
67
+ "multiple": false,
68
+ "options": [
69
+ "md",
70
+ "json"
71
+ ],
72
+ "type": "option"
73
+ },
74
+ "json": {
75
+ "name": "json",
76
+ "summary": "Print JSON (same as --format json).",
77
+ "allowNo": false,
78
+ "type": "boolean"
79
+ }
80
+ },
81
+ "hasDynamicHelp": false,
82
+ "hiddenAliases": [],
83
+ "id": "preflight:agent-tests",
84
+ "pluginAlias": "sf-plugin-preflight",
85
+ "pluginName": "sf-plugin-preflight",
86
+ "pluginType": "core",
87
+ "strict": false,
88
+ "summary": "Pick the Agentforce Testing Center tests a change affects, and run them in an org.",
89
+ "enableJsonFlag": false,
90
+ "isESM": true,
91
+ "relativePath": [
92
+ "dist",
93
+ "commands",
94
+ "preflight",
95
+ "agent-tests.js"
96
+ ]
97
+ },
98
+ "preflight:agents": {
99
+ "aliases": [],
100
+ "args": {},
101
+ "examples": [
102
+ "<%= config.bin %> <%= command.id %>",
103
+ "<%= config.bin %> <%= command.id %> Sales_Agent"
104
+ ],
105
+ "flags": {
106
+ "project": {
107
+ "char": "p",
108
+ "name": "project",
109
+ "summary": "SFDX project directory (default: the current directory).",
110
+ "hasDynamicHelp": false,
111
+ "multiple": false,
112
+ "type": "option"
113
+ },
114
+ "format": {
115
+ "name": "format",
116
+ "summary": "Output format.",
117
+ "hasDynamicHelp": false,
118
+ "multiple": false,
119
+ "options": [
120
+ "md",
121
+ "json"
122
+ ],
123
+ "type": "option"
124
+ },
125
+ "json": {
126
+ "name": "json",
127
+ "summary": "Print JSON (same as --format json).",
128
+ "allowNo": false,
129
+ "type": "boolean"
130
+ }
131
+ },
132
+ "hasDynamicHelp": false,
133
+ "hiddenAliases": [],
134
+ "id": "preflight:agents",
135
+ "pluginAlias": "sf-plugin-preflight",
136
+ "pluginName": "sf-plugin-preflight",
137
+ "pluginType": "core",
138
+ "strict": false,
139
+ "summary": "List Agentforce agents, or explain what one agent's actions call, save and need.",
140
+ "enableJsonFlag": false,
141
+ "isESM": true,
142
+ "relativePath": [
143
+ "dist",
144
+ "commands",
145
+ "preflight",
146
+ "agents.js"
147
+ ]
148
+ },
149
+ "preflight:analyze": {
150
+ "aliases": [],
151
+ "args": {},
152
+ "description": "Reads the git diff (or the files you name) and follows it through the metadata in your SFDX project. Nothing is deployed and no org is needed. With --target-org, read-only facts from the org (such as active flows) are added.",
153
+ "examples": [
154
+ "<%= config.bin %> <%= command.id %> --base origin/main",
155
+ "<%= config.bin %> <%= command.id %> --base origin/main --gate --md-out preflight.md",
156
+ "<%= config.bin %> <%= command.id %> --files force-app/main/default/classes/Foo.cls --json"
157
+ ],
158
+ "flags": {
159
+ "project": {
160
+ "char": "p",
161
+ "name": "project",
162
+ "summary": "SFDX project directory (default: the current directory).",
163
+ "hasDynamicHelp": false,
164
+ "multiple": false,
165
+ "type": "option"
166
+ },
167
+ "target-org": {
168
+ "char": "o",
169
+ "name": "target-org",
170
+ "summary": "Org to read from, by username or alias (an `sf org login` org).",
171
+ "hasDynamicHelp": false,
172
+ "multiple": false,
173
+ "type": "option"
174
+ },
175
+ "base": {
176
+ "char": "b",
177
+ "name": "base",
178
+ "summary": "Git base ref, e.g. origin/main.",
179
+ "hasDynamicHelp": false,
180
+ "multiple": false,
181
+ "type": "option"
182
+ },
183
+ "head": {
184
+ "name": "head",
185
+ "summary": "Git head ref (default: the working tree).",
186
+ "hasDynamicHelp": false,
187
+ "multiple": false,
188
+ "type": "option"
189
+ },
190
+ "files": {
191
+ "char": "f",
192
+ "name": "files",
193
+ "summary": "Explicit changed files instead of a git diff.",
194
+ "hasDynamicHelp": false,
195
+ "multiple": true,
196
+ "type": "option"
197
+ },
198
+ "format": {
199
+ "name": "format",
200
+ "summary": "Output format.",
201
+ "hasDynamicHelp": false,
202
+ "multiple": false,
203
+ "options": [
204
+ "md",
205
+ "json",
206
+ "sarif",
207
+ "junit"
208
+ ],
209
+ "type": "option"
210
+ },
211
+ "gate": {
212
+ "name": "gate",
213
+ "summary": "Evaluate the quality gate from .preflight.json; exit 2 when it fails.",
214
+ "allowNo": false,
215
+ "type": "boolean"
216
+ },
217
+ "json": {
218
+ "name": "json",
219
+ "summary": "Print JSON (same as --format json).",
220
+ "allowNo": false,
221
+ "type": "boolean"
222
+ }
223
+ },
224
+ "hasDynamicHelp": false,
225
+ "hiddenAliases": [],
226
+ "id": "preflight:analyze",
227
+ "pluginAlias": "sf-plugin-preflight",
228
+ "pluginName": "sf-plugin-preflight",
229
+ "pluginType": "core",
230
+ "strict": false,
231
+ "summary": "See what a Salesforce change sets off: flows, triggers, validation rules, roll-ups, permissions.",
232
+ "enableJsonFlag": false,
233
+ "isESM": true,
234
+ "relativePath": [
235
+ "dist",
236
+ "commands",
237
+ "preflight",
238
+ "analyze.js"
239
+ ]
240
+ },
241
+ "preflight:evidence": {
242
+ "aliases": [],
243
+ "args": {},
244
+ "examples": [
245
+ "<%= config.bin %> <%= command.id %> --base origin/main --out preflight-evidence.json"
246
+ ],
247
+ "flags": {
248
+ "project": {
249
+ "char": "p",
250
+ "name": "project",
251
+ "summary": "SFDX project directory (default: the current directory).",
252
+ "hasDynamicHelp": false,
253
+ "multiple": false,
254
+ "type": "option"
255
+ },
256
+ "base": {
257
+ "char": "b",
258
+ "name": "base",
259
+ "summary": "Git base ref, e.g. origin/main.",
260
+ "hasDynamicHelp": false,
261
+ "multiple": false,
262
+ "type": "option"
263
+ },
264
+ "head": {
265
+ "name": "head",
266
+ "summary": "Git head ref (default: the working tree).",
267
+ "hasDynamicHelp": false,
268
+ "multiple": false,
269
+ "type": "option"
270
+ },
271
+ "files": {
272
+ "char": "f",
273
+ "name": "files",
274
+ "summary": "Explicit changed files instead of a git diff.",
275
+ "hasDynamicHelp": false,
276
+ "multiple": true,
277
+ "type": "option"
278
+ },
279
+ "json": {
280
+ "name": "json",
281
+ "summary": "Accepted; no effect.",
282
+ "allowNo": false,
283
+ "type": "boolean"
284
+ }
285
+ },
286
+ "hasDynamicHelp": false,
287
+ "hiddenAliases": [],
288
+ "id": "preflight:evidence",
289
+ "pluginAlias": "sf-plugin-preflight",
290
+ "pluginName": "sf-plugin-preflight",
291
+ "pluginType": "core",
292
+ "strict": false,
293
+ "summary": "Write the evidence pack for a change: what changed, findings, tests, approvals, gate.",
294
+ "enableJsonFlag": false,
295
+ "isESM": true,
296
+ "relativePath": [
297
+ "dist",
298
+ "commands",
299
+ "preflight",
300
+ "evidence.js"
301
+ ]
302
+ },
303
+ "preflight:explain": {
304
+ "aliases": [],
305
+ "args": {},
306
+ "examples": [
307
+ "<%= config.bin %> <%= command.id %> Opportunity"
308
+ ],
309
+ "flags": {
310
+ "project": {
311
+ "char": "p",
312
+ "name": "project",
313
+ "summary": "SFDX project directory (default: the current directory).",
314
+ "hasDynamicHelp": false,
315
+ "multiple": false,
316
+ "type": "option"
317
+ },
318
+ "json": {
319
+ "name": "json",
320
+ "summary": "Accepted; no effect.",
321
+ "allowNo": false,
322
+ "type": "boolean"
323
+ }
324
+ },
325
+ "hasDynamicHelp": false,
326
+ "hiddenAliases": [],
327
+ "id": "preflight:explain",
328
+ "pluginAlias": "sf-plugin-preflight",
329
+ "pluginName": "sf-plugin-preflight",
330
+ "pluginType": "core",
331
+ "strict": false,
332
+ "summary": "Show what runs, in order, when records of an object are saved.",
333
+ "enableJsonFlag": false,
334
+ "isESM": true,
335
+ "relativePath": [
336
+ "dist",
337
+ "commands",
338
+ "preflight",
339
+ "explain.js"
340
+ ]
341
+ },
342
+ "preflight:incidents": {
343
+ "aliases": [],
344
+ "args": {},
345
+ "description": "Reads errors from the target org (read-only; production is fine) and searches git history.",
346
+ "examples": [
347
+ "<%= config.bin %> <%= command.id %> --target-org prod --since 7d"
348
+ ],
349
+ "flags": {
350
+ "project": {
351
+ "char": "p",
352
+ "name": "project",
353
+ "summary": "SFDX project directory (default: the current directory).",
354
+ "hasDynamicHelp": false,
355
+ "multiple": false,
356
+ "type": "option"
357
+ },
358
+ "target-org": {
359
+ "char": "o",
360
+ "name": "target-org",
361
+ "summary": "Org to read from, by username or alias (an `sf org login` org).",
362
+ "hasDynamicHelp": false,
363
+ "multiple": false,
364
+ "type": "option"
365
+ },
366
+ "since": {
367
+ "name": "since",
368
+ "summary": "Errors since a duration (7d, 24h, 2w) or a date.",
369
+ "hasDynamicHelp": false,
370
+ "multiple": false,
371
+ "type": "option"
372
+ },
373
+ "format": {
374
+ "name": "format",
375
+ "summary": "Output format.",
376
+ "hasDynamicHelp": false,
377
+ "multiple": false,
378
+ "options": [
379
+ "md",
380
+ "json"
381
+ ],
382
+ "type": "option"
383
+ },
384
+ "json": {
385
+ "name": "json",
386
+ "summary": "Print JSON (same as --format json).",
387
+ "allowNo": false,
388
+ "type": "boolean"
389
+ }
390
+ },
391
+ "hasDynamicHelp": false,
392
+ "hiddenAliases": [],
393
+ "id": "preflight:incidents",
394
+ "pluginAlias": "sf-plugin-preflight",
395
+ "pluginName": "sf-plugin-preflight",
396
+ "pluginType": "core",
397
+ "strict": false,
398
+ "summary": "Match recent org errors to the recent changes that probably caused them.",
399
+ "enableJsonFlag": false,
400
+ "isESM": true,
401
+ "relativePath": [
402
+ "dist",
403
+ "commands",
404
+ "preflight",
405
+ "incidents.js"
406
+ ]
407
+ },
408
+ "preflight:rollback": {
409
+ "aliases": [],
410
+ "args": {},
411
+ "description": "Prints the commands; nothing is deployed.",
412
+ "examples": [
413
+ "<%= config.bin %> <%= command.id %> a1b2c3d --target-org my-sandbox"
414
+ ],
415
+ "flags": {
416
+ "project": {
417
+ "char": "p",
418
+ "name": "project",
419
+ "summary": "SFDX project directory (default: the current directory).",
420
+ "hasDynamicHelp": false,
421
+ "multiple": false,
422
+ "type": "option"
423
+ },
424
+ "target-org": {
425
+ "char": "o",
426
+ "name": "target-org",
427
+ "summary": "Org to read from, by username or alias (an `sf org login` org).",
428
+ "hasDynamicHelp": false,
429
+ "multiple": false,
430
+ "type": "option"
431
+ },
432
+ "format": {
433
+ "name": "format",
434
+ "summary": "Output format.",
435
+ "hasDynamicHelp": false,
436
+ "multiple": false,
437
+ "options": [
438
+ "md",
439
+ "json"
440
+ ],
441
+ "type": "option"
442
+ },
443
+ "json": {
444
+ "name": "json",
445
+ "summary": "Print JSON (same as --format json).",
446
+ "allowNo": false,
447
+ "type": "boolean"
448
+ }
449
+ },
450
+ "hasDynamicHelp": false,
451
+ "hiddenAliases": [],
452
+ "id": "preflight:rollback",
453
+ "pluginAlias": "sf-plugin-preflight",
454
+ "pluginName": "sf-plugin-preflight",
455
+ "pluginType": "core",
456
+ "strict": false,
457
+ "summary": "Plan a partial rollback of a change: restore or deactivate only the components that need it.",
458
+ "enableJsonFlag": false,
459
+ "isESM": true,
460
+ "relativePath": [
461
+ "dist",
462
+ "commands",
463
+ "preflight",
464
+ "rollback.js"
465
+ ]
466
+ },
467
+ "preflight:tests": {
468
+ "aliases": [],
469
+ "args": {},
470
+ "description": "Writes test classes to a folder. With --validate and --target-org it runs them in a check-only deployment: nothing is saved in the org. Production orgs are refused unless --allow-production.",
471
+ "examples": [
472
+ "<%= config.bin %> <%= command.id %> --base origin/main --dry-run",
473
+ "<%= config.bin %> <%= command.id %> --base origin/main --validate --target-org my-sandbox"
474
+ ],
475
+ "flags": {
476
+ "project": {
477
+ "char": "p",
478
+ "name": "project",
479
+ "summary": "SFDX project directory (default: the current directory).",
480
+ "hasDynamicHelp": false,
481
+ "multiple": false,
482
+ "type": "option"
483
+ },
484
+ "target-org": {
485
+ "char": "o",
486
+ "name": "target-org",
487
+ "summary": "Org to read from, by username or alias (an `sf org login` org).",
488
+ "hasDynamicHelp": false,
489
+ "multiple": false,
490
+ "type": "option"
491
+ },
492
+ "base": {
493
+ "char": "b",
494
+ "name": "base",
495
+ "summary": "Git base ref, e.g. origin/main.",
496
+ "hasDynamicHelp": false,
497
+ "multiple": false,
498
+ "type": "option"
499
+ },
500
+ "head": {
501
+ "name": "head",
502
+ "summary": "Git head ref (default: the working tree).",
503
+ "hasDynamicHelp": false,
504
+ "multiple": false,
505
+ "type": "option"
506
+ },
507
+ "files": {
508
+ "char": "f",
509
+ "name": "files",
510
+ "summary": "Explicit changed files instead of a git diff.",
511
+ "hasDynamicHelp": false,
512
+ "multiple": true,
513
+ "type": "option"
514
+ },
515
+ "dry-run": {
516
+ "name": "dry-run",
517
+ "summary": "Print the summary without writing files.",
518
+ "allowNo": false,
519
+ "type": "boolean"
520
+ },
521
+ "validate": {
522
+ "name": "validate",
523
+ "summary": "Run the tests in the target org with a check-only deployment.",
524
+ "allowNo": false,
525
+ "type": "boolean"
526
+ },
527
+ "allow-production": {
528
+ "name": "allow-production",
529
+ "summary": "Allow --validate in a production org.",
530
+ "allowNo": false,
531
+ "type": "boolean"
532
+ },
533
+ "format": {
534
+ "name": "format",
535
+ "summary": "Summary format.",
536
+ "hasDynamicHelp": false,
537
+ "multiple": false,
538
+ "options": [
539
+ "md",
540
+ "json"
541
+ ],
542
+ "type": "option"
543
+ },
544
+ "json": {
545
+ "name": "json",
546
+ "summary": "Print JSON (same as --format json).",
547
+ "allowNo": false,
548
+ "type": "boolean"
549
+ }
550
+ },
551
+ "hasDynamicHelp": false,
552
+ "hiddenAliases": [],
553
+ "id": "preflight:tests",
554
+ "pluginAlias": "sf-plugin-preflight",
555
+ "pluginName": "sf-plugin-preflight",
556
+ "pluginType": "core",
557
+ "strict": false,
558
+ "summary": "Generate Apex tests for what a change touches (bulk, recursion, idempotency, swallowed errors).",
559
+ "enableJsonFlag": false,
560
+ "isESM": true,
561
+ "relativePath": [
562
+ "dist",
563
+ "commands",
564
+ "preflight",
565
+ "tests.js"
566
+ ]
567
+ }
568
+ },
569
+ "version": "0.8.0"
570
+ }
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "sf-plugin-preflight",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.8.0",
4
+ "description": "Salesforce CLI plugin for sf-preflight: see what a Salesforce change sets off before it ships. Adds `sf preflight analyze`, `tests`, `agents`, `incidents`, `rollback` and more.",
5
+ "license": "Apache-2.0",
6
+ "author": "sf-preflight contributors",
7
+ "homepage": "https://visparashar.github.io/sf-preflight/",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/visparashar/sf-preflight.git",
11
+ "directory": "sf-plugin"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/visparashar/sf-preflight/issues"
15
+ },
16
+ "type": "module",
17
+ "keywords": [
18
+ "salesforce",
19
+ "sfdx",
20
+ "sf",
21
+ "sf-plugin",
22
+ "oclif-plugin",
23
+ "devops",
24
+ "impact-analysis"
25
+ ],
26
+ "engines": {
27
+ "node": ">=22.13"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "oclif.manifest.json",
32
+ "README.md",
33
+ "CHANGELOG.md"
34
+ ],
35
+ "publishConfig": {
36
+ "access": "public",
37
+ "provenance": true
38
+ },
39
+ "oclif": {
40
+ "commands": "./dist/commands",
41
+ "bin": "sf",
42
+ "topicSeparator": " ",
43
+ "topics": {
44
+ "preflight": {
45
+ "description": "See what a Salesforce change sets off before it ships."
46
+ }
47
+ }
48
+ },
49
+ "scripts": {
50
+ "build": "tsc -p tsconfig.json",
51
+ "typecheck": "tsc -p tsconfig.json --noEmit",
52
+ "manifest": "oclif manifest",
53
+ "test": "npm run build && node --test test/plugin.test.mjs",
54
+ "prepack": "npm run build && oclif manifest"
55
+ },
56
+ "dependencies": {
57
+ "@oclif/core": "^4.0.0",
58
+ "sf-preflight": "^0.8.0"
59
+ },
60
+ "devDependencies": {
61
+ "@types/node": "^26.6.4",
62
+ "oclif": "^4.0.0",
63
+ "typescript": "^7.0.2"
64
+ }
65
+ }