diff --git a/main/agent/context_builder.c b/main/agent/context_builder.c index bca2e37..e8a5625 100644 --- a/main/agent/context_builder.c +++ b/main/agent/context_builder.c @@ -40,10 +40,10 @@ esp_err_t context_build_system_prompt(char *buf, size_t size) "Use this when you need up-to-date facts, news, weather, or anything beyond your training data.\n" "- get_current_time: Get the current date and time. " "You do NOT have an internal clock — always use this tool when you need to know the time or date.\n" - "- read_file: Read a file from SPIFFS (path must start with /spiffs/).\n" - "- write_file: Write/overwrite a file on SPIFFS.\n" - "- edit_file: Find-and-replace edit a file on SPIFFS.\n" - "- list_dir: List files on SPIFFS, optionally filter by prefix.\n" + "- read_file: Read a file (path must start with " MIMI_SPIFFS_BASE "/).\n" + "- write_file: Write/overwrite a file.\n" + "- edit_file: Find-and-replace edit a file.\n" + "- list_dir: List files, optionally filter by prefix.\n" "- cron_add: Schedule a recurring or one-shot task. The message will trigger an agent turn when the job fires.\n" "- cron_list: List all scheduled cron jobs.\n" "- cron_remove: Remove a scheduled cron job by ID.\n\n" @@ -51,8 +51,8 @@ esp_err_t context_build_system_prompt(char *buf, size_t size) "Use tools when needed. Provide your final answer as text after using tools.\n\n" "## Memory\n" "You have persistent memory stored on local flash:\n" - "- Long-term memory: /spiffs/memory/MEMORY.md\n" - "- Daily notes: /spiffs/memory/daily/.md\n\n" + "- Long-term memory: " MIMI_SPIFFS_MEMORY_DIR "/MEMORY.md\n" + "- Daily notes: " MIMI_SPIFFS_MEMORY_DIR "/daily/.md\n\n" "IMPORTANT: Actively use memory to remember things across conversations.\n" "- When you learn something new about the user (name, preferences, habits, context), write it to MEMORY.md.\n" "- When something noteworthy happens in a conversation, append it to today's daily note.\n" @@ -61,9 +61,9 @@ esp_err_t context_build_system_prompt(char *buf, size_t size) "- Keep MEMORY.md concise and organized — summarize, don't dump raw conversation.\n" "- You should proactively save memory without being asked. If the user tells you their name, preferences, or important facts, persist them immediately.\n\n" "## Skills\n" - "Skills are specialized instruction files stored in /spiffs/skills/.\n" + "Skills are specialized instruction files stored in " MIMI_SKILLS_PREFIX ".\n" "When a task matches a skill, read the full skill file for detailed instructions.\n" - "You can create new skills using write_file to /spiffs/skills/.md.\n"); + "You can create new skills using write_file to " MIMI_SKILLS_PREFIX ".md.\n"); /* Bootstrap files */ off = append_file(buf, size, off, MIMI_SOUL_FILE, "Personality"); diff --git a/main/cli/serial_cli.c b/main/cli/serial_cli.c index 8667a45..4ac76a9 100644 --- a/main/cli/serial_cli.c +++ b/main/cli/serial_cli.c @@ -280,7 +280,7 @@ static int cmd_skill_list(int argc, char **argv) size_t n = skill_loader_build_summary(buf, 4096); if (n == 0) { - printf("No skills found under /spiffs/skills/.\n"); + printf("No skills found under " MIMI_SKILLS_PREFIX ".\n"); } else { printf("=== Skills ===\n%s", buf); } @@ -307,9 +307,9 @@ static bool build_skill_path(const char *name, char *out, size_t out_size) if (strchr(name, '/') != NULL || strchr(name, '\\') != NULL) return false; if (has_md_suffix(name)) { - snprintf(out, out_size, "/spiffs/skills/%s", name); + snprintf(out, out_size, MIMI_SKILLS_PREFIX "%s", name); } else { - snprintf(out, out_size, "/spiffs/skills/%s.md", name); + snprintf(out, out_size, MIMI_SKILLS_PREFIX "%s.md", name); } return true; } @@ -375,9 +375,9 @@ static int cmd_skill_search(int argc, char **argv) } const char *keyword = skill_search_args.keyword->sval[0]; - DIR *dir = opendir("/spiffs"); + DIR *dir = opendir(MIMI_SPIFFS_BASE); if (!dir) { - printf("Cannot open /spiffs.\n"); + printf("Cannot open " MIMI_SPIFFS_BASE ".\n"); return 1; } @@ -395,7 +395,7 @@ static int cmd_skill_search(int argc, char **argv) if (strcmp(name + name_len - 3, ".md") != 0) continue; char full_path[296]; - snprintf(full_path, sizeof(full_path), "/spiffs/%s", name); + snprintf(full_path, sizeof(full_path), MIMI_SPIFFS_BASE "/%s", name); bool file_matched = contains_nocase(name, keyword); int matched_line = 0; @@ -655,7 +655,7 @@ esp_err_t serial_cli_init(void) /* skill_list */ esp_console_cmd_t skill_list_cmd = { .command = "skill_list", - .help = "List installed skills from /spiffs/skills/", + .help = "List installed skills from " MIMI_SKILLS_PREFIX, .func = &cmd_skill_list, }; esp_console_cmd_register(&skill_list_cmd); diff --git a/main/mimi_config.h b/main/mimi_config.h index c90eb34..9244f05 100644 --- a/main/mimi_config.h +++ b/main/mimi_config.h @@ -83,24 +83,24 @@ /* Memory / SPIFFS */ #define MIMI_SPIFFS_BASE "/spiffs" -#define MIMI_SPIFFS_CONFIG_DIR "/spiffs/config" -#define MIMI_SPIFFS_MEMORY_DIR "/spiffs/memory" -#define MIMI_SPIFFS_SESSION_DIR "/spiffs/sessions" -#define MIMI_MEMORY_FILE "/spiffs/memory/MEMORY.md" -#define MIMI_SOUL_FILE "/spiffs/config/SOUL.md" -#define MIMI_USER_FILE "/spiffs/config/USER.md" +#define MIMI_SPIFFS_CONFIG_DIR MIMI_SPIFFS_BASE "/config" +#define MIMI_SPIFFS_MEMORY_DIR MIMI_SPIFFS_BASE "/memory" +#define MIMI_SPIFFS_SESSION_DIR MIMI_SPIFFS_BASE "/sessions" +#define MIMI_MEMORY_FILE MIMI_SPIFFS_MEMORY_DIR "/MEMORY.md" +#define MIMI_SOUL_FILE MIMI_SPIFFS_CONFIG_DIR "/SOUL.md" +#define MIMI_USER_FILE MIMI_SPIFFS_CONFIG_DIR "/USER.md" #define MIMI_CONTEXT_BUF_SIZE (16 * 1024) #define MIMI_SESSION_MAX_MSGS 20 /* Cron / Heartbeat */ -#define MIMI_CRON_FILE "/spiffs/cron.json" +#define MIMI_CRON_FILE MIMI_SPIFFS_BASE "/cron.json" #define MIMI_CRON_MAX_JOBS 16 #define MIMI_CRON_CHECK_INTERVAL_MS (60 * 1000) -#define MIMI_HEARTBEAT_FILE "/spiffs/HEARTBEAT.md" +#define MIMI_HEARTBEAT_FILE MIMI_SPIFFS_BASE "/HEARTBEAT.md" #define MIMI_HEARTBEAT_INTERVAL_MS (30 * 60 * 1000) /* Skills */ -#define MIMI_SKILLS_PREFIX "/spiffs/skills/" +#define MIMI_SKILLS_PREFIX MIMI_SPIFFS_BASE "/skills/" /* WebSocket Gateway */ #define MIMI_WS_PORT 18789 diff --git a/main/skills/skill_loader.c b/main/skills/skill_loader.c index 32b85fe..0dc5e67 100644 --- a/main/skills/skill_loader.c +++ b/main/skills/skill_loader.c @@ -41,7 +41,7 @@ static const char *TAG = "skills"; "\n" \ "## How to use\n" \ "1. Use get_current_time for today's date\n" \ - "2. Read /spiffs/memory/MEMORY.md for user preferences and context\n" \ + "2. Read " MIMI_SPIFFS_MEMORY_DIR "/MEMORY.md for user preferences and context\n" \ "3. Read today's daily note if it exists\n" \ "4. Use web_search for relevant news based on user interests\n" \ "5. Compile a concise briefing covering:\n" \ @@ -70,7 +70,7 @@ static const char *TAG = "skills"; " - `## When to use` — trigger conditions\n" \ " - `## How to use` — step-by-step instructions\n" \ " - `## Example` — concrete example (optional but helpful)\n" \ - "3. Save to `/spiffs/skills/.md` using write_file\n" \ + "3. Save to `" MIMI_SKILLS_PREFIX ".md` using write_file\n" \ "4. The skill will be automatically available after the next conversation\n" \ "\n" \ "## Best practices\n" \ @@ -81,7 +81,7 @@ static const char *TAG = "skills"; "\n" \ "## Example\n" \ "To create a \"translate\" skill:\n" \ - "write_file path=\"/spiffs/skills/translate.md\" content=\"# Translate\\n\\nTranslate text between languages.\\n\\n" \ + "write_file path=\"" MIMI_SKILLS_PREFIX "translate.md\" content=\"# Translate\\n\\nTranslate text between languages.\\n\\n" \ "## When to use\\nWhen the user asks to translate text.\\n\\n" \ "## How to use\\n1. Identify source and target languages\\n" \ "2. Translate directly using your language knowledge\\n" \ diff --git a/main/tools/tool_files.c b/main/tools/tool_files.c index 4d70d50..3b2a62b 100644 --- a/main/tools/tool_files.c +++ b/main/tools/tool_files.c @@ -15,12 +15,17 @@ static const char *TAG = "tool_files"; #define MAX_FILE_SIZE (32 * 1024) /** - * Validate that a path starts with /spiffs/ and contains no ".." traversal. + * Validate that a path starts with MIMI_SPIFFS_BASE and contains no ".." traversal. */ static bool validate_path(const char *path) { if (!path) return false; - if (strncmp(path, "/spiffs/", 8) != 0) return false; + size_t base_len = strlen(MIMI_SPIFFS_BASE); + if (strncmp(path, MIMI_SPIFFS_BASE, base_len) != 0) return false; + /* Require a path separator after the base (unless base ends with '/') */ + if (base_len > 0 && MIMI_SPIFFS_BASE[base_len - 1] != '/') { + if (path[base_len] != '/') return false; + } if (strstr(path, "..") != NULL) return false; return true; } @@ -37,7 +42,7 @@ esp_err_t tool_read_file_execute(const char *input_json, char *output, size_t ou const char *path = cJSON_GetStringValue(cJSON_GetObjectItem(root, "path")); if (!validate_path(path)) { - snprintf(output, output_size, "Error: path must start with /spiffs/ and must not contain '..'"); + snprintf(output, output_size, "Error: path must start with %s/ and must not contain '..'", MIMI_SPIFFS_BASE); cJSON_Delete(root); return ESP_ERR_INVALID_ARG; } @@ -75,7 +80,7 @@ esp_err_t tool_write_file_execute(const char *input_json, char *output, size_t o const char *content = cJSON_GetStringValue(cJSON_GetObjectItem(root, "content")); if (!validate_path(path)) { - snprintf(output, output_size, "Error: path must start with /spiffs/ and must not contain '..'"); + snprintf(output, output_size, "Error: path must start with %s/ and must not contain '..'", MIMI_SPIFFS_BASE); cJSON_Delete(root); return ESP_ERR_INVALID_ARG; } @@ -123,7 +128,7 @@ esp_err_t tool_edit_file_execute(const char *input_json, char *output, size_t ou const char *new_str = cJSON_GetStringValue(cJSON_GetObjectItem(root, "new_string")); if (!validate_path(path)) { - snprintf(output, output_size, "Error: path must start with /spiffs/ and must not contain '..'"); + snprintf(output, output_size, "Error: path must start with %s/ and must not contain '..'", MIMI_SPIFFS_BASE); cJSON_Delete(root); return ESP_ERR_INVALID_ARG; } @@ -226,7 +231,7 @@ esp_err_t tool_list_dir_execute(const char *input_json, char *output, size_t out DIR *dir = opendir(MIMI_SPIFFS_BASE); if (!dir) { - snprintf(output, output_size, "Error: cannot open /spiffs directory"); + snprintf(output, output_size, "Error: cannot open %s directory", MIMI_SPIFFS_BASE); cJSON_Delete(root); return ESP_FAIL; } diff --git a/main/tools/tool_files.h b/main/tools/tool_files.h index b84dd46..4b47909 100644 --- a/main/tools/tool_files.h +++ b/main/tools/tool_files.h @@ -5,24 +5,24 @@ /** * Read a file from SPIFFS. - * Input JSON: {"path": "/spiffs/..."} + * Input JSON: {"path": "/..."} */ esp_err_t tool_read_file_execute(const char *input_json, char *output, size_t output_size); /** * Write/overwrite a file on SPIFFS. - * Input JSON: {"path": "/spiffs/...", "content": "..."} + * Input JSON: {"path": "/...", "content": "..."} */ esp_err_t tool_write_file_execute(const char *input_json, char *output, size_t output_size); /** * Find-and-replace edit a file on SPIFFS. - * Input JSON: {"path": "/spiffs/...", "old_string": "...", "new_string": "..."} + * Input JSON: {"path": "/...", "old_string": "...", "new_string": "..."} */ esp_err_t tool_edit_file_execute(const char *input_json, char *output, size_t output_size); /** * List files on SPIFFS, optionally filtered by path prefix. - * Input JSON: {"prefix": "/spiffs/..."} (prefix is optional) + * Input JSON: {"prefix": "/..."} (prefix is optional) */ esp_err_t tool_list_dir_execute(const char *input_json, char *output, size_t output_size); diff --git a/main/tools/tool_registry.c b/main/tools/tool_registry.c index d4dde35..6323ef1 100644 --- a/main/tools/tool_registry.c +++ b/main/tools/tool_registry.c @@ -1,4 +1,5 @@ #include "tool_registry.h" +#include "mimi_config.h" #include "tools/tool_web_search.h" #include "tools/tool_get_time.h" #include "tools/tool_files.h" @@ -83,10 +84,10 @@ esp_err_t tool_registry_init(void) /* Register read_file */ mimi_tool_t rf = { .name = "read_file", - .description = "Read a file from SPIFFS storage. Path must start with /spiffs/.", + .description = "Read a file from SPIFFS storage. Path must start with " MIMI_SPIFFS_BASE "/.", .input_schema_json = "{\"type\":\"object\"," - "\"properties\":{\"path\":{\"type\":\"string\",\"description\":\"Absolute path starting with /spiffs/\"}}," + "\"properties\":{\"path\":{\"type\":\"string\",\"description\":\"Absolute path starting with " MIMI_SPIFFS_BASE "/\"}}," "\"required\":[\"path\"]}", .execute = tool_read_file_execute, }; @@ -95,10 +96,10 @@ esp_err_t tool_registry_init(void) /* Register write_file */ mimi_tool_t wf = { .name = "write_file", - .description = "Write or overwrite a file on SPIFFS storage. Path must start with /spiffs/.", + .description = "Write or overwrite a file on SPIFFS storage. Path must start with " MIMI_SPIFFS_BASE "/.", .input_schema_json = "{\"type\":\"object\"," - "\"properties\":{\"path\":{\"type\":\"string\",\"description\":\"Absolute path starting with /spiffs/\"}," + "\"properties\":{\"path\":{\"type\":\"string\",\"description\":\"Absolute path starting with " MIMI_SPIFFS_BASE "/\"}," "\"content\":{\"type\":\"string\",\"description\":\"File content to write\"}}," "\"required\":[\"path\",\"content\"]}", .execute = tool_write_file_execute, @@ -111,7 +112,7 @@ esp_err_t tool_registry_init(void) .description = "Find and replace text in a file on SPIFFS. Replaces first occurrence of old_string with new_string.", .input_schema_json = "{\"type\":\"object\"," - "\"properties\":{\"path\":{\"type\":\"string\",\"description\":\"Absolute path starting with /spiffs/\"}," + "\"properties\":{\"path\":{\"type\":\"string\",\"description\":\"Absolute path starting with " MIMI_SPIFFS_BASE "/\"}," "\"old_string\":{\"type\":\"string\",\"description\":\"Text to find\"}," "\"new_string\":{\"type\":\"string\",\"description\":\"Replacement text\"}}," "\"required\":[\"path\",\"old_string\",\"new_string\"]}", @@ -125,7 +126,7 @@ esp_err_t tool_registry_init(void) .description = "List files on SPIFFS storage, optionally filtered by path prefix.", .input_schema_json = "{\"type\":\"object\"," - "\"properties\":{\"prefix\":{\"type\":\"string\",\"description\":\"Optional path prefix filter, e.g. /spiffs/memory/\"}}," + "\"properties\":{\"prefix\":{\"type\":\"string\",\"description\":\"Optional path prefix filter, e.g. " MIMI_SPIFFS_BASE "/memory/\"}}," "\"required\":[]}", .execute = tool_list_dir_execute, };