Bunch of different things here...
I'll just try to address all the mentioned problems, in no particular order.
Regarding Imports
What import does
The import doesn't care about packages or singletons: it just imports members. For example, on Strings, you can call the method contains:
"foobar".contains("foo") == true
Here is how you can import the member contains of a string s:
val s = "hello world"
import s.contains
println(contains("hello")) // true
println(contains("world")) // true
println(contains("12345")) // false
More generally, whenever you have some object a in scope for which the chain of names a.b.c.d.e.f is valid, you can select a point at which you want to break this chain (at d, say), then import a.b.c.d, then simply use d wherever you previously would have written a.b.c.d.
In the following code snippet, both printlns are equivalent:
object a:
  object b:
    object c:
      object d:
        object e:
          def f: String = "hey!"
println(a.b.c.d.e.f)
import a.b.c.d 
println(d.e.f)
Imports are not limited to top-level
It doesn't matter where you use the import, it's not constrained to be on top-level of the file. You can do the same in a function (or anywhere else, really):
// same `object a` as above
def foobar(): Unit =
  println(a.b.c.d.e.f)
  import a.b.c.d 
  println(d.e.f)
foobar()
Works on all objects (not only singletons)
It's also not constrained to "static" imports or singleton objects or packages or anything like that:
case class Foo(bar: String)
def useFoo(foo: Foo): Unit =
  import foo.bar
  println(bar)
useFoo(Foo("hello"))
Here, foo is not a singleton, it's just some instance passed from the outside.
It works in exactly the same way for Givens
It also doesn't matter whether you pass a parameter or whether the compiler does it for you automatically: import works on all parameters in exactly the same way. For example, here the timesThree imports a member of an implicitly given adder:
trait Addition[A]:
  def addStuff(x: A, y: A): A
def timesThree[A](a: A)(using adder: Addition[A]): A =
  import adder.addStuff
  addStuff(a, addStuff(a, a))
given Addition[Int] with
  def addStuff(x: Int, y: Int) = x + y
given [A]: Addition[List[A]] with
  def addStuff(x: List[A], y: List[A]) = x ++ y
println(timesThree(42))           // 126
println(timesThree(List(0, 1)))   // 0 1 0 1 0 1
Again, it works with the singleton given Addition[Int], as well as with instances of Addition[List[A]], which are generated on-the-fly whenever needed.
Regarding Reference.Parser
That's just an ordinary member access. The only thing to know about it is that there are no weird constraints about what can be accessed where. Type members and value members work in the same way:
object Foo:
  type Bar[X] = List[X]
  val baz: Int = 42
val x: Foo.Bar[Int] = List(1, 2, 3) // accessing a type member
println(x)
println(Foo.baz)                    // accessing a value member
Regarding extensions
The extension [A](p: Parser[A]) ... doesn't "define" or "declare" the Parser. The Parser is just left generic. The extension only guarantees that if you have some Foobar[_]-type constructor, and an implementation of Parsers[Foobar], then any instance of Foobar[A] can be patched up with a bunch of useful methods.
It's just a bunch of generic methods, but with x.foo(y,z) syntax (as opposed to foo(x, y, z)), and with a slightly more convenient type inference behavior.
Putting it all together
Here is a little example that uses everything at once:
trait Tacos[T[_]]:
  def makeTaco[A](a: A): T[A]
  extension [A](t: T[A])
    def doubleWrap: T[T[A]] = makeTaco(t)
object CornTortillaTacos extends Tacos[CornTortillaTacos.CornTortilla]:
  case class CornTortilla[A](wrappedContent: A)
  def makeTaco[A](a: A): CornTortilla[A] = CornTortilla(a)
def intTaco[T[_]](tacos: Tacos[T]) =
  import tacos.*
  makeTaco(42).doubleWrap
println(intTaco(CornTortillaTacos))
Here is what happens:
- It defines an interface of a taco factory: Tacos. The only thing that a taco factory can do is to take any objectsa: Aand make a taco with the fillinga.
- Because it's so convenient, and because we don't want to reimplement it in every taco factory, we provide an extension method that can wrap a taco twice, by putting a taco into a taco.
- We implement a concrete CornTortillaTacosfactory, which knows how to wrap anything intoCornTortillas. TheCornTortillaTacos.CornTortillais just a type constructor of kind* -> *, so it's suitable as argument forTacos[T[_]], and we provide the right factory methodmakeTacoin order to implementTacos[CornTortillaTacos.CornTortilla]. Nothing fancy here.
- We define a generic intTacomethod, which takes any taco factory, and produces a double-wrapped answer to all questions.
- We invoke intTacowithCornTortillaTacos, and obtain the double-wrappedCornTortilla(CornTortilla(42)).
I'm afraid that reading Bjarnason/Chiusano while still struggling with basic language features might prove rather difficult.