Agents + bash
diff --git a/tools/bash.go b/tools/bash.go
new file mode 100644
index 0000000..71285bd
--- /dev/null
+++ b/tools/bash.go
@@ -0,0 +1,18 @@
+package tools
+
+import (
+	"os/exec"
+)
+
+type BashCommandArgs struct {
+	Command string `json:"command" jsonschema:"title=command,description=bash command to run,required"`
+}
+
+func BashCommand(args BashCommandArgs) (string, error) {
+	cmd := exec.Command("bash", "-c", args.Command)
+	if out, err := cmd.Output(); err != nil {
+		return "", err
+	} else {
+		return string(out), nil
+	}
+}
diff --git a/tools/file.go b/tools/file.go
index 815c2eb..405d046 100644
--- a/tools/file.go
+++ b/tools/file.go
@@ -2,6 +2,7 @@
 
 import (
 	"os"
+	"path/filepath"
 )
 
 type FileReadArgs struct {
@@ -24,7 +25,27 @@
 type FileWriteResult struct {
 }
 
+func sanitizePath(p string) (string, error) {
+	cwd, err := os.Getwd()
+	if err != nil {
+		return "", err
+	}
+	p = filepath.Join(cwd, p)
+	p, err = filepath.Rel(p, cwd)
+	if err != nil {
+		return "", err
+	}
+	return p, nil
+}
+
 func FileWrite(args FileWriteArgs) (string, error) {
+	p, err := sanitizePath(args.Path)
+	if err != nil {
+		return "", err
+	}
+	if os.MkdirAll(filepath.Dir(p), 0666) != nil {
+		return "", nil
+	}
 	if err := os.WriteFile(args.Path, []byte(args.Contents), 0666); err != nil {
 		return "error", err
 	} else {
diff --git a/tools/init.go b/tools/init.go
index 7c2ec4d..a04cf98 100644
--- a/tools/init.go
+++ b/tools/init.go
@@ -4,5 +4,6 @@
 	reg.Add(NewFuncTool("file_read", FileRead, "Reads contents of the given file."))
 	reg.Add(NewFuncTool("file_write", FileWrite, "Writes given contents to a file."))
 	reg.Add(NewFuncTool("dir_list", DirList, "Reads directory with given name, returning all its entries."))
+	reg.Add(NewFuncTool("bash_command", BashCommand, "Runs given bash command and returns result back."))
 	return nil
 }