From 2e049fa19899f03b80aa76cbc5574cbe4a024c90 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Fri, 9 Aug 2024 18:51:54 +0200
Subject: [PATCH 01/12] feat: add a built-in console task

---
 .../internal/builtin-plugins/console/index.ts | 19 +++++
 .../builtin-plugins/console/task-action.ts    | 46 ++++++++++++
 .../src/internal/builtin-plugins/index.ts     |  9 ++-
 .../builtin-plugins/console/task-action.ts    | 74 +++++++++++++++++++
 v-next/hardhat/test/internal/cli/main.ts      |  1 +
 5 files changed, 148 insertions(+), 1 deletion(-)
 create mode 100644 v-next/hardhat/src/internal/builtin-plugins/console/index.ts
 create mode 100644 v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
 create mode 100644 v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
new file mode 100644
index 0000000000..8ac05d402e
--- /dev/null
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
@@ -0,0 +1,19 @@
+import type { HardhatPlugin } from "@ignored/hardhat-vnext-core/types/plugins";
+
+import { task } from "@ignored/hardhat-vnext-core/config";
+
+const hardhatPlugin: HardhatPlugin = {
+  id: "console",
+  tasks: [
+    task("console", "Opens a hardhat console")
+      .setAction(import.meta.resolve("./task-action.js"))
+      .addVariadicArgument({
+        name: "commands",
+        description: "Commands to run in the console",
+        defaultValue: [".help"],
+      })
+      .build(),
+  ],
+};
+
+export default hardhatPlugin;
diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
new file mode 100644
index 0000000000..18f7fb910e
--- /dev/null
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -0,0 +1,46 @@
+import type { NewTaskActionFunction } from "@ignored/hardhat-vnext-core/types/tasks";
+import type { REPLServer } from "node:repl";
+
+import path from "node:path";
+import repl from "node:repl";
+
+import { getCacheDir } from "@ignored/hardhat-vnext-core/global-dir";
+import debug from "debug";
+
+const log = debug("hardhat:core:tasks:console");
+
+interface ConsoleActionArguments {
+  commands: string[];
+}
+
+const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
+  { commands },
+  _hre,
+) => {
+  const globalCacheDir = await getCacheDir();
+
+  return new Promise<REPLServer>((resolve) => {
+    // Start a new REPL server with the default options
+    const replServer = repl.start();
+
+    // Set up the REPL history file in the global cache directory
+    const historyPath = path.join(globalCacheDir, "console-history.txt");
+    replServer.setupHistory(historyPath, (err: Error | null) => {
+      if (err !== null) {
+        log(`Failed to setup REPL history: ${err.message}`);
+      }
+    });
+
+    // Execute each command in the REPL server
+    for (const command of commands) {
+      replServer.write(`${command}\n`);
+    }
+
+    // Resolve the task action promise when the REPL server exits
+    replServer.on("exit", () => {
+      resolve(replServer);
+    });
+  });
+};
+
+export default consoleAction;
diff --git a/v-next/hardhat/src/internal/builtin-plugins/index.ts b/v-next/hardhat/src/internal/builtin-plugins/index.ts
index d8a7773f4b..00b74e4c6c 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/index.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/index.ts
@@ -1,13 +1,20 @@
 import type { HardhatPlugin } from "@ignored/hardhat-vnext-core/types/plugins";
 
 import clean from "./clean/index.js";
+import console from "./console/index.js";
 import hardhatFoo from "./hardhat-foo/index.js";
 import run from "./run/index.js";
 
 // Note: When importing a plugin, you have to export its types, so that its
 // type extensions, if any, also get loaded.
 export type * from "./clean/index.js";
+export type * from "./console/index.js";
 export type * from "./hardhat-foo/index.js";
 export type * from "./run/index.js";
 
-export const builtinPlugins: HardhatPlugin[] = [clean, hardhatFoo, run];
+export const builtinPlugins: HardhatPlugin[] = [
+  clean,
+  console,
+  hardhatFoo,
+  run,
+];
diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
new file mode 100644
index 0000000000..7b06ff7c7f
--- /dev/null
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -0,0 +1,74 @@
+import type { HardhatRuntimeEnvironment } from "@ignored/hardhat-vnext-core/types/hre";
+
+import assert from "node:assert/strict";
+import { before, describe, it } from "node:test";
+
+import { ensureError } from "@ignored/hardhat-vnext-utils/error";
+
+import { createHardhatRuntimeEnvironment } from "../../../../src/hre.js";
+import consoleAction from "../../../../src/internal/builtin-plugins/console/task-action.js";
+import { useFixtureProject } from "../../../helpers/project.js";
+
+describe("console/task-action", function () {
+  let hre: HardhatRuntimeEnvironment;
+
+  before(async function () {
+    hre = await createHardhatRuntimeEnvironment({});
+  });
+
+  describe("javascript", function () {
+    useFixtureProject("run-js-script");
+
+    it("should throw inside the console if script does not exist", async function () {
+      const replServer = await consoleAction(
+        { commands: ['await import("./scripts/non-existent.js");', ".exit"] },
+        hre,
+      );
+      ensureError(replServer.lastError);
+    });
+
+    it("should run a script inside the console successfully", async function () {
+      const replServer = await consoleAction(
+        { commands: ['await import("./scripts/success.js");', ".exit"] },
+        hre,
+      );
+      assert.equal(replServer.lastError, undefined);
+    });
+
+    it("should throw inside the console if the script throws", async function () {
+      const replServer = await consoleAction(
+        { commands: ['await import("./scripts/throws.js");', ".exit"] },
+        hre,
+      );
+      ensureError(replServer.lastError);
+    });
+  });
+
+  describe("typescript", function () {
+    useFixtureProject("run-ts-script");
+
+    it("should throw inside the console if script does not exist", async function () {
+      const replServer = await consoleAction(
+        { commands: ['await import("./scripts/non-existent.ts");', ".exit"] },
+        hre,
+      );
+      ensureError(replServer.lastError);
+    });
+
+    it("should run a script inside the console successfully", async function () {
+      const replServer = await consoleAction(
+        { commands: ['await import("./scripts/success.ts");', ".exit"] },
+        hre,
+      );
+      assert.equal(replServer.lastError, undefined);
+    });
+
+    it("should throw inside the console if the script throws", async function () {
+      const replServer = await consoleAction(
+        { commands: ['await import("./scripts/throws.ts");', ".exit"] },
+        hre,
+      );
+      ensureError(replServer.lastError);
+    });
+  });
+});
diff --git a/v-next/hardhat/test/internal/cli/main.ts b/v-next/hardhat/test/internal/cli/main.ts
index 64036921ad..8800aa764d 100644
--- a/v-next/hardhat/test/internal/cli/main.ts
+++ b/v-next/hardhat/test/internal/cli/main.ts
@@ -223,6 +223,7 @@ Usage: hardhat [GLOBAL OPTIONS] <TASK> [SUBTASK] [TASK OPTIONS] [--] [TASK ARGUM
 AVAILABLE TASKS:
 
   clean                    Clears the cache and deletes all artifacts
+  console                  Opens a hardhat console
   example                  Example task
   run                      Runs a user-defined script after compiling the project
   task                     A task that uses arg1

From 1fde8fe4c91075bf632ac6174b7b5c19c3159365 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Sat, 10 Aug 2024 11:54:34 +0200
Subject: [PATCH 02/12] fix: do not use stdio nor global cache dir during
 console tests

---
 .../internal/builtin-plugins/console/index.ts |  5 ++
 .../builtin-plugins/console/task-action.ts    | 42 +++++----
 .../builtin-plugins/console/task-action.ts    | 90 +++++++++++++++++--
 3 files changed, 115 insertions(+), 22 deletions(-)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
index 8ac05d402e..225f7441b0 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
@@ -7,6 +7,11 @@ const hardhatPlugin: HardhatPlugin = {
   tasks: [
     task("console", "Opens a hardhat console")
       .setAction(import.meta.resolve("./task-action.js"))
+      .addOption({
+        name: "history",
+        description: "Path to a history file",
+        defaultValue: "console-history.txt",
+      })
       .addVariadicArgument({
         name: "commands",
         description: "Commands to run in the console",
diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index 18f7fb910e..4928b8d800 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -11,35 +11,47 @@ const log = debug("hardhat:core:tasks:console");
 
 interface ConsoleActionArguments {
   commands: string[];
+  history: string;
+  // We accept ReplOptions as an argument to allow tests overriding the IO streams
+  options?: repl.ReplOptions;
 }
 
 const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
-  { commands },
+  { commands, history, options },
   _hre,
 ) => {
-  const globalCacheDir = await getCacheDir();
+  // Resolve the history path if it is not empty
+  let historyPath: string | undefined;
+  if (history !== "") {
+    const globalCacheDir = await getCacheDir();
+    historyPath = path.isAbsolute(history)
+      ? history
+      : path.resolve(globalCacheDir, history);
+  }
 
   return new Promise<REPLServer>((resolve) => {
     // Start a new REPL server with the default options
-    const replServer = repl.start();
-
-    // Set up the REPL history file in the global cache directory
-    const historyPath = path.join(globalCacheDir, "console-history.txt");
-    replServer.setupHistory(historyPath, (err: Error | null) => {
-      if (err !== null) {
-        log(`Failed to setup REPL history: ${err.message}`);
-      }
+    const replServer = repl.start(options);
+
+    // Resolve the task action promise only when the REPL server exits
+    replServer.on("exit", () => {
+      resolve(replServer);
     });
 
+    // Set up the REPL history file if the historyPath has been set
+    if (historyPath !== undefined) {
+      replServer.setupHistory(historyPath, (err: Error | null) => {
+        // Fail silently if the history file cannot be set up
+        if (err !== null) {
+          log(`Failed to setup REPL history: ${err.message}`);
+        }
+      });
+    }
+
     // Execute each command in the REPL server
     for (const command of commands) {
       replServer.write(`${command}\n`);
     }
-
-    // Resolve the task action promise when the REPL server exits
-    replServer.on("exit", () => {
-      resolve(replServer);
-    });
   });
 };
 
diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
index 7b06ff7c7f..b6f523b395 100644
--- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -1,7 +1,12 @@
 import type { HardhatRuntimeEnvironment } from "@ignored/hardhat-vnext-core/types/hre";
+import type repl from "node:repl";
 
 import assert from "node:assert/strict";
-import { before, describe, it } from "node:test";
+import fs from "node:fs";
+import os from "node:os";
+import path from "node:path";
+import { PassThrough } from "node:stream";
+import { afterEach, before, beforeEach, describe, it } from "node:test";
 
 import { ensureError } from "@ignored/hardhat-vnext-utils/error";
 
@@ -11,17 +16,32 @@ import { useFixtureProject } from "../../../helpers/project.js";
 
 describe("console/task-action", function () {
   let hre: HardhatRuntimeEnvironment;
+  let options: repl.ReplOptions;
 
   before(async function () {
     hre = await createHardhatRuntimeEnvironment({});
   });
 
+  beforeEach(function () {
+    const input = new PassThrough();
+    const output = new PassThrough();
+    output.pipe(process.stdout);
+    options = {
+      input,
+      output,
+    };
+  });
+
   describe("javascript", function () {
     useFixtureProject("run-js-script");
 
     it("should throw inside the console if script does not exist", async function () {
       const replServer = await consoleAction(
-        { commands: ['await import("./scripts/non-existent.js");', ".exit"] },
+        {
+          commands: ['await import("./scripts/non-existent.js");', ".exit"],
+          history: "",
+          options,
+        },
         hre,
       );
       ensureError(replServer.lastError);
@@ -29,7 +49,11 @@ describe("console/task-action", function () {
 
     it("should run a script inside the console successfully", async function () {
       const replServer = await consoleAction(
-        { commands: ['await import("./scripts/success.js");', ".exit"] },
+        {
+          commands: [".help", 'await import("./scripts/success.js");', ".exit"],
+          history: "",
+          options,
+        },
         hre,
       );
       assert.equal(replServer.lastError, undefined);
@@ -37,7 +61,11 @@ describe("console/task-action", function () {
 
     it("should throw inside the console if the script throws", async function () {
       const replServer = await consoleAction(
-        { commands: ['await import("./scripts/throws.js");', ".exit"] },
+        {
+          commands: ['await import("./scripts/throws.js");', ".exit"],
+          history: "",
+          options,
+        },
         hre,
       );
       ensureError(replServer.lastError);
@@ -49,7 +77,11 @@ describe("console/task-action", function () {
 
     it("should throw inside the console if script does not exist", async function () {
       const replServer = await consoleAction(
-        { commands: ['await import("./scripts/non-existent.ts");', ".exit"] },
+        {
+          commands: ['await import("./scripts/non-existent.ts");', ".exit"],
+          history: "",
+          options,
+        },
         hre,
       );
       ensureError(replServer.lastError);
@@ -57,7 +89,11 @@ describe("console/task-action", function () {
 
     it("should run a script inside the console successfully", async function () {
       const replServer = await consoleAction(
-        { commands: ['await import("./scripts/success.ts");', ".exit"] },
+        {
+          commands: ['await import("./scripts/success.ts");', ".exit"],
+          history: "",
+          options,
+        },
         hre,
       );
       assert.equal(replServer.lastError, undefined);
@@ -65,10 +101,50 @@ describe("console/task-action", function () {
 
     it("should throw inside the console if the script throws", async function () {
       const replServer = await consoleAction(
-        { commands: ['await import("./scripts/throws.ts");', ".exit"] },
+        {
+          commands: ['await import("./scripts/throws.ts");', ".exit"],
+          history: "",
+          options,
+        },
         hre,
       );
       ensureError(replServer.lastError);
     });
   });
+
+  describe("history", function () {
+    let cacheDir: string;
+    let history: string;
+
+    beforeEach(function () {
+      cacheDir = fs.mkdtempSync(
+        path.resolve(os.tmpdir(), "console-action-test-"),
+      );
+      history = path.resolve(cacheDir, "console-history.txt");
+    });
+
+    afterEach(function () {
+      fs.rmSync(cacheDir, { recursive: true });
+    });
+
+    it("should create a history file", async function () {
+      assert.ok(
+        !fs.existsSync(history),
+        "History file exists before running the console",
+      );
+      const replServer = await consoleAction(
+        {
+          commands: [".help", ".exit"],
+          history,
+          options,
+        },
+        hre,
+      );
+      assert.equal(replServer.lastError, undefined);
+      assert.ok(
+        fs.existsSync(history),
+        "History file does not exist after running the console",
+      );
+    });
+  });
 });

From f73612f6d7b3b71ffb5aa9b47e22362389dac5d2 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Sat, 10 Aug 2024 11:55:08 +0200
Subject: [PATCH 03/12] feat: expose hre to the hardhat console's context

---
 .../builtin-plugins/console/task-action.ts         |  5 ++++-
 .../builtin-plugins/console/task-action.ts         | 14 ++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index 4928b8d800..d228b01b29 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -18,7 +18,7 @@ interface ConsoleActionArguments {
 
 const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
   { commands, history, options },
-  _hre,
+  hre,
 ) => {
   // Resolve the history path if it is not empty
   let historyPath: string | undefined;
@@ -38,6 +38,9 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
       resolve(replServer);
     });
 
+    // Add the Hardhat Runtime Environment to the REPL context
+    replServer.context.hre = hre;
+
     // Set up the REPL history file if the historyPath has been set
     if (historyPath !== undefined) {
       replServer.setupHistory(historyPath, (err: Error | null) => {
diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
index b6f523b395..5fffa4ac46 100644
--- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -112,6 +112,20 @@ describe("console/task-action", function () {
     });
   });
 
+  describe("context", function () {
+    it("should expose the Hardhat Runtime Environment", async function () {
+      const replServer = await consoleAction(
+        {
+          commands: ["console.log(hre);", ".exit"],
+          history: "",
+          options,
+        },
+        hre,
+      );
+      assert.equal(replServer.lastError, undefined);
+    });
+  });
+
   describe("history", function () {
     let cacheDir: string;
     let history: string;

From a0ecee1b76621d4e2aa86c5cc1bdadee788f1c14 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Sat, 10 Aug 2024 14:43:42 +0200
Subject: [PATCH 04/12] fix: await history setup

---
 .../builtin-plugins/console/task-action.ts    | 33 ++++++++++---------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index d228b01b29..00bb4f890f 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -20,16 +20,16 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
   { commands, history, options },
   hre,
 ) => {
-  // Resolve the history path if it is not empty
-  let historyPath: string | undefined;
-  if (history !== "") {
-    const globalCacheDir = await getCacheDir();
-    historyPath = path.isAbsolute(history)
-      ? history
-      : path.resolve(globalCacheDir, history);
-  }
-
-  return new Promise<REPLServer>((resolve) => {
+  return new Promise<REPLServer>(async (resolve) => {
+    // Resolve the history path if it is not empty
+    let historyPath: string | undefined;
+    if (history !== "") {
+      const globalCacheDir = await getCacheDir();
+      historyPath = path.isAbsolute(history)
+        ? history
+        : path.resolve(globalCacheDir, history);
+    }
+
     // Start a new REPL server with the default options
     const replServer = repl.start(options);
 
@@ -43,11 +43,14 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
 
     // Set up the REPL history file if the historyPath has been set
     if (historyPath !== undefined) {
-      replServer.setupHistory(historyPath, (err: Error | null) => {
-        // Fail silently if the history file cannot be set up
-        if (err !== null) {
-          log(`Failed to setup REPL history: ${err.message}`);
-        }
+      await new Promise<void>((resolveSetupHistory) => {
+        replServer.setupHistory(historyPath, (err: Error | null) => {
+          // Fail silently if the history file cannot be set up
+          if (err !== null) {
+            log(`Failed to setup REPL history: ${err.message}`);
+          }
+          resolveSetupHistory();
+        });
       });
     }
 

From 35336949e88ac1d55fc2c03a4d7d813accc90fee Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Sat, 10 Aug 2024 14:47:47 +0200
Subject: [PATCH 05/12] fix: force remove temporary cache directory

---
 .../test/internal/builtin-plugins/console/task-action.ts        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
index 5fffa4ac46..dc3a532bd6 100644
--- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -138,7 +138,7 @@ describe("console/task-action", function () {
     });
 
     afterEach(function () {
-      fs.rmSync(cacheDir, { recursive: true });
+      fs.rmSync(cacheDir, { recursive: true, force: true });
     });
 
     it("should create a history file", async function () {

From b02caf90e18c6dc9ccac2dc4a117877985480da0 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Sat, 10 Aug 2024 15:04:24 +0200
Subject: [PATCH 06/12] feat: add noCompile flag to the console task

---
 .../internal/builtin-plugins/console/index.ts |  4 +++
 .../builtin-plugins/console/task-action.ts    | 26 ++++++++++++-------
 .../builtin-plugins/console/task-action.ts    |  8 ++++++
 3 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
index 225f7441b0..4f3290915e 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/index.ts
@@ -12,6 +12,10 @@ const hardhatPlugin: HardhatPlugin = {
         description: "Path to a history file",
         defaultValue: "console-history.txt",
       })
+      .addFlag({
+        name: "noCompile",
+        description: "Don't compile before running this task",
+      })
       .addVariadicArgument({
         name: "commands",
         description: "Commands to run in the console",
diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index 00bb4f890f..3897c236e4 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -12,24 +12,30 @@ const log = debug("hardhat:core:tasks:console");
 interface ConsoleActionArguments {
   commands: string[];
   history: string;
+  noCompile: boolean;
   // We accept ReplOptions as an argument to allow tests overriding the IO streams
   options?: repl.ReplOptions;
 }
 
 const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
-  { commands, history, options },
+  { commands, history, noCompile, options },
   hre,
 ) => {
-  return new Promise<REPLServer>(async (resolve) => {
-    // Resolve the history path if it is not empty
-    let historyPath: string | undefined;
-    if (history !== "") {
-      const globalCacheDir = await getCacheDir();
-      historyPath = path.isAbsolute(history)
-        ? history
-        : path.resolve(globalCacheDir, history);
-    }
+  // Resolve the history path if it is not empty
+  let historyPath: string | undefined;
+  if (history !== "") {
+    const globalCacheDir = await getCacheDir();
+    historyPath = path.isAbsolute(history)
+      ? history
+      : path.resolve(globalCacheDir, history);
+  }
 
+  // If noCompile is false, run the compile task first
+  if (!noCompile) {
+    // todo: run compile task
+  }
+
+  return new Promise<REPLServer>(async (resolve) => {
     // Start a new REPL server with the default options
     const replServer = repl.start(options);
 
diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
index dc3a532bd6..c0f9f38f7f 100644
--- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -40,6 +40,7 @@ describe("console/task-action", function () {
         {
           commands: ['await import("./scripts/non-existent.js");', ".exit"],
           history: "",
+          noCompile: false,
           options,
         },
         hre,
@@ -52,6 +53,7 @@ describe("console/task-action", function () {
         {
           commands: [".help", 'await import("./scripts/success.js");', ".exit"],
           history: "",
+          noCompile: false,
           options,
         },
         hre,
@@ -64,6 +66,7 @@ describe("console/task-action", function () {
         {
           commands: ['await import("./scripts/throws.js");', ".exit"],
           history: "",
+          noCompile: false,
           options,
         },
         hre,
@@ -80,6 +83,7 @@ describe("console/task-action", function () {
         {
           commands: ['await import("./scripts/non-existent.ts");', ".exit"],
           history: "",
+          noCompile: false,
           options,
         },
         hre,
@@ -92,6 +96,7 @@ describe("console/task-action", function () {
         {
           commands: ['await import("./scripts/success.ts");', ".exit"],
           history: "",
+          noCompile: false,
           options,
         },
         hre,
@@ -104,6 +109,7 @@ describe("console/task-action", function () {
         {
           commands: ['await import("./scripts/throws.ts");', ".exit"],
           history: "",
+          noCompile: false,
           options,
         },
         hre,
@@ -118,6 +124,7 @@ describe("console/task-action", function () {
         {
           commands: ["console.log(hre);", ".exit"],
           history: "",
+          noCompile: false,
           options,
         },
         hre,
@@ -150,6 +157,7 @@ describe("console/task-action", function () {
         {
           commands: [".help", ".exit"],
           history,
+          noCompile: false,
           options,
         },
         hre,

From b0b1badeae1166ceb51241a28f67df28a796b942 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Sat, 10 Aug 2024 15:25:56 +0200
Subject: [PATCH 07/12] fix: us fs utils for file operations

---
 .../builtin-plugins/console/task-action.ts    |  2 +-
 .../builtin-plugins/console/task-action.ts    | 24 +++++++++++++------
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index 3897c236e4..89fae266e5 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -53,7 +53,7 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
         replServer.setupHistory(historyPath, (err: Error | null) => {
           // Fail silently if the history file cannot be set up
           if (err !== null) {
-            log(`Failed to setup REPL history: ${err.message}`);
+            log("Failed to setup REPL history", err);
           }
           resolveSetupHistory();
         });
diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
index c0f9f38f7f..aac04b91d0 100644
--- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -2,18 +2,22 @@ import type { HardhatRuntimeEnvironment } from "@ignored/hardhat-vnext-core/type
 import type repl from "node:repl";
 
 import assert from "node:assert/strict";
-import fs from "node:fs";
+import fsPromises from "node:fs/promises";
 import os from "node:os";
 import path from "node:path";
 import { PassThrough } from "node:stream";
 import { afterEach, before, beforeEach, describe, it } from "node:test";
 
 import { ensureError } from "@ignored/hardhat-vnext-utils/error";
+import { exists, remove } from "@ignored/hardhat-vnext-utils/fs";
+import debug from "debug";
 
 import { createHardhatRuntimeEnvironment } from "../../../../src/hre.js";
 import consoleAction from "../../../../src/internal/builtin-plugins/console/task-action.js";
 import { useFixtureProject } from "../../../helpers/project.js";
 
+const log = debug("hardhat:test:console:task-action");
+
 describe("console/task-action", function () {
   let hre: HardhatRuntimeEnvironment;
   let options: repl.ReplOptions;
@@ -137,20 +141,25 @@ describe("console/task-action", function () {
     let cacheDir: string;
     let history: string;
 
-    beforeEach(function () {
-      cacheDir = fs.mkdtempSync(
+    beforeEach(async function () {
+      cacheDir = await fsPromises.mkdtemp(
         path.resolve(os.tmpdir(), "console-action-test-"),
       );
       history = path.resolve(cacheDir, "console-history.txt");
     });
 
-    afterEach(function () {
-      fs.rmSync(cacheDir, { recursive: true, force: true });
+    afterEach(async function () {
+      try {
+        await remove(cacheDir);
+      } catch (error) {
+        log("Failed to remove temporary cache dir", error);
+      }
     });
 
     it("should create a history file", async function () {
+      let historyExists = await exists(history);
       assert.ok(
-        !fs.existsSync(history),
+        !historyExists,
         "History file exists before running the console",
       );
       const replServer = await consoleAction(
@@ -163,8 +172,9 @@ describe("console/task-action", function () {
         hre,
       );
       assert.equal(replServer.lastError, undefined);
+      historyExists = await exists(history);
       assert.ok(
-        fs.existsSync(history),
+        historyExists,
         "History file does not exist after running the console",
       );
     });

From 511a3bb518e656765f99968d38d29ee47577a126 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Mon, 12 Aug 2024 10:30:16 +0200
Subject: [PATCH 08/12] chore: add comments about testing choices

---
 .../test/internal/builtin-plugins/console/task-action.ts | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
index aac04b91d0..bcbc17cb94 100644
--- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -27,6 +27,10 @@ describe("console/task-action", function () {
   });
 
   beforeEach(function () {
+    // Using process.stdin for the input during tests is not reliable as it
+    // causes the test runner to hang indefinitely. We use a PassThrough stream
+    // instead. This, in turn, prevents us from using process.stdout for output.
+    // Hence, we use a PassThrough stream for output as well.
     const input = new PassThrough();
     const output = new PassThrough();
     output.pipe(process.stdout);
@@ -142,6 +146,8 @@ describe("console/task-action", function () {
     let history: string;
 
     beforeEach(async function () {
+      // We use a temporary cache dir to avoid conflicts with other tests
+      // and global user settings.
       cacheDir = await fsPromises.mkdtemp(
         path.resolve(os.tmpdir(), "console-action-test-"),
       );
@@ -149,6 +155,9 @@ describe("console/task-action", function () {
     });
 
     afterEach(async function () {
+      // We try to remove the temporary cache dir after each test, but we don't
+      // fail the test if it fails. For example, we have observed that in GHA
+      // on Windows, the temp dir cannot be removed due to permission issues.
       try {
         await remove(cacheDir);
       } catch (error) {

From c23b974279680c14ff0b26a4d2f0fe50d310c07c Mon Sep 17 00:00:00 2001
From: Piotr Galar <piotr.galar@gmail.com>
Date: Wed, 14 Aug 2024 10:31:14 +0200
Subject: [PATCH 09/12] feat: expose hre fields in the console context

---
 .../src/internal/builtin-plugins/console/task-action.ts      | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index 89fae266e5..45b3f05e23 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -46,6 +46,11 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
 
     // Add the Hardhat Runtime Environment to the REPL context
     replServer.context.hre = hre;
+    replServer.context.config = hre.config;
+    replServer.context.tasks = hre.tasks;
+    replServer.context.globalOptions = hre.globalOptions;
+    replServer.context.hooks = hre.hooks;
+    replServer.context.interruptions = hre.interruptions;
 
     // Set up the REPL history file if the historyPath has been set
     if (historyPath !== undefined) {

From 57a4811ebf4d738f1b35fc1dfb1e920485a3c38c Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Wed, 14 Aug 2024 10:47:10 +0200
Subject: [PATCH 10/12] chore: add a note to replace usage of global cache
 directory

---
 .../src/internal/builtin-plugins/console/task-action.ts      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index 45b3f05e23..ab407d808a 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -24,10 +24,11 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
   // Resolve the history path if it is not empty
   let historyPath: string | undefined;
   if (history !== "") {
-    const globalCacheDir = await getCacheDir();
+    // TODO(#5599): Replace with hre.config.paths.cache once it is available
+    const cacheDir = await getCacheDir();
     historyPath = path.isAbsolute(history)
       ? history
-      : path.resolve(globalCacheDir, history);
+      : path.resolve(cacheDir, history);
   }
 
   // If noCompile is false, run the compile task first

From eccb397b50046abc394fcd1d656796cf9e10fa41 Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Wed, 14 Aug 2024 10:52:58 +0200
Subject: [PATCH 11/12]  chore: add an issue number to the compile related
 todos

---
 .../hardhat/src/internal/builtin-plugins/console/task-action.ts | 2 +-
 v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
index ab407d808a..818b130ab5 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/console/task-action.ts
@@ -33,7 +33,7 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
 
   // If noCompile is false, run the compile task first
   if (!noCompile) {
-    // todo: run compile task
+    // TODO(#5600): run compile task
   }
 
   return new Promise<REPLServer>(async (resolve) => {
diff --git a/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts b/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts
index e5a7c7f741..134101af64 100644
--- a/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts
+++ b/v-next/hardhat/src/internal/builtin-plugins/run/task-action.ts
@@ -27,7 +27,7 @@ const runScriptWithHardhat: NewTaskActionFunction<RunActionArguments> = async (
   }
 
   if (!noCompile) {
-    // todo: run compile task
+    // TODO(#5600): run compile task
   }
 
   try {

From 62fa7d28fff2f3940856cd692d8122219585f5cc Mon Sep 17 00:00:00 2001
From: galargh <piotr.galar@gmail.com>
Date: Wed, 14 Aug 2024 10:59:19 +0200
Subject: [PATCH 12/12] chore: add a note to remove direct fs use when
 replacement is available

---
 .../hardhat/test/internal/builtin-plugins/console/task-action.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
index bcbc17cb94..9a4d88f5b1 100644
--- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
+++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts
@@ -146,6 +146,7 @@ describe("console/task-action", function () {
     let history: string;
 
     beforeEach(async function () {
+      // TODO(#5601): Use the mkdtemp from hardhat-utils once it's available
       // We use a temporary cache dir to avoid conflicts with other tests
       // and global user settings.
       cacheDir = await fsPromises.mkdtemp(