completion-file-name-table is called with three arguments. The first one is the string to be completed, and the second one is a predicate with which it is expected to filter possible matches.
We can put an advice around
completion-file-name-table that calls the function with the original arguments unchanged when the first argument starts with a dot (so we have auto-completion when we actually want to open a dot-file), but that calls
completion-file-name-table with a dot-file-excluding predicate when the first argument does not start with a dot.
(defun completion-f-n-table-ignoring-dot-prefix (fun str pred flag)
"Call `completion-file-name-table' with a predicate that
ignores matches starting with a dot, unless STR starts with a
(funcall fun str
(cond ((string-prefix-p "." (file-name-nondirectory str))
(not (string-prefix-p "." str))))
(and (not (string-prefix-p "." str))
(funcall pred str)))))
(advice-add 'completion-file-name-table :around
To restore the standard behaviour, remove the advice