diff --git a/crates/forge/bin/cmd/inspect.rs b/crates/forge/bin/cmd/inspect.rs
index d1836c9bccf9..42ba370b2edc 100644
--- a/crates/forge/bin/cmd/inspect.rs
+++ b/crates/forge/bin/cmd/inspect.rs
@@ -1,3 +1,4 @@
+use alloy_json_abi::{EventParam, InternalType, JsonAbi, Param};
 use alloy_primitives::{hex, keccak256, Address};
 use clap::Parser;
 use comfy_table::{modifiers::UTF8_ROUND_CORNERS, Cell, Table};
@@ -17,7 +18,8 @@ use foundry_compilers::{
     utils::canonicalize,
 };
 use regex::Regex;
-use std::{fmt, sync::LazyLock};
+use serde_json::{Map, Value};
+use std::{collections::BTreeMap, fmt, sync::LazyLock};
 
 /// CLI arguments for `forge inspect`.
 #[derive(Clone, Debug, Parser)]
@@ -29,10 +31,6 @@ pub struct InspectArgs {
     #[arg(value_enum)]
     pub field: ContractArtifactField,
 
-    /// Pretty print the selected field, if supported.
-    #[arg(long)]
-    pub pretty: bool,
-
     /// All build arguments are supported
     #[command(flatten)]
     build: BuildOpts,
@@ -40,7 +38,7 @@ pub struct InspectArgs {
 
 impl InspectArgs {
     pub fn run(self) -> Result<()> {
-        let Self { contract, field, build, pretty } = self;
+        let Self { contract, field, build } = self;
 
         trace!(target: "forge", ?field, ?contract, "running forge inspect");
 
@@ -85,12 +83,7 @@ impl InspectArgs {
                     .abi
                     .as_ref()
                     .ok_or_else(|| eyre::eyre!("Failed to fetch lossless ABI"))?;
-                if pretty {
-                    let source = foundry_cli::utils::abi_to_solidity(abi, &contract.name)?;
-                    sh_println!("{source}")?;
-                } else {
-                    print_json(abi)?;
-                }
+                print_abi(abi)?;
             }
             ContractArtifactField::Bytecode => {
                 print_json_str(&artifact.bytecode, Some("object"))?;
@@ -105,7 +98,7 @@ impl InspectArgs {
                 print_json_str(&artifact.legacy_assembly, None)?;
             }
             ContractArtifactField::MethodIdentifiers => {
-                print_json(&artifact.method_identifiers)?;
+                print_method_identifiers(&artifact.method_identifiers)?;
             }
             ContractArtifactField::GasEstimates => {
                 print_json(&artifact.gas_estimates)?;
@@ -117,10 +110,10 @@ impl InspectArgs {
                 print_json(&artifact.devdoc)?;
             }
             ContractArtifactField::Ir => {
-                print_yul(artifact.ir.as_deref(), self.pretty)?;
+                print_yul(artifact.ir.as_deref())?;
             }
             ContractArtifactField::IrOptimized => {
-                print_yul(artifact.ir_optimized.as_deref(), self.pretty)?;
+                print_yul(artifact.ir_optimized.as_deref())?;
             }
             ContractArtifactField::Metadata => {
                 print_json(&artifact.metadata)?;
@@ -132,37 +125,12 @@ impl InspectArgs {
                 print_json_str(&artifact.ewasm, None)?;
             }
             ContractArtifactField::Errors => {
-                let mut out = serde_json::Map::new();
-                if let Some(abi) = &artifact.abi {
-                    let abi = &abi;
-                    // Print the signature of all errors.
-                    for er in abi.errors.iter().flat_map(|(_, errors)| errors) {
-                        let types = er.inputs.iter().map(|p| p.ty.clone()).collect::<Vec<_>>();
-                        let sig = format!("{:x}", er.selector());
-                        let sig_trimmed = &sig[0..8];
-                        out.insert(
-                            format!("{}({})", er.name, types.join(",")),
-                            sig_trimmed.to_string().into(),
-                        );
-                    }
-                }
-                print_json(&out)?;
+                let out = artifact.abi.as_ref().map_or(Map::new(), parse_errors);
+                print_errors_events(&out, true)?;
             }
             ContractArtifactField::Events => {
-                let mut out = serde_json::Map::new();
-                if let Some(abi) = &artifact.abi {
-                    let abi = &abi;
-                    // Print the topic of all events including anonymous.
-                    for ev in abi.events.iter().flat_map(|(_, events)| events) {
-                        let types = ev.inputs.iter().map(|p| p.ty.clone()).collect::<Vec<_>>();
-                        let topic = hex::encode(keccak256(ev.signature()));
-                        out.insert(
-                            format!("{}({})", ev.name, types.join(",")),
-                            format!("0x{topic}").into(),
-                        );
-                    }
-                }
-                print_json(&out)?;
+                let out = artifact.abi.as_ref().map_or(Map::new(), parse_events);
+                print_errors_events(&out, false)?;
             }
             ContractArtifactField::Eof => {
                 print_eof(artifact.deployed_bytecode.and_then(|b| b.bytecode))?;
@@ -176,6 +144,127 @@ impl InspectArgs {
     }
 }
 
+fn parse_errors(abi: &JsonAbi) -> Map<String, Value> {
+    let mut out = serde_json::Map::new();
+    for er in abi.errors.iter().flat_map(|(_, errors)| errors) {
+        let types = get_ty_sig(&er.inputs);
+        let sig = format!("{:x}", er.selector());
+        let sig_trimmed = &sig[0..8];
+        out.insert(format!("{}({})", er.name, types), sig_trimmed.to_string().into());
+    }
+    out
+}
+
+fn parse_events(abi: &JsonAbi) -> Map<String, Value> {
+    let mut out = serde_json::Map::new();
+    for ev in abi.events.iter().flat_map(|(_, events)| events) {
+        let types = parse_event_params(&ev.inputs);
+        let topic = hex::encode(keccak256(ev.signature()));
+        out.insert(format!("{}({})", ev.name, types), format!("0x{topic}").into());
+    }
+    out
+}
+
+fn parse_event_params(ev_params: &[EventParam]) -> String {
+    ev_params
+        .iter()
+        .map(|p| {
+            if let Some(ty) = p.internal_type() {
+                return internal_ty(ty)
+            }
+            p.ty.clone()
+        })
+        .collect::<Vec<_>>()
+        .join(",")
+}
+
+fn print_abi(abi: &JsonAbi) -> Result<()> {
+    if shell::is_json() {
+        return print_json(abi)
+    }
+
+    let headers = vec![Cell::new("Type"), Cell::new("Signature"), Cell::new("Selector")];
+    print_table(headers, |table| {
+        // Print events
+        for ev in abi.events.iter().flat_map(|(_, events)| events) {
+            let types = parse_event_params(&ev.inputs);
+            let selector = ev.selector().to_string();
+            table.add_row(["event", &format!("{}({})", ev.name, types), &selector]);
+        }
+
+        // Print errors
+        for er in abi.errors.iter().flat_map(|(_, errors)| errors) {
+            let selector = er.selector().to_string();
+            table.add_row([
+                "error",
+                &format!("{}({})", er.name, get_ty_sig(&er.inputs)),
+                &selector,
+            ]);
+        }
+
+        // Print functions
+        for func in abi.functions.iter().flat_map(|(_, f)| f) {
+            let selector = func.selector().to_string();
+            let state_mut = func.state_mutability.as_json_str();
+            let func_sig = if !func.outputs.is_empty() {
+                format!(
+                    "{}({}) {state_mut} returns ({})",
+                    func.name,
+                    get_ty_sig(&func.inputs),
+                    get_ty_sig(&func.outputs)
+                )
+            } else {
+                format!("{}({}) {state_mut}", func.name, get_ty_sig(&func.inputs))
+            };
+            table.add_row(["function", &func_sig, &selector]);
+        }
+
+        if let Some(constructor) = abi.constructor() {
+            let state_mut = constructor.state_mutability.as_json_str();
+            table.add_row([
+                "constructor",
+                &format!("constructor({}) {state_mut}", get_ty_sig(&constructor.inputs)),
+                "",
+            ]);
+        }
+
+        if let Some(fallback) = &abi.fallback {
+            let state_mut = fallback.state_mutability.as_json_str();
+            table.add_row(["fallback", &format!("fallback() {state_mut}"), ""]);
+        }
+
+        if let Some(receive) = &abi.receive {
+            let state_mut = receive.state_mutability.as_json_str();
+            table.add_row(["receive", &format!("receive() {state_mut}"), ""]);
+        }
+    })
+}
+
+fn get_ty_sig(inputs: &[Param]) -> String {
+    inputs
+        .iter()
+        .map(|p| {
+            if let Some(ty) = p.internal_type() {
+                return internal_ty(ty);
+            }
+            p.ty.clone()
+        })
+        .collect::<Vec<_>>()
+        .join(",")
+}
+
+fn internal_ty(ty: &InternalType) -> String {
+    let contract_ty =
+        |c: &Option<String>, ty: &String| c.clone().map_or(ty.clone(), |c| format!("{c}.{ty}"));
+    match ty {
+        InternalType::AddressPayable(addr) => addr.clone(),
+        InternalType::Contract(contract) => contract.clone(),
+        InternalType::Enum { contract, ty } => contract_ty(contract, ty),
+        InternalType::Struct { contract, ty } => contract_ty(contract, ty),
+        InternalType::Other { contract, ty } => contract_ty(contract, ty),
+    }
+}
+
 pub fn print_storage_layout(storage_layout: Option<&StorageLayout>) -> Result<()> {
     let Some(storage_layout) = storage_layout else {
         eyre::bail!("Could not get storage layout");
@@ -185,30 +274,70 @@ pub fn print_storage_layout(storage_layout: Option<&StorageLayout>) -> Result<()
         return print_json(&storage_layout)
     }
 
-    let mut table = Table::new();
-    table.apply_modifier(UTF8_ROUND_CORNERS);
-
-    table.set_header(vec![
+    let headers = vec![
         Cell::new("Name"),
         Cell::new("Type"),
         Cell::new("Slot"),
         Cell::new("Offset"),
         Cell::new("Bytes"),
         Cell::new("Contract"),
-    ]);
-
-    for slot in &storage_layout.storage {
-        let storage_type = storage_layout.types.get(&slot.storage_type);
-        table.add_row([
-            slot.label.as_str(),
-            storage_type.map_or("?", |t| &t.label),
-            &slot.slot,
-            &slot.offset.to_string(),
-            storage_type.map_or("?", |t| &t.number_of_bytes),
-            &slot.contract,
-        ]);
+    ];
+
+    print_table(headers, |table| {
+        for slot in &storage_layout.storage {
+            let storage_type = storage_layout.types.get(&slot.storage_type);
+            table.add_row([
+                slot.label.as_str(),
+                storage_type.map_or("?", |t| &t.label),
+                &slot.slot,
+                &slot.offset.to_string(),
+                storage_type.map_or("?", |t| &t.number_of_bytes),
+                &slot.contract,
+            ]);
+        }
+    })
+}
+
+fn print_method_identifiers(method_identifiers: &Option<BTreeMap<String, String>>) -> Result<()> {
+    let Some(method_identifiers) = method_identifiers else {
+        eyre::bail!("Could not get method identifiers");
+    };
+
+    if shell::is_json() {
+        return print_json(method_identifiers)
+    }
+
+    let headers = vec![Cell::new("Method"), Cell::new("Identifier")];
+
+    print_table(headers, |table| {
+        for (method, identifier) in method_identifiers {
+            table.add_row([method, identifier]);
+        }
+    })
+}
+
+fn print_errors_events(map: &Map<String, Value>, is_err: bool) -> Result<()> {
+    if shell::is_json() {
+        return print_json(map);
     }
 
+    let headers = if is_err {
+        vec![Cell::new("Error"), Cell::new("Selector")]
+    } else {
+        vec![Cell::new("Event"), Cell::new("Topic")]
+    };
+    print_table(headers, |table| {
+        for (method, selector) in map {
+            table.add_row([method, selector.as_str().unwrap()]);
+        }
+    })
+}
+
+fn print_table(headers: Vec<Cell>, add_rows: impl FnOnce(&mut Table)) -> Result<()> {
+    let mut table = Table::new();
+    table.apply_modifier(UTF8_ROUND_CORNERS);
+    table.set_header(headers);
+    add_rows(&mut table);
     sh_println!("\n{table}\n")?;
     Ok(())
 }
@@ -407,7 +536,7 @@ fn print_json_str(obj: &impl serde::Serialize, key: Option<&str>) -> Result<()>
     Ok(())
 }
 
-fn print_yul(yul: Option<&str>, pretty: bool) -> Result<()> {
+fn print_yul(yul: Option<&str>) -> Result<()> {
     let Some(yul) = yul else {
         eyre::bail!("Could not get IR output");
     };
@@ -415,11 +544,7 @@ fn print_yul(yul: Option<&str>, pretty: bool) -> Result<()> {
     static YUL_COMMENTS: LazyLock<Regex> =
         LazyLock::new(|| Regex::new(r"(///.*\n\s*)|(\s*/\*\*.*\*/)").unwrap());
 
-    if pretty {
-        sh_println!("{}", YUL_COMMENTS.replace_all(yul, ""))?;
-    } else {
-        sh_println!("{yul}")?;
-    }
+    sh_println!("{}", YUL_COMMENTS.replace_all(yul, ""))?;
 
     Ok(())
 }
diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs
index f0774a36802a..35a7bd4411a7 100644
--- a/crates/forge/tests/cli/cmd.rs
+++ b/crates/forge/tests/cli/cmd.rs
@@ -3230,19 +3230,177 @@ Compiler run successful!
         .stdout_eq(str![[r#""{...}""#]].is_json());
 });
 
-// <https://github.com/foundry-rs/foundry/issues/6816>
 forgetest_init!(can_inspect_counter_pretty, |prj, cmd| {
-    cmd.args(["inspect", "src/Counter.sol:Counter", "abi", "--pretty"]).assert_success().stdout_eq(
-        str![[r#"
-interface Counter {
-    function increment() external;
-    function number() external view returns (uint256);
-    function setNumber(uint256 newNumber) external;
+    cmd.args(["inspect", "src/Counter.sol:Counter", "abi"]).assert_success().stdout_eq(str![[r#"
+
+╭----------+---------------------------------+------------╮
+| Type     | Signature                       | Selector   |
++=========================================================+
+| function | increment() nonpayable          | 0xd09de08a |
+|----------+---------------------------------+------------|
+| function | number() view returns (uint256) | 0x8381f58a |
+|----------+---------------------------------+------------|
+| function | setNumber(uint256) nonpayable   | 0x3fb5c1cb |
+╰----------+---------------------------------+------------╯
+
+
+"#]]);
+});
+
+const CUSTOM_COUNTER: &str = r#"
+    contract Counter {
+    uint256 public number;
+    uint64 public count;
+    struct MyStruct {
+        uint64 count;
+    }
+    struct ErrWithMsg {
+        string message;
+    }
+
+    event Incremented(uint256 newValue);
+    event Decremented(uint256 newValue);
+
+    error NumberIsZero();
+    error CustomErr(ErrWithMsg e);
+
+    constructor(uint256 _number) {
+        number = _number;
+    }
+
+    function setNumber(uint256 newNumber) public {
+        number = newNumber;
+    }
+
+    function increment() external {
+        number++;
+    }
+
+    function decrement() public payable {
+        if (number == 0) {
+            return;
+        }
+        number--;
+    }
+
+    function square() public {
+        number = number * number;
+    }
+
+    fallback() external payable {
+        ErrWithMsg memory err = ErrWithMsg("Fallback function is not allowed");
+        revert CustomErr(err);
+    }
+
+    receive() external payable {
+        count++;
+    }
+
+    function setStruct(MyStruct memory s, uint32 b) public {
+        count = s.count;
+    }
 }
+    "#;
+forgetest!(inspect_custom_counter_abi, |prj, cmd| {
+    prj.add_source("Counter.sol", CUSTOM_COUNTER).unwrap();
+
+    cmd.args(["inspect", "Counter", "abi"]).assert_success().stdout_eq(str![[r#"
+
+╭-------------+-----------------------------------------------+--------------------------------------------------------------------╮
+| Type        | Signature                                     | Selector                                                           |
++==================================================================================================================================+
+| event       | Decremented(uint256)                          | 0xc9118d86370931e39644ee137c931308fa3774f6c90ab057f0c3febf427ef94a |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| event       | Incremented(uint256)                          | 0x20d8a6f5a693f9d1d627a598e8820f7a55ee74c183aa8f1a30e8d4e8dd9a8d84 |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| error       | CustomErr(Counter.ErrWithMsg)                 | 0x0625625a                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| error       | NumberIsZero()                                | 0xde5d32ac                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| function    | count() view returns (uint64)                 | 0x06661abd                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| function    | decrement() payable                           | 0x2baeceb7                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| function    | increment() nonpayable                        | 0xd09de08a                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| function    | number() view returns (uint256)               | 0x8381f58a                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| function    | setNumber(uint256) nonpayable                 | 0x3fb5c1cb                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| function    | setStruct(Counter.MyStruct,uint32) nonpayable | 0x08ef7366                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| function    | square() nonpayable                           | 0xd742cb01                                                         |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| constructor | constructor(uint256) nonpayable               |                                                                    |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| fallback    | fallback() payable                            |                                                                    |
+|-------------+-----------------------------------------------+--------------------------------------------------------------------|
+| receive     | receive() payable                             |                                                                    |
+╰-------------+-----------------------------------------------+--------------------------------------------------------------------╯
 
 
-"#]],
-    );
+"#]]);
+});
+
+forgetest!(inspect_custom_counter_events, |prj, cmd| {
+    prj.add_source("Counter.sol", CUSTOM_COUNTER).unwrap();
+
+    cmd.args(["inspect", "Counter", "events"]).assert_success().stdout_eq(str![[r#"
+
+╭----------------------+--------------------------------------------------------------------╮
+| Event                | Topic                                                              |
++===========================================================================================+
+| Decremented(uint256) | 0xc9118d86370931e39644ee137c931308fa3774f6c90ab057f0c3febf427ef94a |
+|----------------------+--------------------------------------------------------------------|
+| Incremented(uint256) | 0x20d8a6f5a693f9d1d627a598e8820f7a55ee74c183aa8f1a30e8d4e8dd9a8d84 |
+╰----------------------+--------------------------------------------------------------------╯
+
+
+"#]]);
+});
+
+forgetest!(inspect_custom_counter_errors, |prj, cmd| {
+    prj.add_source("Counter.sol", CUSTOM_COUNTER).unwrap();
+
+    cmd.args(["inspect", "Counter", "errors"]).assert_success().stdout_eq(str![[r#"
+
+╭-------------------------------+----------╮
+| Error                         | Selector |
++==========================================+
+| CustomErr(Counter.ErrWithMsg) | 0625625a |
+|-------------------------------+----------|
+| NumberIsZero()                | de5d32ac |
+╰-------------------------------+----------╯
+
+
+"#]]);
+});
+
+forgetest!(inspect_custom_counter_method_identifiers, |prj, cmd| {
+    prj.add_source("Counter.sol", CUSTOM_COUNTER).unwrap();
+
+    cmd.args(["inspect", "Counter", "method-identifiers"]).assert_success().stdout_eq(str![[r#"
+
+╭----------------------------+------------╮
+| Method                     | Identifier |
++=========================================+
+| count()                    | 06661abd   |
+|----------------------------+------------|
+| decrement()                | 2baeceb7   |
+|----------------------------+------------|
+| increment()                | d09de08a   |
+|----------------------------+------------|
+| number()                   | 8381f58a   |
+|----------------------------+------------|
+| setNumber(uint256)         | 3fb5c1cb   |
+|----------------------------+------------|
+| setStruct((uint64),uint32) | 08ef7366   |
+|----------------------------+------------|
+| square()                   | d742cb01   |
+╰----------------------------+------------╯
+
+
+"#]]);
 });
 
 // checks that `clean` also works with the "out" value set in Config