From b3af44832790fbc6dd6a7e09d18362f55149a4da Mon Sep 17 00:00:00 2001 From: Hubert Tarnacki Date: Wed, 9 Sep 2026 22:44:18 +0200 Subject: [PATCH] fix(cli): accept JSON array literals for array-typed arguments `codebase-memory-mcp cli --flag value` builds the tool's JSON arguments from typed flags. Array-typed parameters such as `semantic_query` were only accepted as repeated flags (`--semantic-query a --semantic-query b`); passing the JSON spelling shown in the MCP schema (`--semantic-query '["a","b"]'`) was stored as a single opaque string element, so the search ran against the literal text `["a","b"]` instead of two keywords. Parse a value that is a JSON array of strings into individual elements. Any other value keeps the existing behaviour, so repeated flags and plain strings are unaffected. Signed-off-by: Hubert Tarnacki --- src/cli/cli.c | 31 +++++++++++++++++++++++++++++++ tests/test_cli.c | 11 +++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/cli/cli.c b/src/cli/cli.c index 956489cd9..460675cd3 100644 --- a/src/cli/cli.c +++ b/src/cli/cli.c @@ -13212,6 +13212,37 @@ static void cli_add_typed(yyjson_mut_doc *out, yyjson_mut_val *obj, const char * arr = yyjson_mut_arr(out); yyjson_mut_obj_add(obj, yyjson_mut_strcpy(out, key), arr); } + /* Accept the JSON-array spelling shown by MCP schemas in addition to + * repeated flags. This keeps `--semantic-query '["a","b"]'` from + * becoming one opaque keyword while preserving the established + * `--semantic-query a --semantic-query b` form. */ + yyjson_doc *array_doc = + have_value && value && value[0] == '[' ? yyjson_read(value, strlen(value), 0) : NULL; + yyjson_val *array_root = array_doc ? yyjson_doc_get_root(array_doc) : NULL; + bool string_array = + array_root && yyjson_is_arr(array_root) && yyjson_arr_size(array_root) > 0; + if (string_array) { + size_t idx; + size_t count; + yyjson_val *item; + yyjson_arr_foreach(array_root, idx, count, item) { + if (!yyjson_is_str(item)) { + string_array = false; + break; + } + } + } + if (string_array) { + size_t idx; + size_t count; + yyjson_val *item; + yyjson_arr_foreach(array_root, idx, count, item) { + yyjson_mut_arr_add_strcpy(out, arr, yyjson_get_str(item)); + } + yyjson_doc_free(array_doc); + return; + } + yyjson_doc_free(array_doc); yyjson_mut_arr_add_strcpy(out, arr, have_value ? value : ""); return; } diff --git a/tests/test_cli.c b/tests/test_cli.c index ae1a5c5f4..1d6ed0210 100644 --- a/tests/test_cli.c +++ b/tests/test_cli.c @@ -14141,6 +14141,16 @@ TEST(cli_build_args_json_repeated_array_issue680) { PASS(); } +TEST(cli_build_args_json_array_literal) { + char *err = NULL; + char *argv[] = {"--semantic-query", "[\"create\",\"billing\",\"account\"]"}; + char *json = cbm_cli_build_args_json("search_graph", 2, argv, &err); + ASSERT_NOT_NULL(json); + ASSERT(strstr(json, "\"semantic_query\":[\"create\",\"billing\",\"account\"]") != NULL); + free(json); + PASS(); +} + /* kebab-case flag names map to snake_case JSON keys. */ TEST(cli_build_args_json_kebab_to_snake_issue680) { char *err = NULL; @@ -15244,6 +15254,7 @@ SUITE(cli) { RUN_TEST(cli_build_args_json_bare_boolean_issue680); RUN_TEST(cli_build_args_json_unknown_flag_rejected); RUN_TEST(cli_build_args_json_repeated_array_issue680); + RUN_TEST(cli_build_args_json_array_literal); RUN_TEST(cli_build_args_json_kebab_to_snake_issue680); RUN_TEST(cli_build_args_json_key_equals_value_issue680); RUN_TEST(cli_build_args_json_bad_positional_errors_issue680);