There are a lot of different ways to do this. What I do is have a "master controller" that knows how many and what type of creeps it needs for a specific task (e.g. each source in my room has a harvester controller that knows how many harvesters/couriers it should have). Each controller keeps a list of IDs for the creeps it owns, and every tick it checks to see if the creep is still alive (`Game.getObjectById()` returns null if the creep is dead). If one or more creeps are dead, the controller asks for a new creep of the same type to be spawned.
If instead you would rather have creeps respawn themselves, you can do that by storing a creep's information in memory (mostly the body, since everything else should already be in memory). When a creep dies, the memory entry still exists. You can iterate through `Memory.creeps` to find any creep that no longer exists in `Game.creeps`. Once you've found one or more creeps that are dead, you can respawn them using the data in memory. Note that you need to do this before you spawn any new creeps in a tick, or it will try to "respawn" those newly-created creeps and likely error out.