From 0634f2048c7c9a474aab1d8104b964b3bf70a064 Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Sat, 4 Jul 2026 17:21:32 +0900 Subject: feat(P1-002): migrate offline JSONL import/export to dedicated CLI - Replace the MCP import_messages tool with standalone CLI commands: iny-convmem-mcp --data-dir --import iny-convmem-mcp --data-dir --export - Add src/cli/{import,export,cli.h} and a CTest process test (tiny-convmem-mcp.cli) covering round-trip, manifest generation, overwrite/reject policies, and include_deleted export. - Fix Debug heap corruption in iny_store_import() by deep-copying imported messages instead of taking ownership of caller-allocated records, which were later freed by the caller. - Remove the now-redundant src/mcp/tools/import_messages.c and keep the tool registry at 7 MCP tools. - Update docs/development-plan.md (P1-002 complete) and docs/implementation-status.md with CLI test coverage. --- src/cli/export.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/cli/export.c (limited to 'src/cli/export.c') diff --git a/src/cli/export.c b/src/cli/export.c new file mode 100644 index 0000000..4288ba4 --- /dev/null +++ b/src/cli/export.c @@ -0,0 +1,98 @@ +#include "cli/cli.h" + +#include "mcp/tools/common/common.h" +#include "storage/codec.h" +#include "storage/store.h" + +#include +#include +#include + +int +tiny_cli_export(tiny_store_t *store, const tiny_cli_options_t *options) +{ + size_t index; + size_t emitted = 0; + FILE *output = NULL; + tiny_json_buffer_t item; + + if (!options || !options->export_path) + { + fprintf(stderr, "export: path is required\n"); + return 1; + } + + if (strcmp(options->export_path, "-") == 0) + { + output = stdout; + } + else + { + if (fopen_s(&output, options->export_path, "wb") != 0 || !output) + { + fprintf(stderr, "export: unable to open '%s'\n", options->export_path); + return 1; + } + } + + if (fprintf(output, "{\"type\":\"tiny-convmem-manifest\",\"format_version\":2}\n") < 0) + { + fprintf(stderr, "export: failed to write manifest\n"); + if (output != stdout) + { + fclose(output); + } + return 1; + } + + for (index = 0; index < tiny_store_entry_count(store); ++index) + { + const tiny_store_entry_t *entry = tiny_store_entry(store, index); + int write_failed; + if (!entry) + { + continue; + } + if (entry->deleted && !options->include_deleted) + { + continue; + } + if (options->session_id && options->session_id[0] + && strcmp(entry->message.session_id, options->session_id) != 0) + { + continue; + } + + tiny_json_init(&item); + if (entry->deleted) + { + tiny_json_raw(&item, "{\"type\":\"DEL\",\"message_id\":"); + tiny_json_string(&item, entry->message.message_id); + tiny_json_raw(&item, "}"); + } + else + { + tiny_mcp_tool_append_message(&item, &entry->message); + } + + write_failed = item.failed || fprintf(output, "%s\n", item.data ? item.data : "") < 0; + tiny_json_free(&item); + if (write_failed) + { + fprintf(stderr, "export: failed to write record\n"); + if (output != stdout) + { + fclose(output); + } + return 1; + } + emitted++; + } + + if (output != stdout) + { + fclose(output); + } + (void)emitted; + return 0; +} -- cgit v1.2.3