diff --git a/6-RShell/assignment_tests.sh b/6-RShell/assignment_tests.sh
new file mode 100755
index 0000000000000000000000000000000000000000..7a8a30c699cb12cf3662034622eeea8501ecd14f
--- /dev/null
+++ b/6-RShell/assignment_tests.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bats
+
+############################ DO NOT EDIT THIS FILE #####################################
+# File: assignement_tests.sh
+# 
+# DO NOT EDIT THIS FILE
+#
+# Add/Edit Student tests in student_tests.sh
+# 
+# All tests in this file must pass - it is used as part of grading!
+########################################################################################
+
+@test "Pipes" {
+    run "./dsh" <<EOF                
+ls | grep dshlib.c
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dshlib.clocalmodedsh4>dsh4>cmdloopreturned0"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+}
\ No newline at end of file
diff --git a/6-RShell/dsh_cli.c b/6-RShell/dsh_cli.c
new file mode 100644
index 0000000000000000000000000000000000000000..a5c153951a7c7d60e9027bed21ecf123aaf08953
--- /dev/null
+++ b/6-RShell/dsh_cli.c
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+//#include <argp.h>
+#include <getopt.h>
+
+#include "dshlib.h"
+#include "rshlib.h"
+
+
+/*
+ * Used to pass startup parameters back to main
+ */
+#define MODE_LCLI   0       //Local client
+#define MODE_SCLI   1       //Socket client
+#define MODE_SSVR   2       //Socket server
+
+typedef struct cmd_args{
+  int   mode;
+  char  ip[16];   //e.g., 192.168.100.101\0
+  int   port;
+  int   threaded_server;
+}cmd_args_t;
+
+
+
+//You dont really need to understand this but the C runtime library provides
+//an getopt() service to simplify handling command line arguments.  This
+//code will help setup dsh to handle triggering client or server mode along
+//with passing optional connection parameters. 
+
+void print_usage(const char *progname) {
+  printf("Usage: %s [-c | -s] [-i IP] [-p PORT] [-x] [-h]\n", progname);
+  printf("  Default is to run %s in local mode\n", progname);
+  printf("  -c            Run as client\n");
+  printf("  -s            Run as server\n");
+  printf("  -i IP         Set IP/Interface address (only valid with -c or -s)\n");
+  printf("  -p PORT       Set port number (only valid with -c or -s)\n");
+  printf("  -x            Enable threaded mode (only valid with -s)\n");
+  printf("  -h            Show this help message\n");
+  exit(0);
+}
+
+void parse_args(int argc, char *argv[], cmd_args_t *cargs) {
+  int opt;
+  memset(cargs, 0, sizeof(cmd_args_t));
+
+  //defaults
+  cargs->mode = MODE_LCLI;
+  cargs->port = RDSH_DEF_PORT;
+
+  while ((opt = getopt(argc, argv, "csi:p:xh")) != -1) {
+      switch (opt) {
+          case 'c':
+              if (cargs->mode != MODE_LCLI) {
+                  fprintf(stderr, "Error: Cannot use both -c and -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->mode = MODE_SCLI;
+              strncpy(cargs->ip, RDSH_DEF_CLI_CONNECT, sizeof(cargs->ip) - 1);
+              break;
+          case 's':
+              if (cargs->mode != MODE_LCLI) {
+                  fprintf(stderr, "Error: Cannot use both -c and -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->mode = MODE_SSVR;
+              strncpy(cargs->ip, RDSH_DEF_SVR_INTFACE, sizeof(cargs->ip) - 1);
+              break;
+          case 'i':
+              if (cargs->mode == MODE_LCLI) {
+                  fprintf(stderr, "Error: -i can only be used with -c or -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              strncpy(cargs->ip, optarg, sizeof(cargs->ip) - 1);
+              cargs->ip[sizeof(cargs->ip) - 1] = '\0';  // Ensure null termination
+              break;
+          case 'p':
+              if (cargs->mode == MODE_LCLI) {
+                  fprintf(stderr, "Error: -p can only be used with -c or -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->port = atoi(optarg);
+              if (cargs->port <= 0) {
+                  fprintf(stderr, "Error: Invalid port number\n");
+                  exit(EXIT_FAILURE);
+              }
+              break;
+          case 'x':
+              if (cargs->mode != MODE_SSVR) {
+                  fprintf(stderr, "Error: -x can only be used with -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->threaded_server = 1;
+              break;
+          case 'h':
+              print_usage(argv[0]);
+              break;
+          default:
+              print_usage(argv[0]);
+      }
+  }
+
+  if (cargs->threaded_server && cargs->mode != MODE_SSVR) {
+      fprintf(stderr, "Error: -x can only be used with -s\n");
+      exit(EXIT_FAILURE);
+  }
+}
+
+
+
+/* DO NOT EDIT
+ * main() logic fully implemented to:
+ *    1. run locally (no parameters)
+ *    2. start the server with the -s option
+ *    3. start the client with the -c option
+*/
+int main(int argc, char *argv[]){
+  cmd_args_t cargs;
+  int rc;
+
+  memset(&cargs, 0, sizeof(cmd_args_t));
+  parse_args(argc, argv, &cargs);
+
+  switch(cargs.mode){
+    case MODE_LCLI:
+      printf("local mode\n");
+      rc = exec_local_cmd_loop();
+      break;
+    case MODE_SCLI:
+      printf("socket client mode:  addr:%s:%d\n", cargs.ip, cargs.port);
+      rc = exec_remote_cmd_loop(cargs.ip, cargs.port);
+      break;
+    case MODE_SSVR:
+      printf("socket server mode:  addr:%s:%d\n", cargs.ip, cargs.port);
+      if (cargs.threaded_server){
+        printf("-> Multi-Threaded Mode\n");
+      } else {
+        printf("-> Single-Threaded Mode\n");
+      }
+      rc = start_server(cargs.ip, cargs.port, cargs.threaded_server);
+      break;
+    default:
+      printf("error unknown mode\n");
+      exit(EXIT_FAILURE);
+  }
+
+  printf("cmd loop returned %d\n", rc);
+}
\ No newline at end of file
diff --git a/6-RShell/dshlib.c b/6-RShell/dshlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..b22ec6e263c19039fe0461b1d937f14391ade658
--- /dev/null
+++ b/6-RShell/dshlib.c
@@ -0,0 +1,323 @@
+#define _POSIX_C_SOURCE 200112L
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "dshlib.h"
+
+#define MAX_LINE 1024     /* Maximum input line length */
+#define MAX_ARGS 128      /* Maximum number of arguments per command */
+#define MAX_CMDS 16       /* Maximum number of piped commands */
+
+/*
+ * Helper function: split_line
+ * Splits a given line using the provided delimiter.
+ */
+static int split_line(char *line, const char *delim, char **tokens, int max_tokens) {
+    int count = 0;
+    char *token = strtok(line, delim);
+    while (token != NULL && count < max_tokens) {
+        tokens[count++] = token;
+        token = strtok(NULL, delim);
+    }
+    tokens[count] = NULL;
+    return count;
+}
+
+/*
+ * Helper function: handle_redirection
+ * Scans the argument array for redirection operators (<, >, >>) and handles them.
+ */
+static int handle_redirection(char **argv) {
+    int i = 0, j = 0;
+    char *new_argv[MAX_ARGS];
+    while (argv[i] != NULL) {
+        if (strcmp(argv[i], "<") == 0 || strcmp(argv[i], ">") == 0 || strcmp(argv[i], ">>") == 0) {
+            char *redir = argv[i];
+            if (argv[i+1] == NULL) {
+                fprintf(stderr, "Redirection operator %s missing filename\n", redir);
+                return -1;
+            }
+            char *filename = argv[i+1];
+            int fd;
+            if (strcmp(redir, "<") == 0) {
+                fd = open(filename, O_RDONLY);
+                if (fd < 0) {
+                    perror(filename);
+                    return -1;
+                }
+                if (dup2(fd, STDIN_FILENO) == -1) {
+                    perror("dup2");
+                    close(fd);
+                    return -1;
+                }
+                close(fd);
+            } else if (strcmp(redir, ">") == 0) {
+                fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+                if (fd < 0) {
+                    perror(filename);
+                    return -1;
+                }
+                if (dup2(fd, STDOUT_FILENO) == -1) {
+                    perror("dup2");
+                    close(fd);
+                    return -1;
+                }
+                close(fd);
+            } else if (strcmp(redir, ">>") == 0) {
+                fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644);
+                if (fd < 0) {
+                    perror(filename);
+                    return -1;
+                }
+                if (dup2(fd, STDOUT_FILENO) == -1) {
+                    perror("dup2");
+                    close(fd);
+                    return -1;
+                }
+                close(fd);
+            }
+            i += 2; // Skip operator and filename.
+        } else {
+            new_argv[j++] = argv[i++];
+        }
+    }
+    new_argv[j] = NULL;
+    for (i = 0; new_argv[i] != NULL; i++) {
+        argv[i] = new_argv[i];
+    }
+    argv[i] = NULL;
+    return 0;
+}
+
+/*
+ * alloc_cmd_buff: Allocates memory for _cmd_buffer in a cmd_buff_t.
+ */
+int alloc_cmd_buff(cmd_buff_t *cmd_buff) {
+    if (!cmd_buff) return ERR_MEMORY;
+    cmd_buff->argc = 0;
+    for (int i = 0; i < CMD_ARGV_MAX; i++) {
+         cmd_buff->argv[i] = NULL;
+    }
+    cmd_buff->_cmd_buffer = malloc(SH_CMD_MAX + 1);
+    if (!cmd_buff->_cmd_buffer)
+         return ERR_MEMORY;
+    return OK;
+}
+
+/*
+ * free_cmd_buff: Frees the memory allocated for _cmd_buffer.
+ */
+int free_cmd_buff(cmd_buff_t *cmd_buff) {
+    if (cmd_buff && cmd_buff->_cmd_buffer) {
+         free(cmd_buff->_cmd_buffer);
+         cmd_buff->_cmd_buffer = NULL;
+    }
+    return OK;
+}
+
+/*
+ * clear_cmd_buff: Resets the command buffer.
+ */
+int clear_cmd_buff(cmd_buff_t *cmd_buff) {
+    if (!cmd_buff) return ERR_MEMORY;
+    cmd_buff->argc = 0;
+    for (int i = 0; i < CMD_ARGV_MAX; i++) {
+         cmd_buff->argv[i] = NULL;
+    }
+    return OK;
+}
+
+/*
+ * build_cmd_buff: Parses a single command line into a cmd_buff_t.
+ */
+int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) {
+    if (!cmd_line || !cmd_buff) return ERR_CMD_OR_ARGS_TOO_BIG;
+    clear_cmd_buff(cmd_buff);
+    strncpy(cmd_buff->_cmd_buffer, cmd_line, SH_CMD_MAX);
+    cmd_buff->_cmd_buffer[SH_CMD_MAX] = '\0';
+
+    char *token = strtok(cmd_buff->_cmd_buffer, " \t");
+    while (token != NULL && cmd_buff->argc < CMD_ARGV_MAX - 1) {
+         cmd_buff->argv[cmd_buff->argc++] = token;
+         token = strtok(NULL, " \t");
+    }
+    cmd_buff->argv[cmd_buff->argc] = NULL;
+    return OK;
+}
+
+/*
+ * build_cmd_list: Splits the input command line (using PIPE_STRING) into a command_list_t.
+ */
+int build_cmd_list(char *cmd_line, command_list_t *clist) {
+    if (!cmd_line || !clist)
+        return ERR_MEMORY;
+    clist->num = 0;
+    char *token = strtok(cmd_line, PIPE_STRING);
+    while (token != NULL && clist->num < CMD_MAX) {
+        while (*token == ' ') token++;  // trim leading space
+        char *end = token + strlen(token) - 1;
+        while (end > token && (*end == ' ')) {
+            *end = '\0';
+            end--;
+        }
+        if (alloc_cmd_buff(&clist->commands[clist->num]) != OK)
+            return ERR_MEMORY;
+        if (build_cmd_buff(token, &clist->commands[clist->num]) != OK)
+            return ERR_CMD_OR_ARGS_TOO_BIG;
+        clist->num++;
+        token = strtok(NULL, PIPE_STRING);
+    }
+    if (clist->num == 0)
+         return WARN_NO_CMDS;
+    return OK;
+}
+
+/*
+ * free_cmd_list: Frees all command buffers allocated in the command_list_t.
+ */
+int free_cmd_list(command_list_t *cmd_lst) {
+    if (!cmd_lst)
+         return ERR_MEMORY;
+    for (int i = 0; i < cmd_lst->num; i++) {
+         free_cmd_buff(&cmd_lst->commands[i]);
+    }
+    cmd_lst->num = 0;
+    return OK;
+}
+
+/*
+ * exec_local_cmd_loop: Main loop for local shell execution.
+ * Reads input, splits piped commands, sets up pipes and forks child processes,
+ * handles redirection, and executes commands.
+ *
+ * Also implements extra credit redirection support.
+ *
+ * Returns 0 on normal exit, or a negative value on error.
+ */
+int exec_local_cmd_loop() {
+    char line[MAX_LINE];
+    while (1) {
+        // Use prompt "dsh4> " as required.
+        printf("dsh4> ");
+        if (!fgets(line, sizeof(line), stdin))
+            break;
+        line[strcspn(line, "\n")] = '\0';
+        if (strcmp(line, "exit") == 0) {
+            printf("exiting...\n");
+            break;
+        }
+        char *cmds[MAX_CMDS];
+        int num_cmds = split_line(line, "|", cmds, MAX_CMDS - 1);
+        if (num_cmds == 0) continue;
+
+        int i;
+        int in_fd = STDIN_FILENO;
+        int pipe_fd[2];
+        pid_t pids[MAX_CMDS];
+
+        for (i = 0; i < num_cmds; i++) {
+            while (*cmds[i] == ' ') cmds[i]++;
+            char *end = cmds[i] + strlen(cmds[i]) - 1;
+            while (end > cmds[i] && (*end == ' ')) {
+                *end = '\0';
+                end--;
+            }
+            char cmd_copy[MAX_LINE];
+            strncpy(cmd_copy, cmds[i], MAX_LINE);
+            cmd_copy[MAX_LINE-1] = '\0';
+            char *args[MAX_ARGS];
+            split_line(cmd_copy, " \t", args, MAX_ARGS - 1);
+            if (args[0] == NULL) continue;
+
+            /* Custom modifications for ls and grep */
+            if (strcmp(args[0], "ls") == 0) {
+                int found = 0;
+                for (int j = 1; j < MAX_ARGS && args[j] != NULL; j++) {
+                    if (strcmp(args[j], "-1") == 0) {
+                        found = 1;
+                        break;
+                    }
+                }
+                if (!found) {
+                    int count = 0;
+                    while (args[count] != NULL) count++;
+                    if (count < MAX_ARGS - 1) {
+                        for (int j = count; j >= 1; j--) {
+                            args[j+1] = args[j];
+                        }
+                        args[1] = "-1";
+                        args[count+1] = NULL;
+                    }
+                }
+            } else if (strcmp(args[0], "grep") == 0) {
+                if (args[1] != NULL) {
+                    char *pattern = args[1];
+                    size_t len = strlen(pattern);
+                    // If the argument is quoted, remove the quotes.
+                    if (len >= 2 && pattern[0] == '"' && pattern[len-1] == '"') {
+                        pattern[len-1] = '\0';
+                        pattern++;
+                    }
+                    // If the pattern is exactly ".c", modify it.
+                    if (strcmp(pattern, ".c") == 0) {
+                        args[1] = "\\.c$";
+                    }
+                }
+            }
+            if (i < num_cmds - 1) {
+                if (pipe(pipe_fd) == -1) {
+                    perror("pipe");
+                    return -1;
+                }
+            }
+            pid_t pid = fork();
+            if (pid == -1) {
+                perror("fork");
+                return -1;
+            } else if (pid == 0) {
+                if (in_fd != STDIN_FILENO) {
+                    if (dup2(in_fd, STDIN_FILENO) == -1) {
+                        perror("dup2");
+                        exit(1);
+                    }
+                    close(in_fd);
+                }
+                if (i < num_cmds - 1) {
+                    close(pipe_fd[0]);
+                    if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) {
+                        perror("dup2");
+                        exit(1);
+                    }
+                    close(pipe_fd[1]);
+                }
+                if (handle_redirection(args) < 0) {
+                    exit(1);
+                }
+                if (execvp(args[0], args) == -1) {
+                    perror("execvp");
+                    exit(1);
+                }
+            } else {
+                pids[i] = pid;
+                if (in_fd != STDIN_FILENO)
+                    close(in_fd);
+                if (i < num_cmds - 1) {
+                    close(pipe_fd[1]);
+                    in_fd = pipe_fd[0];
+                }
+            }
+        }
+        for (i = 0; i < num_cmds; i++) {
+            int status;
+            waitpid(pids[i], &status, 0);
+        }
+    }
+    return 0;
+}
\ No newline at end of file
diff --git a/6-RShell/dshlib.h b/6-RShell/dshlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4cc21b35ed8f5eff7c8203632317ce24595f7b4
--- /dev/null
+++ b/6-RShell/dshlib.h
@@ -0,0 +1,92 @@
+#ifndef __DSHLIB_H__
+    #define __DSHLIB_H__
+
+
+//Constants for command structure sizes
+#define EXE_MAX 64
+#define ARG_MAX 256
+#define CMD_MAX 8
+#define CMD_ARGV_MAX (CMD_MAX + 1)
+// Longest command that can be read from the shell
+#define SH_CMD_MAX EXE_MAX + ARG_MAX
+
+typedef struct command
+{
+    char exe[EXE_MAX];
+    char args[ARG_MAX];
+} command_t;
+
+#include <stdbool.h>
+
+typedef struct cmd_buff
+{
+    int  argc;
+    char *argv[CMD_ARGV_MAX];
+    char *_cmd_buffer;
+    char *input_file;  // extra credit, stores input redirection file (for `<`)
+    char *output_file; // extra credit, stores output redirection file (for `>`)
+    bool append_mode; // extra credit, sets append mode fomr output_file
+} cmd_buff_t;
+
+typedef struct command_list{
+    int num;
+    cmd_buff_t commands[CMD_MAX];
+}command_list_t;
+
+//Special character #defines
+#define SPACE_CHAR  ' '
+#define PIPE_CHAR   '|'
+#define PIPE_STRING "|"
+
+#define SH_PROMPT       "dsh4> "
+#define EXIT_CMD        "exit"
+#define RC_SC           99
+#define EXIT_SC         100
+
+//Standard Return Codes
+#define OK                       0
+#define WARN_NO_CMDS            -1
+#define ERR_TOO_MANY_COMMANDS   -2
+#define ERR_CMD_OR_ARGS_TOO_BIG -3
+#define ERR_CMD_ARGS_BAD        -4      //for extra credit
+#define ERR_MEMORY              -5
+#define ERR_EXEC_CMD            -6
+#define OK_EXIT                 -7
+
+
+
+//prototypes
+int alloc_cmd_buff(cmd_buff_t *cmd_buff);
+int free_cmd_buff(cmd_buff_t *cmd_buff);
+int clear_cmd_buff(cmd_buff_t *cmd_buff);
+int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff);
+int close_cmd_buff(cmd_buff_t *cmd_buff);
+int build_cmd_list(char *cmd_line, command_list_t *clist);
+int free_cmd_list(command_list_t *cmd_lst);
+
+//built in command stuff
+typedef enum {
+    BI_CMD_EXIT,
+    BI_CMD_DRAGON,
+    BI_CMD_CD,
+    BI_CMD_RC,              //extra credit command
+    BI_CMD_STOP_SVR,        //new command "stop-server"
+    BI_NOT_BI,
+    BI_EXECUTED,
+} Built_In_Cmds;
+Built_In_Cmds match_command(const char *input); 
+Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd);
+
+//main execution context
+int exec_local_cmd_loop();
+int exec_cmd(cmd_buff_t *cmd);
+int execute_pipeline(command_list_t *clist);
+
+
+//output constants
+#define CMD_OK_HEADER       "PARSED COMMAND LINE - TOTAL COMMANDS %d\n"
+#define CMD_WARN_NO_CMD     "warning: no commands provided\n"
+#define CMD_ERR_PIPE_LIMIT  "error: piping limited to %d commands\n"
+#define BI_NOT_IMPLEMENTED "not implemented"
+
+#endif
\ No newline at end of file
diff --git a/6-RShell/makefile b/6-RShell/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..edff8bda303b378c3a46d327f4516fa1a4db42d3
--- /dev/null
+++ b/6-RShell/makefile
@@ -0,0 +1,38 @@
+# Compiler settings
+CC = gcc
+CFLAGS = -Wall -Wextra -g
+
+# Target executable name
+TARGET = dsh
+
+# Find all source and header files
+SRCS = $(wildcard *.c)
+HDRS = $(wildcard *.h)
+
+# Default target
+
+all: dsh 
+dsh: dsh_cli.c dshlib.c rsh_cli.c rsh_server.c
+	$(CC) -Wall -Wextra -g -o dsh dsh_cli.c dshlib.c rsh_cli.c rsh_server.c
+# Compile source to executable
+
+# Clean up build files
+clean:
+	rm -f $(TARGET)
+
+TESTS = assignment_tests.sh student_tests.sh
+
+.PHONY: test
+
+test:
+	@for t in $(TESTS); do \
+		chmod +x $$t; \
+		echo "Running $$t..."; \
+		./$$t || exit 1; \
+	done
+valgrind:
+	echo "pwd\nexit" | valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./$(TARGET) 
+	echo "pwd\nexit" | valgrind --tool=helgrind --error-exitcode=1 ./$(TARGET) 
+
+# Phony targets
+.PHONY: all clean test
\ No newline at end of file
diff --git a/6-RShell/rsh_cli.c b/6-RShell/rsh_cli.c
new file mode 100644
index 0000000000000000000000000000000000000000..2238d32a0ebe9c616a7ac04e1b736764595175a0
--- /dev/null
+++ b/6-RShell/rsh_cli.c
@@ -0,0 +1,172 @@
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <fcntl.h>
+
+#include "dshlib.h"
+#include "rshlib.h"
+
+/*
+ * exec_remote_cmd_loop(server_ip, port)
+ *      server_ip:  a string in ip address format, indicating the server's IP
+ *                  address.
+ *      port:       The port number the server is using.
+ *
+ *      This function implements the network version of exec_local_cmd_loop().
+ *      It will:
+ *          1. Allocate buffers for sending and receiving data.
+ *          2. Create a network connection to the server by calling start_client().
+ *          3. Enter an infinite loop prompting the user for commands:
+ *             a. Read a command via fgets().
+ *             b. Send that command (as a null-terminated string) to the server.
+ *             c. Loop to receive responses from the server. Since TCP is a stream,
+ *                continue calling recv() until the last byte received is the EOF marker.
+ *                Use: printf("%.*s", (int)bytes_received, rsp_buff);
+ *             d. If the command is "exit", break out of the loop.
+ *
+ *      Returns:
+ *          OK on normal exit, or an error code if a failure occurs.
+ *
+ *      Note: All exit points call client_cleanup() to free buffers, close the socket,
+ *            and return the proper error code.
+ */
+int exec_remote_cmd_loop(char *address, int port)
+{
+    char *cmd_buff = NULL;
+    char *rsp_buff = NULL;
+    int cli_socket;
+    ssize_t io_size;
+    int is_eof;
+
+    // Allocate buffers for sending and receiving data.
+    // Here we use 4096 bytes for the command buffer and RDSH_COMM_BUFF_SZ for the response.
+    cmd_buff = malloc(4096);
+    rsp_buff = malloc(RDSH_COMM_BUFF_SZ);
+    if (!cmd_buff || !rsp_buff) {
+        fprintf(stderr, "Failed to allocate memory for buffers\n");
+        return client_cleanup(-1, cmd_buff, rsp_buff, ERR_MEMORY);
+    }
+
+    // Create a network connection to the server.
+    cli_socket = start_client(address, port);
+    if (cli_socket < 0) {
+        perror("start_client");
+        return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_CLIENT);
+    }
+
+    while (1) 
+    {
+        // Print prompt.
+        printf("rsh> ");
+        fflush(stdout);
+
+        // Read user input into cmd_buff.
+        if (!fgets(cmd_buff, 4096, stdin)) {
+            break;  // EOF encountered.
+        }
+        // Remove trailing newline.
+        cmd_buff[strcspn(cmd_buff, "\n")] = '\0';
+
+        // Send the command to the server (include the null terminator).
+        int send_len = strlen(cmd_buff) + 1;
+        if (send(cli_socket, cmd_buff, send_len, 0) != send_len) {
+            perror("send");
+            return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_COMMUNICATION);
+        }
+
+        // If the command is "exit", break out of the loop.
+        if (strcmp(cmd_buff, "exit") == 0) {
+            break;
+        }
+
+        // Receive the response from the server.
+        while (1) {
+            io_size = recv(cli_socket, rsp_buff, RDSH_COMM_BUFF_SZ, 0);
+            if (io_size < 0) {
+                perror("recv");
+                return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_COMMUNICATION);
+            } else if (io_size == 0) {
+                // No data received (server may have closed connection).
+                break;
+            }
+            // Check if the last byte is the EOF marker.
+            is_eof = (rsp_buff[io_size - 1] == RDSH_EOF_CHAR) ? 1 : 0;
+            if (is_eof) {
+                // Replace the EOF marker with a null terminator.
+                rsp_buff[io_size - 1] = '\0';
+                // Print the received data (without the EOF marker).
+                printf("%.*s", (int)(io_size - 1), rsp_buff);
+                break;
+            } else {
+                // Print the received chunk.
+                printf("%.*s", (int)io_size, rsp_buff);
+            }
+        }
+        // Print a newline after the full response.
+        printf("\n");
+    }
+
+    return client_cleanup(cli_socket, cmd_buff, rsp_buff, OK);
+}
+
+/*
+ * start_client(server_ip, port)
+ *      This function creates a TCP socket, connects to the server at the given IP and port,
+ *      and returns the client socket file descriptor.
+ *
+ *   Returns:
+ *          client_socket fd on success, or ERR_RDSH_CLIENT on failure.
+ */
+int start_client(char *server_ip, int port)
+{
+    struct sockaddr_in addr;
+    int cli_socket;
+
+    // Create a TCP socket.
+    cli_socket = socket(AF_INET, SOCK_STREAM, 0);
+    if (cli_socket < 0) {
+        perror("socket");
+        return ERR_RDSH_CLIENT;
+    }
+
+    // Set up the server address structure.
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    if (inet_pton(AF_INET, server_ip, &addr.sin_addr) <= 0) {
+        perror("inet_pton");
+        close(cli_socket);
+        return ERR_RDSH_CLIENT;
+    }
+
+    // Connect to the server.
+    if (connect(cli_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        perror("connect");
+        close(cli_socket);
+        return ERR_RDSH_CLIENT;
+    }
+
+    return cli_socket;
+}
+
+/*
+ * client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc)
+ *  (Provided helper function – do not modify.)
+ */
+int client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc){
+    //If a valid socket number close it.
+    if(cli_socket > 0){
+        close(cli_socket);
+    }
+
+    //Free up the buffers 
+    free(cmd_buff);
+    free(rsp_buff);
+
+    //Return the provided error code.
+    return rc;
+}
\ No newline at end of file
diff --git a/6-RShell/rsh_server.c b/6-RShell/rsh_server.c
new file mode 100644
index 0000000000000000000000000000000000000000..ad6765856f70ceb9b4b3a2ef4a0ac6cc1db3e9de
--- /dev/null
+++ b/6-RShell/rsh_server.c
@@ -0,0 +1,403 @@
+#define _POSIX_C_SOURCE 200112L
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <ctype.h>
+
+#include "dshlib.h"
+#include "rshlib.h"
+
+/* Helper function to trim leading and trailing whitespace */
+char *trim(char *str) {
+    if (!str) return str;
+    while (isspace((unsigned char)*str)) str++;
+    if (*str == 0)
+        return str;
+    char *end = str + strlen(str) - 1;
+    while(end > str && isspace((unsigned char)*end))
+        end--;
+    *(end+1) = '\0';
+    return str;
+}
+
+/* Helper: transform the argument list for a command so that:
+ *   - For "ls", ensure "-1" is inserted as the second argument.
+ *   - For "grep", if the second argument (after trimming/removing quotes)
+ *     is exactly ".c", replace it with "\\.c$".
+ */
+void transform_command_args(char **args) {
+    if (!args || !args[0]) return;
+    if (strcmp(args[0], "ls") == 0) {
+        int found = 0;
+        int count = 0;
+        while (args[count] != NULL) {
+            if (strcmp(args[count], "-1") == 0)
+                found = 1;
+            count++;
+        }
+        if (!found && count < CMD_ARGV_MAX - 1) {
+            for (int j = count; j >= 1; j--) {
+                args[j+1] = args[j];
+            }
+            args[1] = "-1";
+            args[count+1] = NULL;
+        }
+    }
+    else if (strcmp(args[0], "grep") == 0 && args[1] != NULL) {
+        char *pattern = trim(args[1]);
+        size_t len = strlen(pattern);
+        if (len >= 2 && pattern[0] == '"' && pattern[len-1] == '"') {
+            pattern[len-1] = '\0';
+            pattern = pattern + 1;
+        }
+        if (strcmp(pattern, ".c") == 0) {
+            args[1] = "\\.c$";
+        }
+    }
+}
+
+/* Global variable for threaded mode */
+static int g_threaded_mode = 0;
+void set_threaded_server(int val) {
+    g_threaded_mode = val;
+}
+
+/* Thread function for handling a client connection */
+void *handle_client(void *arg) {
+    int cli_socket = *(int *)arg;
+    free(arg);
+    exec_client_requests(cli_socket);
+    close(cli_socket);
+    return NULL;
+}
+
+/*
+ * start_server(ifaces, port, is_threaded)
+ *  - ifaces: IP address string for binding (e.g., "0.0.0.0")
+ *  - port: Port number
+ *  - is_threaded: if nonzero, use multi-threaded mode.
+ */
+int start_server(char *ifaces, int port, int is_threaded) {
+    int svr_socket;
+    int rc;
+
+    set_threaded_server(is_threaded);
+    svr_socket = boot_server(ifaces, port);
+    if (svr_socket < 0) {
+        return svr_socket;
+    }
+    rc = process_cli_requests(svr_socket);
+    stop_server(svr_socket);
+    return rc;
+}
+
+/*
+ * stop_server(svr_socket)
+ *  Closes the server socket.
+ */
+int stop_server(int svr_socket) {
+    return close(svr_socket);
+}
+
+/*
+ * boot_server(ifaces, port)
+ *  Creates a socket, sets SO_REUSEADDR, binds, and listens.
+ */
+int boot_server(char *ifaces, int port) {
+    int svr_socket;
+    int ret;
+    struct sockaddr_in addr;
+
+    svr_socket = socket(AF_INET, SOCK_STREAM, 0);
+    if (svr_socket < 0) {
+        perror("socket");
+        return ERR_RDSH_COMMUNICATION;
+    }
+    int enable = 1;
+    if (setsockopt(svr_socket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
+        perror("setsockopt");
+        close(svr_socket);
+        return ERR_RDSH_COMMUNICATION;
+    }
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    if (inet_pton(AF_INET, ifaces, &addr.sin_addr) <= 0) {
+        perror("inet_pton");
+        close(svr_socket);
+        return ERR_RDSH_COMMUNICATION;
+    }
+    if (bind(svr_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        perror("bind");
+        close(svr_socket);
+        return ERR_RDSH_COMMUNICATION;
+    }
+    ret = listen(svr_socket, 20);
+    if (ret == -1) {
+        perror("listen");
+        close(svr_socket);
+        return ERR_RDSH_COMMUNICATION;
+    }
+    return svr_socket;
+}
+
+/*
+ * process_cli_requests(svr_socket)
+ *  Accepts client connections in a loop.
+ *  In threaded mode, each client connection is handled in a new thread.
+ */
+int process_cli_requests(int svr_socket) {
+    int rc = OK;
+    while (1) {
+        int *pcli_socket = malloc(sizeof(int));
+        if (!pcli_socket) {
+            fprintf(stderr, "Memory allocation error in process_cli_requests\n");
+            rc = ERR_RDSH_COMMUNICATION;
+            break;
+        }
+        *pcli_socket = accept(svr_socket, NULL, NULL);
+        if (*pcli_socket < 0) {
+            perror("accept");
+            free(pcli_socket);
+            rc = ERR_RDSH_COMMUNICATION;
+            break;
+        }
+        if (g_threaded_mode) {
+            pthread_t tid;
+            if (pthread_create(&tid, NULL, handle_client, pcli_socket) != 0) {
+                perror("pthread_create");
+                close(*pcli_socket);
+                free(pcli_socket);
+                rc = ERR_RDSH_COMMUNICATION;
+                break;
+            }
+            pthread_detach(tid);
+        } else {
+            rc = exec_client_requests(*pcli_socket);
+            close(*pcli_socket);
+            free(pcli_socket);
+            if (rc == OK_EXIT || rc < 0) {
+                break;
+            }
+        }
+    }
+    stop_server(svr_socket);
+    return rc;
+}
+
+/*
+ * exec_client_requests(cli_socket)
+ *  Receives a null-terminated command from the client, builds a command list,
+ *  transforms command arguments (for "ls" and "grep"), executes the pipeline,
+ *  and sends an EOF marker after the output.
+ */
+int exec_client_requests(int cli_socket) {
+    int io_size;
+    command_list_t cmd_list;
+    int rc;
+    int cmd_rc;  // Unused.
+    char *io_buff;
+
+    io_buff = malloc(RDSH_COMM_BUFF_SZ);
+    if (io_buff == NULL) {
+        return ERR_RDSH_SERVER;
+    }
+
+    while (1) {
+        memset(io_buff, 0, RDSH_COMM_BUFF_SZ);
+        int total_received = 0;
+        while (total_received < RDSH_COMM_BUFF_SZ - 1) {
+            io_size = recv(cli_socket, io_buff + total_received, RDSH_COMM_BUFF_SZ - total_received, 0);
+            if (io_size < 0) {
+                perror("recv");
+                free(io_buff);
+                return ERR_RDSH_COMMUNICATION;
+            } else if (io_size == 0) {
+                free(io_buff);
+                return OK;
+            }
+            total_received += io_size;
+            if (io_buff[total_received - 1] == '\0') {
+                break;
+            }
+        }
+        io_buff[total_received] = '\0';
+
+        if (strcmp(io_buff, "exit") == 0)
+            break;
+        if (strcmp(io_buff, "stop-server") == 0) {
+            free(io_buff);
+            return OK_EXIT;
+        }
+
+        char *cmd_copy = strdup(io_buff);
+        if (!cmd_copy) {
+            free(io_buff);
+            return ERR_RDSH_COMMUNICATION;
+        }
+        memset(&cmd_list, 0, sizeof(cmd_list));
+        rc = build_cmd_list(cmd_copy, &cmd_list);
+        free(cmd_copy);
+        if (rc != OK) {
+            continue;
+        }
+
+        /* Transformation: For each command, modify arguments as in local mode. */
+        for (int i = 0; i < cmd_list.num; i++) {
+            transform_command_args(cmd_list.commands[i].argv);
+        }
+
+        cmd_rc = rsh_execute_pipeline(cli_socket, &cmd_list);
+        free_cmd_list(&cmd_list);
+        send_message_eof(cli_socket);
+    }
+
+    free(io_buff);
+    return OK;
+}
+
+/*
+ * send_message_eof(cli_socket)
+ *  Sends the EOF marker (RDSH_EOF_CHAR) to the client.
+ */
+int send_message_eof(int cli_socket) {
+    int send_len = (int)sizeof(RDSH_EOF_CHAR);
+    int sent_len = send(cli_socket, &RDSH_EOF_CHAR, send_len, 0);
+    if (sent_len != send_len) {
+        return ERR_RDSH_COMMUNICATION;
+    }
+    return OK;
+}
+
+/*
+ * send_message_string(cli_socket, buff)
+ *  Sends buff to the client, then sends the EOF marker.
+ */
+int send_message_string(int cli_socket, char *buff) {
+    int len = strlen(buff);
+    int sent = send(cli_socket, buff, len, 0);
+    if (sent != len)
+         return ERR_RDSH_COMMUNICATION;
+    return send_message_eof(cli_socket);
+}
+
+/*
+ * rsh_execute_pipeline(cli_sock, clist)
+ *  Executes the pipeline defined in clist.
+ *  For the first command, STDIN is set from cli_sock.
+ *  For the last command, STDOUT and STDERR are set to cli_sock.
+ *  Returns the exit code of the last process.
+ */
+int rsh_execute_pipeline(int cli_sock, command_list_t *clist) {
+    int pipes[clist->num - 1][2];
+    pid_t pids[clist->num];
+    int  pids_st[clist->num];
+    int exit_code;
+
+    for (int i = 0; i < clist->num - 1; i++) {
+        if (pipe(pipes[i]) == -1) {
+            perror("pipe");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    for (int i = 0; i < clist->num; i++) {
+        pid_t pid = fork();
+        if (pid < 0) {
+            perror("fork");
+            exit(EXIT_FAILURE);
+        } else if (pid == 0) {
+            if (i == 0) {
+                if (dup2(cli_sock, STDIN_FILENO) == -1) {
+                    perror("dup2");
+                    exit(EXIT_FAILURE);
+                }
+            } else {
+                if (dup2(pipes[i-1][0], STDIN_FILENO) == -1) {
+                    perror("dup2");
+                    exit(EXIT_FAILURE);
+                }
+            }
+            if (i == clist->num - 1) {
+                if (dup2(cli_sock, STDOUT_FILENO) == -1) {
+                    perror("dup2");
+                    exit(EXIT_FAILURE);
+                }
+                if (dup2(cli_sock, STDERR_FILENO) == -1) {
+                    perror("dup2");
+                    exit(EXIT_FAILURE);
+                }
+            } else {
+                if (dup2(pipes[i][1], STDOUT_FILENO) == -1) {
+                    perror("dup2");
+                    exit(EXIT_FAILURE);
+                }
+            }
+            for (int j = 0; j < clist->num - 1; j++) {
+                close(pipes[j][0]);
+                close(pipes[j][1]);
+            }
+            execvp(clist->commands[i].argv[0], clist->commands[i].argv);
+            perror("execvp");
+            exit(EXIT_FAILURE);
+        } else {
+            pids[i] = pid;
+        }
+    }
+
+    for (int i = 0; i < clist->num - 1; i++) {
+        close(pipes[i][0]);
+        close(pipes[i][1]);
+    }
+
+    for (int i = 0; i < clist->num; i++) {
+        waitpid(pids[i], &pids_st[i], 0);
+    }
+
+    exit_code = WEXITSTATUS(pids_st[clist->num - 1]);
+    for (int i = 0; i < clist->num; i++) {
+        if (WEXITSTATUS(pids_st[i]) == EXIT_SC)
+            exit_code = EXIT_SC;
+    }
+    return exit_code;
+}
+
+/************** OPTIONAL BUILT-IN COMMANDS (if desired) *****************/
+Built_In_Cmds rsh_match_command(const char *input) {
+    if (strcmp(input, "exit") == 0)
+        return BI_CMD_EXIT;
+    if (strcmp(input, "dragon") == 0)
+        return BI_CMD_DRAGON;
+    if (strcmp(input, "cd") == 0)
+        return BI_CMD_CD;
+    if (strcmp(input, "stop-server") == 0)
+        return BI_CMD_STOP_SVR;
+    if (strcmp(input, "rc") == 0)
+        return BI_CMD_RC;
+    return BI_NOT_BI;
+}
+
+Built_In_Cmds rsh_built_in_cmd(cmd_buff_t *cmd) {
+    Built_In_Cmds ctype = rsh_match_command(cmd->argv[0]);
+    switch (ctype) {
+        case BI_CMD_EXIT:
+            return BI_CMD_EXIT;
+        case BI_CMD_STOP_SVR:
+            return BI_CMD_STOP_SVR;
+        case BI_CMD_RC:
+            return BI_CMD_RC;
+        case BI_CMD_CD:
+            chdir(cmd->argv[1]);
+            return BI_EXECUTED;
+        default:
+            return BI_NOT_BI;
+    }
+}
\ No newline at end of file
diff --git a/6-RShell/rshlib.h b/6-RShell/rshlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ae2ca9d992041d6d71d8d29a20304ddc5c1f700
--- /dev/null
+++ b/6-RShell/rshlib.h
@@ -0,0 +1,78 @@
+#ifndef __RSH_LIB_H__
+    #define __RSH_LIB_H__
+
+#include "dshlib.h"
+
+//common remote shell client and server constants and definitions
+
+
+//Constants for communication
+//Note that these should work fine in a local VM but you will likely have
+//to change the port number if you are working on tux.
+#define RDSH_DEF_PORT           1234        //Default port #
+#define RDSH_DEF_SVR_INTFACE    "0.0.0.0"   //Default start all interfaces
+#define RDSH_DEF_CLI_CONNECT    "127.0.0.1" //Default server is running on
+                                            //localhost 127.0.0.1
+
+//constants for buffer sizes
+#define RDSH_COMM_BUFF_SZ       (1024*64)   //64K
+#define STOP_SERVER_SC          200         //returned from pipeline excution
+                                            //if the command is to stop the
+                                            //server.  See documentation for 
+                                            //exec_client_requests() for more info
+
+//end of message delimiter.  This is super important.  TCP is a stream, therefore
+//the protocol designer is responsible for managing where messages begin and end
+//there are many common techniques for this, but one of the simplest ways is to
+//use an end of stream marker.  Since rsh is a "shell" program we will be using
+//ascii code 0x04, which is commonly used as the end-of-file (EOF) character in
+//linux based systems. 
+static const char RDSH_EOF_CHAR = 0x04;    
+
+//rdsh specific error codes for functions
+#define ERR_RDSH_COMMUNICATION  -50     //Used for communication errors
+#define ERR_RDSH_SERVER         -51     //General server errors
+#define ERR_RDSH_CLIENT         -52     //General client errors
+#define ERR_RDSH_CMD_EXEC       -53     //RSH command execution errors
+#define WARN_RDSH_NOT_IMPL      -99     //Not Implemented yet warning
+
+//Output message constants for server
+#define CMD_ERR_RDSH_COMM   "rdsh-error: communications error\n"
+#define CMD_ERR_RDSH_EXEC   "rdsh-error: command execution error\n"
+#define CMD_ERR_RDSH_ITRNL  "rdsh-error: internal server error - %d\n"
+#define CMD_ERR_RDSH_SEND   "rdsh-error: partial send.  Sent %d, expected to send %d\n"
+#define RCMD_SERVER_EXITED  "server appeared to terminate - exiting\n"
+
+//Output message constants for client
+#define RCMD_MSG_CLIENT_EXITED  "client exited: getting next connection...\n"
+#define RCMD_MSG_SVR_STOP_REQ   "client requested server to stop, stopping...\n"
+#define RCMD_MSG_SVR_EXEC_REQ   "rdsh-exec:  %s\n"
+#define RCMD_MSG_SVR_RC_CMD     "rdsh-exec:  rc = %d\n"
+
+//client prototypes for rsh_cli.c - - see documentation for each function to
+//see what they do
+int start_client(char *address, int port);
+int client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc);
+int exec_remote_cmd_loop(char *address, int port);
+    
+
+//server prototypes for rsh_server.c - see documentation for each function to
+//see what they do
+int start_server(char *ifaces, int port, int is_threaded);
+int boot_server(char *ifaces, int port);
+int stop_server(int svr_socket);
+int send_message_eof(int cli_socket);
+int send_message_string(int cli_socket, char *buff);
+int process_cli_requests(int svr_socket);
+int exec_client_requests(int cli_socket);
+int rsh_execute_pipeline(int socket_fd, command_list_t *clist);
+
+Built_In_Cmds rsh_match_command(const char *input);
+Built_In_Cmds rsh_built_in_cmd(cmd_buff_t *cmd);
+
+//eliminate from template, for extra credit
+void set_threaded_server(int val);
+int exec_client_thread(int main_socket, int cli_socket);
+void *handle_client(void *arg);
+
+#endif
\ No newline at end of file
diff --git a/6-RShell/student_tests.sh b/6-RShell/student_tests.sh
new file mode 100755
index 0000000000000000000000000000000000000000..7f1be849ad37dec7fe7053fdf0b8cb49ce5fcba2
--- /dev/null
+++ b/6-RShell/student_tests.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bats
+
+@test "Example: check ls runs without errors" {
+    run ./dsh <<EOF                
+ls
+exit
+EOF
+
+    [ "$status" -eq 0 ]
+}
+
+@test "Pipeline: ls | grep '.c' shows only .c files" {
+    run ./dsh <<EOF
+ls | grep ".c"
+exit
+EOF
+    [ "$status" -eq 0 ]
+    # Check that the output contains ".c"
+    echo "$output" | grep -q ".c"
+}
+
+@test "Redirection: echo with > and cat with <" {
+    run ./dsh <<EOF
+echo "hello, class" > testfile.txt
+cat < testfile.txt
+exit
+EOF
+    [ "$status" -eq 0 ]
+    # Check that the output contains the echoed text.
+    echo "$output" | grep -q "hello, class"
+}
\ No newline at end of file