@williambeto/ai-workflow 2.4.9 → 2.6.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.
- package/CHANGELOG.md +1138 -168
- package/README.md +203 -77
- package/bin/ai-workflow.d.ts +3 -0
- package/bin/ai-workflow.js +2669 -1
- package/bin/ai-workflow.js.map +1 -0
- package/chunk-2AOV2ATY.js +749 -0
- package/chunk-5WRI5ZAA.js +31 -0
- package/chunk-6EMG6FAO.js +734 -0
- package/chunk-UHLZX2EG.js +1827 -0
- package/core/index.d.ts +440 -0
- package/core/index.js +32 -0
- package/core/index.js.map +1 -0
- package/dist-assets/AGENTS.md +23 -6
- package/dist-assets/agents/atlas.md +8 -8
- package/dist-assets/agents/nexus.md +4 -4
- package/dist-assets/agents/orion.md +1 -1
- package/dist-assets/agents/phoenix.md +1 -1
- package/dist-assets/agents/sage.md +1 -1
- package/dist-assets/commands/implement.md +1 -1
- package/dist-assets/docs/QUICKSTART.md +8 -8
- package/dist-assets/docs/cli-reference.md +7 -7
- package/dist-assets/docs/compatibility/provider-usage.md +1 -9
- package/dist-assets/docs/compatibility/runtime-matrix.md +5 -6
- package/dist-assets/docs/consumer-onboarding.md +2 -2
- package/dist-assets/docs/design-patterns-policy.md +1 -1
- package/dist-assets/docs/npm-consumer-quickstart.md +70 -109
- package/dist-assets/docs/policies/01-BRANCH_GATE.md +1 -1
- package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +2 -2
- package/dist-assets/docs/policies/08-PRODUCT_TRUTHFULNESS_AND_PROJECT_DOCS.md +1 -1
- package/dist-assets/docs/policies/10-BEHAVIORAL_CONTRACT_HARDENING.md +1 -1
- package/dist-assets/docs/policies/ORCHESTRATION_PROTOCOL.md +8 -9
- package/dist-assets/docs/policies/PROCEDURE_DELIVERY_ARTIFACTS.md +4 -10
- package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +1 -1
- package/dist-assets/docs/profiles/README.md +2 -2
- package/dist-assets/docs/specs/runtime-operational-contract.md +12 -12
- package/dist-assets/docs/troubleshooting-guide.md +2 -2
- package/dist-assets/docs/visual-validation-guide.md +3 -3
- package/dist-assets/schemas/evidence.schema.json +109 -0
- package/dist-assets/templates/ANTIGRAVITY.md.template +7 -5
- package/dist-assets/templates/CLAUDE.md.template +7 -5
- package/dist-assets/templates/CODEX.md.template +7 -5
- package/evidence-validator-76ZQQYDU.js +6661 -0
- package/package.json +7 -64
- package/{src/core/workspace/read-only-workspace.js → read-only-workspace-PZBE7KAX.js} +14 -31
- package/{src/commands/validate.js → validate-Q2NLLQ5G.js} +30 -22
- package/AGENTS.md +0 -34
- package/PUBLISH_MANIFEST.json +0 -34
- package/dist-assets/templates/.geminiignore.template +0 -8
- package/dist-assets/templates/GEMINI.md.template +0 -20
- package/docs/compatibility/provider-usage.md +0 -46
- package/docs/compatibility/runtime-matrix.md +0 -31
- package/docs/getting-started/DESKTOP_PROMPT.md +0 -52
- package/docs/getting-started/authoring-custom-skills.md +0 -80
- package/docs/getting-started/quickstart.md +0 -117
- package/docs/getting-started/troubleshooting.md +0 -123
- package/docs/getting-started/upgrading-to-v2.md +0 -62
- package/src/adapters/index.js +0 -4
- package/src/adapters/platforms/antigravity.js +0 -382
- package/src/adapters/platforms/claude.js +0 -126
- package/src/adapters/platforms/codex.js +0 -113
- package/src/adapters/platforms/gemini.js +0 -245
- package/src/cli.js +0 -155
- package/src/commands/clean.js +0 -106
- package/src/commands/collect-evidence.js +0 -25
- package/src/commands/doctor.js +0 -202
- package/src/commands/execute.js +0 -382
- package/src/commands/init.js +0 -359
- package/src/commands/run.js +0 -112
- package/src/core/backup.js +0 -65
- package/src/core/completion-contract.js +0 -35
- package/src/core/delegation-controller.js +0 -193
- package/src/core/evidence/evidence-ledger.js +0 -98
- package/src/core/execution-planner.js +0 -62
- package/src/core/filesystem.js +0 -141
- package/src/core/finalization/finalizer.js +0 -165
- package/src/core/finalization/workspace-snapshot.js +0 -110
- package/src/core/gates/branch-gate.js +0 -134
- package/src/core/gates/merge-gate.js +0 -74
- package/src/core/handoff/handoff-engine.js +0 -104
- package/src/core/healing/cli-remediation-executor.js +0 -153
- package/src/core/healing/healer-engine.js +0 -212
- package/src/core/healing/runtime-remediation-executor.js +0 -136
- package/src/core/identity.js +0 -43
- package/src/core/install-plan.js +0 -34
- package/src/core/opencode-merge.js +0 -150
- package/src/core/package-assets.js +0 -141
- package/src/core/request-classifier.js +0 -73
- package/src/core/request-router.js +0 -289
- package/src/core/runtime/opencode-adapter.js +0 -202
- package/src/core/sdd/validator.js +0 -67
- package/src/core/statuses.js +0 -29
- package/src/core/symlink-layout.js +0 -93
- package/src/core/templates.js +0 -229
- package/src/core/validation/artifact-fidelity-gate.js +0 -445
- package/src/core/validation/canonical-finalization.js +0 -43
- package/src/core/validation/delivery-decision-engine.js +0 -344
- package/src/core/validation/evidence-collector.js +0 -245
- package/src/core/validation/quality-guard.js +0 -251
- package/src/core/validation/stack-detector.js +0 -65
- package/src/core/validation/validation-planner.js +0 -134
- package/src/core/validation/visual-verifier.js +0 -175
- package/src/core/workflow-profiles.js +0 -107
- package/src/core/workflow-state-machine.js +0 -46
package/src/commands/clean.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { existsSync, rmSync, lstatSync, unlinkSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import readline from "node:readline";
|
|
4
|
-
|
|
5
|
-
function askConfirmation(question) {
|
|
6
|
-
const rl = readline.createInterface({
|
|
7
|
-
input: process.stdin,
|
|
8
|
-
output: process.stdout
|
|
9
|
-
});
|
|
10
|
-
return new Promise((resolve) => {
|
|
11
|
-
rl.question(question, (answer) => {
|
|
12
|
-
rl.close();
|
|
13
|
-
resolve(answer.trim().toLowerCase());
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function isSymlink(filePath) {
|
|
19
|
-
try {
|
|
20
|
-
return lstatSync(filePath).isSymbolicLink();
|
|
21
|
-
} catch (_) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function runClean({ cwd, yes = false, dryRun = false, purgeAgents = false }) {
|
|
27
|
-
const targets = [
|
|
28
|
-
".ai-workflow",
|
|
29
|
-
"opencode.jsonc",
|
|
30
|
-
".workflow-state.json",
|
|
31
|
-
"opencode"
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
const hasAgents = existsSync(join(cwd, ".agents"));
|
|
35
|
-
|
|
36
|
-
// Check what actually exists
|
|
37
|
-
const existingTargets = targets
|
|
38
|
-
.map(name => join(cwd, name))
|
|
39
|
-
.filter(path => existsSync(path) || isSymlink(path));
|
|
40
|
-
|
|
41
|
-
if (existingTargets.length === 0 && (!hasAgents || !purgeAgents)) {
|
|
42
|
-
console.log("No AI Workflow configuration or files found in the current project.");
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
console.log("\n[AI WORKFLOW] De-initialization");
|
|
47
|
-
console.log("===============================");
|
|
48
|
-
|
|
49
|
-
if (dryRun) {
|
|
50
|
-
console.log("[DRY RUN] The following items would be deleted:");
|
|
51
|
-
existingTargets.forEach(path => console.log(` - ${path}`));
|
|
52
|
-
if (hasAgents && purgeAgents) {
|
|
53
|
-
console.log(` - ${join(cwd, ".agents")} (customizations folder)`);
|
|
54
|
-
} else if (hasAgents) {
|
|
55
|
-
console.log(` - NOTE: ${join(cwd, ".agents")} exists but will NOT be deleted (run with --purge-agents to delete)`);
|
|
56
|
-
}
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Interactive prompts if --yes / -y is not passed
|
|
61
|
-
if (!yes) {
|
|
62
|
-
if (!process.stdin.isTTY) {
|
|
63
|
-
console.log("Error: Non-interactive terminal detected. Run with --yes to bypass confirmation.");
|
|
64
|
-
if (process.env.NODE_ENV === "test") {
|
|
65
|
-
// Allow tests to bypass TTY block if they mock/stub process.stdin
|
|
66
|
-
} else {
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const confirmDeinit = await askConfirmation("Are you sure you want to de-initialize AI Workflow from the project? [y/N] ");
|
|
72
|
-
if (confirmDeinit !== "y" && confirmDeinit !== "yes") {
|
|
73
|
-
console.log("Aborted.");
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (hasAgents && !purgeAgents) {
|
|
78
|
-
const confirmPurge = await askConfirmation("A custom '.agents' folder was detected. Do you want to purge it (this deletes custom prompts/rules/skills)? [y/N] ");
|
|
79
|
-
if (confirmPurge === "y" || confirmPurge === "yes") {
|
|
80
|
-
purgeAgents = true;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Add .agents if requested and exists
|
|
86
|
-
if (purgeAgents && hasAgents) {
|
|
87
|
-
existingTargets.push(join(cwd, ".agents"));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
console.log("\nRemoving files...");
|
|
91
|
-
for (const target of existingTargets) {
|
|
92
|
-
try {
|
|
93
|
-
if (isSymlink(target)) {
|
|
94
|
-
unlinkSync(target);
|
|
95
|
-
console.log(`Deleted symlink: ${target}`);
|
|
96
|
-
} else {
|
|
97
|
-
rmSync(target, { recursive: true, force: true });
|
|
98
|
-
console.log(`Deleted directory/file: ${target}`);
|
|
99
|
-
}
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error(`Failed to delete ${target}: ${error.message}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
console.log("\nAI Workflow has been successfully removed from this project.");
|
|
106
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { EvidenceCollector } from "../core/validation/evidence-collector.js";
|
|
2
|
-
import { QualityGuard } from "../core/validation/quality-guard.js";
|
|
3
|
-
import { ValidationPlanner } from "../core/validation/validation-planner.js";
|
|
4
|
-
import { buildDeliverySummary, formatDeliverySummary } from "../core/validation/canonical-finalization.js";
|
|
5
|
-
import fs from "node:fs/promises";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
|
|
8
|
-
export async function runCollectEvidence({ cwd, exitOnError = true, taskSlug = null, mode = null, dryRun = false, profile = "generic", branchRecovery = "NOT_RECORDED", userRequest = null, explicitApprovals = [], visualDist = null, port = null }) {
|
|
9
|
-
const qualityGuard = new QualityGuard({ cwd, taskSlug, mode });
|
|
10
|
-
const planner = new ValidationPlanner({ cwd, qualityGuard });
|
|
11
|
-
const tasks = await planner.plan(profile);
|
|
12
|
-
|
|
13
|
-
const persist = !dryRun && mode === "full";
|
|
14
|
-
const collector = new EvidenceCollector({ cwd, timeout: 60000, taskSlug, mode, profile, branchRecovery, userRequest, explicitApprovals, visualDist, port });
|
|
15
|
-
const evidence = await collector.collect(tasks, { writeArtifact: persist });
|
|
16
|
-
const summary = buildDeliverySummary({ evidence });
|
|
17
|
-
|
|
18
|
-
console.log("\n--- Delivery Summary ---");
|
|
19
|
-
console.log(formatDeliverySummary(summary));
|
|
20
|
-
console.log(persist ? "\nArtifact: EVIDENCE.json" : "\nArtifact: NOT_REQUIRED for quick/standard mode");
|
|
21
|
-
console.log(`Commands processed: ${evidence.commands.length}\n`);
|
|
22
|
-
|
|
23
|
-
if (evidence.status === "BLOCKED" && exitOnError) process.exit(1);
|
|
24
|
-
return evidence;
|
|
25
|
-
}
|
package/src/commands/doctor.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { exists, readJson, readJsonc } from "../core/filesystem.js";
|
|
4
|
-
import { OpenCodeAdapter } from "../core/runtime/opencode-adapter.js";
|
|
5
|
-
|
|
6
|
-
const REQUIRED_FILES = [
|
|
7
|
-
".ai-workflow",
|
|
8
|
-
"opencode/README.md"
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
async function isSymlink(filePath) {
|
|
12
|
-
try {
|
|
13
|
-
const stat = await fs.lstat(filePath);
|
|
14
|
-
return stat.isSymbolicLink();
|
|
15
|
-
} catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function runDoctor({ cwd }) {
|
|
21
|
-
let hasFailure = false;
|
|
22
|
-
let hasWarning = false;
|
|
23
|
-
let managedBlocks = [];
|
|
24
|
-
|
|
25
|
-
console.log("Doctor report:");
|
|
26
|
-
|
|
27
|
-
// Check OpenCode runtime
|
|
28
|
-
const adapter = new OpenCodeAdapter({ cwd });
|
|
29
|
-
const inspection = await adapter.inspect();
|
|
30
|
-
if (inspection.available) {
|
|
31
|
-
console.log("PASS opencode CLI is available");
|
|
32
|
-
const supportedStr = Object.entries(inspection.supports)
|
|
33
|
-
.filter(([_, v]) => v)
|
|
34
|
-
.map(([k, _]) => k)
|
|
35
|
-
.join(", ");
|
|
36
|
-
console.log(`PASS opencode capabilities detected: ${supportedStr || "none"}`);
|
|
37
|
-
} else {
|
|
38
|
-
hasFailure = true;
|
|
39
|
-
console.log("FAIL opencode CLI is not installed or not available in PATH");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
for (const relativePath of REQUIRED_FILES) {
|
|
43
|
-
const ok = await exists(path.join(cwd, relativePath));
|
|
44
|
-
|
|
45
|
-
if (ok) {
|
|
46
|
-
console.log(`PASS ${relativePath}`);
|
|
47
|
-
} else {
|
|
48
|
-
hasFailure = true;
|
|
49
|
-
console.log(`FAIL ${relativePath} missing`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const configPathV2 = path.join(cwd, ".ai-workflow/config.json");
|
|
54
|
-
const configPathLegacy = path.join(cwd, ".ai-workflow.json");
|
|
55
|
-
let configPath = null;
|
|
56
|
-
|
|
57
|
-
if (await exists(configPathV2)) {
|
|
58
|
-
configPath = configPathV2;
|
|
59
|
-
} else if (await exists(configPathLegacy)) {
|
|
60
|
-
configPath = configPathLegacy;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (configPath) {
|
|
64
|
-
try {
|
|
65
|
-
const config = await readJson(configPath);
|
|
66
|
-
|
|
67
|
-
if (config.installMode === "project-local" || config.mode === "standalone") {
|
|
68
|
-
console.log("PASS install mode is project-local/standalone");
|
|
69
|
-
} else {
|
|
70
|
-
hasWarning = true;
|
|
71
|
-
console.log(`WARN install mode is not project-local/standalone in ${path.basename(configPath)}`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (config.profile) {
|
|
75
|
-
console.log(`PASS profile detected: ${config.profile}`);
|
|
76
|
-
} else {
|
|
77
|
-
hasWarning = true;
|
|
78
|
-
console.log(`WARN profile is missing in ${path.basename(configPath)}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (config.runtime) {
|
|
82
|
-
console.log(`PASS runtime: ${config.runtime}`);
|
|
83
|
-
} else {
|
|
84
|
-
console.log(`NOTE runtime field not present in ${path.basename(configPath)}`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (Array.isArray(config.managedBlocks)) {
|
|
88
|
-
managedBlocks = config.managedBlocks;
|
|
89
|
-
} else {
|
|
90
|
-
console.log(`NOTE managedBlocks not present in ${path.basename(configPath)} (legacy field)`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (Array.isArray(config.managedFiles)) {
|
|
94
|
-
for (const relativePath of config.managedFiles) {
|
|
95
|
-
const fileExists = await exists(path.join(cwd, relativePath));
|
|
96
|
-
if (!fileExists) {
|
|
97
|
-
hasWarning = true;
|
|
98
|
-
console.log(`WARN managed file missing: ${relativePath}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (Array.isArray(config.managedLinks)) {
|
|
104
|
-
for (const relativePath of config.managedLinks) {
|
|
105
|
-
const absolutePath = path.join(cwd, relativePath);
|
|
106
|
-
const linkExists = await exists(absolutePath);
|
|
107
|
-
if (!linkExists) {
|
|
108
|
-
hasWarning = true;
|
|
109
|
-
console.log(`WARN managed link missing: ${relativePath}`);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const isLink = await isSymlink(absolutePath);
|
|
114
|
-
if (!isLink) {
|
|
115
|
-
hasWarning = true;
|
|
116
|
-
console.log(`WARN managed link is not a symlink: ${relativePath}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
} catch {
|
|
121
|
-
hasFailure = true;
|
|
122
|
-
console.log(`FAIL ${path.basename(configPath)} is not valid JSON`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const opencodePath = path.join(cwd, "opencode.jsonc");
|
|
127
|
-
if (!(await exists(opencodePath))) {
|
|
128
|
-
hasFailure = true;
|
|
129
|
-
console.log("FAIL opencode.jsonc missing");
|
|
130
|
-
} else {
|
|
131
|
-
try {
|
|
132
|
-
const opencodeConfig = await readJsonc(opencodePath);
|
|
133
|
-
console.log("PASS opencode.jsonc is valid JSONC");
|
|
134
|
-
|
|
135
|
-
const needsAgentDefault = managedBlocks.includes("opencode.jsonc:agent.default");
|
|
136
|
-
if (!needsAgentDefault || opencodeConfig.agent?.default) {
|
|
137
|
-
console.log("PASS opencode agent.default available");
|
|
138
|
-
} else {
|
|
139
|
-
hasFailure = true;
|
|
140
|
-
console.log("FAIL opencode agent.default missing");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const requiredCommands = managedBlocks
|
|
144
|
-
.filter((block) => block.startsWith("opencode.jsonc:command."))
|
|
145
|
-
.map((block) => block.replace("opencode.jsonc:command.", ""));
|
|
146
|
-
|
|
147
|
-
if (requiredCommands.length === 0) {
|
|
148
|
-
console.log("PASS no managed opencode commands required");
|
|
149
|
-
} else {
|
|
150
|
-
const missingCommands = requiredCommands.filter((command) => !opencodeConfig.command?.[command]);
|
|
151
|
-
if (missingCommands.length === 0) {
|
|
152
|
-
console.log(`PASS opencode managed commands available (${requiredCommands.length})`);
|
|
153
|
-
} else {
|
|
154
|
-
hasFailure = true;
|
|
155
|
-
console.log(`FAIL opencode command entries missing: ${missingCommands.join(", ")}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const requiredAgents = managedBlocks
|
|
160
|
-
.filter((block) => block.startsWith("opencode.jsonc:agent.") && !block.endsWith(".default"))
|
|
161
|
-
.map((block) => block.replace("opencode.jsonc:agent.", ""));
|
|
162
|
-
|
|
163
|
-
if (requiredAgents.length === 0) {
|
|
164
|
-
console.log("PASS no managed opencode agents required");
|
|
165
|
-
} else {
|
|
166
|
-
const missingAgents = requiredAgents.filter((agent) => !opencodeConfig.agent?.[agent]);
|
|
167
|
-
if (missingAgents.length === 0) {
|
|
168
|
-
console.log(`PASS opencode managed agents available (${requiredAgents.length})`);
|
|
169
|
-
} else {
|
|
170
|
-
hasFailure = true;
|
|
171
|
-
console.log(`FAIL opencode agent entries missing: ${missingAgents.join(", ")}`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
} catch {
|
|
175
|
-
hasFailure = true;
|
|
176
|
-
console.log("FAIL opencode.jsonc is not valid JSONC");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const packageJsonPath = path.join(cwd, "package.json");
|
|
181
|
-
if (await exists(packageJsonPath)) {
|
|
182
|
-
try {
|
|
183
|
-
const packageJson = await readJson(packageJsonPath);
|
|
184
|
-
const scripts = packageJson.scripts ?? {};
|
|
185
|
-
|
|
186
|
-
if (!scripts.validate) {
|
|
187
|
-
hasWarning = true;
|
|
188
|
-
console.log("WARN package.json has no validate script");
|
|
189
|
-
}
|
|
190
|
-
} catch {
|
|
191
|
-
hasFailure = true;
|
|
192
|
-
console.log("FAIL package.json is not valid JSON");
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const finalStatus = hasFailure ? "FAIL" : hasWarning ? "PASS_WITH_NOTES" : "PASS";
|
|
197
|
-
console.log(`Final status: ${finalStatus}`);
|
|
198
|
-
|
|
199
|
-
if (hasFailure) {
|
|
200
|
-
process.exitCode = 1;
|
|
201
|
-
}
|
|
202
|
-
}
|