/* Tool registry: schema / prompt generation, profiles and /tools switches. */ #include "cJSON.h" #include "cst_agent.h" #include "cst_tools.h" #include #include #include static int nfail; #define EXPECT(cond, msg) \ do { \ if (!(cond)) { \ fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \ nfail++; \ } \ } while (0) static int schema_has(cJSON *arr, const char *name) { const cJSON *t = NULL; cJSON_ArrayForEach(t, arr) { const cJSON *fn = cJSON_GetObjectItemCaseSensitive(t, "function"); const cJSON *nm = cJSON_GetObjectItemCaseSensitive(fn, "name"); if (cJSON_IsString(nm) && strcmp(nm->valuestring, name) == 0) return 1; } return 0; } int main(void) { cst_agent_config_t cfg; memset(&cfg, 0, sizeof(cfg)); /* every tool has a runner and a well-formed parameter list */ EXPECT(cst_tool_count() >= 13, "registry size"); for (int i = 0; i < cst_tool_count(); i++) { const cst_tool_def_t *t = cst_tool_at(i); EXPECT(t->run != NULL, t->name); EXPECT((t->perm == CST_TP_WRITE) == (t->preview != NULL), "write tools (and only those) have previews"); EXPECT(cst_tool_find(t->name) == t, "find by name"); } cJSON *all = cst_tools_schema_cfg(&cfg); EXPECT(cJSON_GetArraySize(all) == cst_tool_count(), "default: every tool"); EXPECT(schema_has(all, "edit_file") && schema_has(all, "grep") && schema_has(all, "web_fetch"), "default schema has new tools"); cJSON_Delete(all); cfg.tool_profile = CST_TOOLS_MINIMAL; cJSON *min = cst_tools_schema_cfg(&cfg); EXPECT(cJSON_GetArraySize(min) == 4, "minimal: four tools"); EXPECT(schema_has(min, "read_file") && schema_has(min, "run_shell") && schema_has(min, "write_file") && schema_has(min, "apply_patch"), "minimal: the original four"); char *ms = cJSON_PrintUnformatted(min); EXPECT(ms && strstr(ms, "\"description\":\"Read a UTF-8 text file under the " "workspace.\""), "minimal keeps the original wording"); EXPECT(ms && !strstr(ms, "offset"), "minimal hides optional params"); free(ms); cJSON_Delete(min); char *pmin = cst_agent_build_prompt(&cfg, CST_PROTO_NATIVE_EOS, NULL); EXPECT(pmin && strcmp(pmin, cst_agent_system_prompt_for( CST_PROTO_NATIVE_EOS)) == 0, "minimal prompt == legacy prompt"); EXPECT(pmin && strstr(pmin, "ls/grep via run_shell"), "legacy workflow"); free(pmin); char *pctl = cst_agent_build_prompt(&cfg, CST_PROTO_CTL, NULL); EXPECT(pctl && !strstr(pctl, "must be quoted"), "minimal ctl: no new rule"); free(pctl); cfg.tool_profile = CST_TOOLS_DEFAULT; char *pdef = cst_agent_build_prompt(&cfg, CST_PROTO_CTL_HEREDOC, "Always run `make test`."); EXPECT(pdef && strstr(pdef, "- edit_file(path, old_string, new_string, " "replace_all?):"), "default prompt lists edit_file with optional marker"); EXPECT(pdef && strstr(pdef, "list_dir, glob, grep"), "default workflow"); EXPECT(pdef && strstr(pdef, "must be quoted"), "ctl numeric rule"); EXPECT(pdef && strstr(pdef, "Always run `make test`."), "project notes"); free(pdef); /* /tools off: grep disappears everywhere */ cfg.tools_off = 1ULL << cst_tool_index("grep"); EXPECT(!cst_tool_active(&cfg, cst_tool_find("grep")), "grep off"); cJSON *off = cst_tools_schema_cfg(&cfg); EXPECT(!schema_has(off, "grep") && schema_has(off, "glob"), "schema off"); cJSON_Delete(off); char *names = cst_tools_names(&cfg); EXPECT(names && !strstr(names, "grep") && strstr(names, "glob"), "names off"); free(names); char *poff = cst_agent_build_prompt(&cfg, CST_PROTO_NATIVE_EOS, NULL); EXPECT(poff && !strstr(poff, "- grep(") && strstr(poff, "list_dir, glob)"), "prompt off"); free(poff); int prof = -1; EXPECT(cst_tool_profile_parse("minimal", &prof) == 0 && prof == CST_TOOLS_MINIMAL, "parse minimal"); EXPECT(cst_tool_profile_parse("bogus", &prof) != 0, "parse bogus"); if (nfail) { fprintf(stderr, "test_registry: %d failure(s)\n", nfail); return 1; } printf("test_registry: OK\n"); return 0; }