Apart from Gianlucas suggestion using AxesOrigin, you are working with `Graphics3D´ and here you can virtually do just anything, e.g. simply adding arrows along axis:
Graphics3D[{Table[{z /. colors, PointSize[0.03],
Point[pointsByZ[z]]}, {z, {-2, -1, 1, 2}}], Thick,
Arrow[{{-3, 0, 0}, {3, 0, 0}}], Arrow[{{0, -3, 0}, {0, 3, 0}}],
Arrow[{{0, 0, -3}, {0, 0, 3}}]}, Axes -> True,
AxesLabel -> {"X", "Y", "Z"}, Boxed -> False, TicksStyle -> Large,
Ticks -> {{-2, -1, 0, 1, 2}, {-2, -1, 0, 1, 2}, {-2, -1, 0, 1, 2}},
BoxRatios -> {1, 1, 1}, PlotRange -> {{-3, 3}, {-3, 3}, {-3, 3}},
PlotLabel -> Style["64 points colored by Z layer", Bold, 14],
ImageSize -> {600, 600},
Epilog -> {Inset[
Grid[{{Graphics[{Red, Disk[]}, ImageSize -> 10],
"z = -2"}, {Graphics[{Blue, Disk[]}, ImageSize -> 10],
"z = -1"}, {Graphics[{Green, Disk[]}, ImageSize -> 10],
"z = 1"}, {Graphics[{Orange, Disk[]}, ImageSize -> 10],
"z = 2"}}], {Right, Top}, {Right, Top}]},
AxesOrigin -> {0, 0, 0}]