Custom Cipher Implementation
You may replace the lines as

text = open("plaintext.txt").read()


with so called context manager approach (see PEP 343 -- The "with" Statement):

with open("plaintext.txt") as pt:
    text = pt.read()


which has the advantage of automatically closing the open file, even if an exception is raised on the way.



sets_of_chars = text.split(" ")


is somehow different from

sets_of_chars = text.split()


in two ways - if there are more consecutive spaces, and if it is applied to the empty string:

'abc  def'.split(' ')        # returns ['abc', '', 'def']
'abc  def'.split()           # returns ['abc', 'def']

''.split(' ')                # returns ['']
''.split()                   # returns []


Consider which one is more appropriate for your code.



chars = "(stephn)a*dowjzr.b\nm,uvklc ?qig!fyx-"


It is a constant, so by PEP 8 -- Style Guide for Python Code its name should be in uppercase letters:

CHARS = "(stephn)a*dowjzr.b\nm,uvklc ?qig!fyx-"




Both functions for converting from base 10

def base_10_to_base_6(n):
    s = ""
    while n:
        s = str(n % 6) + s
        n //= 6

    if len(s) == 1:
        s = "0" + s

    if not s:
        return "00"

    return s


def base_10_to_base_26(n):
    s = ""
    while n:
        s = string.ascii_lowercase[n % 26] + s
        n //= 26

    if len(s) == 1:
        s = "a" + s

    if not s:
        return "aa"

    return s


have very similar structure - by the DRY principle (Don't Repeat Yourself) why not to substitute them with one common function, e. g. with

def base_10_to_other(n, base):
    if base not in (6, 26):
       raise ValueError("Illegal base - allowed values are only 6 and 26")

    DIGIT_SYMBOLS = {6: "012345", 26: string.ascii_lowercase}
    zero_symbol = DIGIT_SYMBOLS[base][0]

    s = ""
    while n:
        s = DIGIT_SYMBOLS[base][n % 6] + s
        n //= base

    if len(s) == 1:
        s = zero_symbol + s

    if not s:
        s = 2 * zero_symbol

    return s


Moreover, they are the same in your both modules (encoder.py and decoder.py) - why don't use the 3rd module for all common functions and import from it?
(The same for the other pair of converting functions, of course.)