This needs a bit of refactoring ...
If there are N pipeline stages, we need N-1 separate pipe calls. The code only has one.
Each pipeline stage can have its own/private stderr diversion (e.g.):
cmd0 | cmd1 2>cmd1_stderr | cmd2 2>cmd2_stderr | cmd3
But, the code assumes all stderr will be the same.
And, it assumes that it has to open stderr [at all]. The child should only open stderr if we have something like above. Otherwise, each child should inherit the parent's stderr [and do nothing].
The parent process is doing things that only the child should do (e.g.): change stdin/stdout/stderr and execute the command. It should mostly just facilitate the pipe.
The parent is doing a wait in the creation/fork loop [for each child]. This causes the parent to block at each step.
The parent should only do the wait in a second loop and wait for all children at once.
Because your struct definition for cmd was not posted, I had to guess a bit as to intent. But, I think you should have an array of the structs, one for each command, rather than putting all arguments for all commands in a single struct.
I've created two versions of the code. One with annotations for the bugs. A second that is cleaned up and restructured. Both are untested but should give you some ideas.
Here's the annotated version:
// NOTE/BUG: each command in the pipeline can have its own/private
// stderr diversion
#if 0
struct cmd {
    int pipes;
    char *filev[3];
    char **argv[MAXCMD][MAXARG];
};
#else
struct cmd {
    char *stderr;
    char *argv[100];
};
#endif
// NOTE/BUG: we need separate filev [and argv] for each pipeline stage
// so we need an _array_ of structs
int cmdcnt;
struct cmd cmdlist[30];
void
pipeline(void)
{
    int pfd[2];
// NOTE/BUG: this only creates a single pipe -- we need N-1 pipes
#if 0
    if (pipe(pfd) < 0)
        exit(-1);
#endif
// NOTE/BUG: if child does _not_ have a private stderr it should just
// use the parent's stderr _unchanged_
    for (int i = 0; i < cmdcnt; i++) {
        pid_t pid;
// NOTE/BUG: here is the correct place to create the pipe
        pid = fork();
        int fd;
        if (pid < 0)
            exit(-1);
        char **argv = cmd->argv;
        char **filev = cmd->filev;
        // child process
        if (pid == 0) {
            close(pfd[0]);
            dup2(pfd[1], STDOUT_FILENO);
            close(pfd[1]);
// NOTE/BUG: this does _not_ connect the input of cmd[N] to cmd[N-1]
#if 0
            if (filev[0] != NULL && i == 0) {
                fd = open(filev[0], O_RDONLY, 0);
                dup2(fd, STDIN_FILENO);
                close(fd);
            }
#endif
            if (filev[2] != NULL) {
                fd = creat(filev[2], 0644);
                dup2(fd, STDERR_FILENO);
                close(fd);
            }
            if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
                levenshtein(cmd->argv[i][0], commands);
        }
        // parent process
        if (pid > 0) {
// NOTE/BUG: parent should _not_ wait in the middle of the creation
// loop
            if (cmd->bg > 0)
                wait(NULL);
// NOTE/BUG: _parent_ should _not_ change its stdin/stderr/stdout
            close(pfd[1]);
            dup2(pfd[0], STDIN_FILENO);
            close(pfd[0]);
            if (filev[1] != NULL && i == (cmd->pipes - 1)) {
                fd = creat(filev[1], 0644);
                dup2(fd, STDOUT_FILENO);
                close(fd);
            }
            if (filev[2] != NULL) {
                fd = creat(filev[2], 0644);
                dup2(fd, STDERR_FILENO);
                close(fd);
            }
// NOTE/BUG: _parent_ should _not_ execute the command
            if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
                levenshtein(cmd->argv[i][0], commands);
        }
    }
}
Here's the refactored version:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
struct cmd {
    char *stderr;
    char *argv[100];
};
char *filev[3];
#define CLOSEME(_fd) \
    do { \
        if (_fd < 0) \
            break; \
        close(_fd); \
        _fd = -1; \
    } while (0)
int run_in_background;
int cmdcnt;
struct cmd cmdlist[30];
int
pipeline(void)
{
    int pfd[2] = { -1, -1 };
    struct cmd *cmd;
    char *file;
    int oldfd;
    pid_t pid;
    pid_t lastpid = -1;
    // open the common stderr
    int errfd = -1;
    if (filev[2] != NULL)
        errfd = open(filev[2],O_APPEND | O_CREAT);
    // start all commands
    for (int i = 0; i < cmdcnt; i++) {
        int iam_first = (i == 0);
        int iam_last = (i == (cmdcnt - 1));
        cmd = &cmdlist[i];
        // get previous stage pipe descriptor
        oldfd = pfd[0];
        // create the pipe to the next stage
        if (! iam_last) {
            if (pipe(pfd) < 0)
                exit(1);
        }
        pid = fork();
        lastpid = pid;
        int fd;
        if (pid < 0)
            exit(-1);
        char **argv = cmd->argv;
        // parent process
        if (pid > 0) {
            CLOSEME(pfd[1]);
            continue;
        }
        // child process ...
        // open stdin for _first_ command
        fd = -1;
        if (iam_first) {
            file = filev[0];
            if (file != NULL) {
                fd = open(file, O_RDONLY, 0);
            }
        }
        // connect stdin to previous stage pipe
        else {
            fd = oldfd;
            oldfd = -1;
        }
        // connect stdin to correct source
        if (fd >= 0) {
            dup2(fd, STDIN_FILENO);
            close(fd);
        }
        CLOSEME(oldfd);
        // connect to stderr
        file = cmd->stderr;
        if (file != NULL) {
            fd = creat(file, 0644);
            dup2(fd, STDERR_FILENO);
            close(fd);
        }
        else {
            if (errfd >= 0)
                dup2(errfd, STDERR_FILENO);
        }
        CLOSEME(errfd);
        // connect stdout
        // NOTE: does _not_ handle ">> outf" [only does "> outf"]
        fd = -1;
        if (iam_last) {
            file = filev[1];
            if (file != NULL) {
                fd = open(file, O_WRONLY | O_CREAT, 0644);
                dup2(fd, STDOUT_FILENO);
                close(fd);
            }
        }
        // execute the command
        execvp(argv[0], argv);
        exit(9);
    }
    CLOSEME(errfd);
    int status;
    int last_status = 0;
    // parent waits for all pipeline stages to complete
    if (! run_in_background) {
        while (1) {
            pid = wait(&status);
            if (pid <= 0)
                break;
            if (pid == lastpid) {
                last_status = status;
                break;
            }
        }
    }
    return last_status;
}