Compact will not recover space, because it really just defragments the data inside the existing data files, making the data more dense, more compact. In fact, a compact occasionally needs extra space to do its work and so can end up using up more space rather than reclaiming it.
Because a repair rewrites the data files from scratch, it reclaims the "wasted" space and recovers disk space.
Besides bringing the secondaries down and running a repair there as you mention, another option is to delete the data files from a secondary and re-sync them from the primary from scratch. This has a similar effect to a repair, because in both cases the data files are re-written, the difference being that you don't need ~2x the disk space because you wipe out the originals and pull the data from the primary.
Once you have done the first secondary, generally the quickest way to do the second would be to snapshot (with journaling enabled) or fsync and lock the secondary and just to a file based copy to seed the other secondary.
Your final option assuming you have run the repair already on the primary, which is really just a variation, would be to snapshot the primary (or fsync and lock) and use its repaired files as a seed to populate a secondary.
These methods are covered in more detail as part of the Backup docs:
http://www.mongodb.org/display/DOCS/Backups#Backups-Methods