Skip to content

Commit 1914285

Browse files
DeusDatagdilla
andcommitted
Consolidate 4 skills into 1 with progressive disclosure
Merge codebase-memory-exploring, codebase-memory-tracing, codebase-memory-quality, codebase-memory-reference into single codebase-memory skill with decision matrix, workflows, and gotchas. Old skill directories are cleaned up automatically during install. Factual corrections vs original PR: trace_call_path -> trace_path, removed non-existent tools (read_file, list_directory) and edge types (OVERRIDE, USAGE, FILE_CHANGES_WITH), removed risk_labels parameter. Co-Authored-By: gdilla <gdilla@users.noreply.github.com>
1 parent 613ce40 commit 1914285

File tree

3 files changed

+107
-129
lines changed

3 files changed

+107
-129
lines changed

src/cli/cli.c

Lines changed: 78 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -376,111 +376,82 @@ int cbm_replace_binary(const char *path, const unsigned char *data, int len, int
376376

377377
/* ── Skill file content (embedded) ────────────────────────────── */
378378

379-
static const char skill_exploring_content[] =
379+
/* Consolidated from 4 separate skills into 1 with progressive disclosure.
380+
* This embedded version is the single source of truth for the CLI installer.
381+
* Based on PR #81 by @gdilla — factual corrections applied. */
382+
static const char skill_content[] =
380383
"---\n"
381-
"name: codebase-memory-exploring\n"
382-
"description: Codebase knowledge graph expert. ALWAYS invoke this skill when the user "
383-
"explores code, searches for functions/classes/routes, asks about architecture, or needs "
384-
"codebase orientation. Do not use Grep, Glob, or file search directly — use "
385-
"codebase-memory-mcp search_graph and get_architecture first.\n"
384+
"name: codebase-memory\n"
385+
"description: Use the codebase knowledge graph for structural code queries. "
386+
"Triggers on: explore the codebase, understand the architecture, what functions exist, "
387+
"show me the structure, who calls this function, what does X call, trace the call chain, "
388+
"find callers of, show dependencies, impact analysis, dead code, unused functions, "
389+
"high fan-out, refactor candidates, code quality audit, graph query syntax, "
390+
"Cypher query examples, edge types, how to use search_graph.\n"
386391
"---\n"
387392
"\n"
388-
"# Codebase Exploration\n"
393+
"# Codebase Memory — Knowledge Graph Tools\n"
389394
"\n"
390-
"Use codebase-memory-mcp tools to explore the codebase:\n"
395+
"Graph tools return precise structural results in ~500 tokens vs ~80K for grep.\n"
391396
"\n"
392-
"## Workflow\n"
393-
"1. `get_graph_schema` — understand what node/edge types exist\n"
394-
"2. `search_graph` — find functions, classes, routes by pattern\n"
395-
"3. `get_code_snippet` — read specific function implementations\n"
396-
"4. `get_architecture` — get high-level project summary\n"
397+
"## Quick Decision Matrix\n"
397398
"\n"
398-
"## Tips\n"
399-
"- Use `search_graph(name_pattern=\".*Pattern.*\")` for fuzzy matching\n"
400-
"- Use `search_graph(label=\"Route\")` to find HTTP routes\n"
401-
"- Use `search_graph(label=\"Function\", file_pattern=\"*.go\")` to scope by language\n";
402-
403-
static const char skill_tracing_content[] =
404-
"---\n"
405-
"name: codebase-memory-tracing\n"
406-
"description: Call chain and dependency expert. ALWAYS invoke this skill when the user "
407-
"asks who calls a function, what a function calls, needs impact analysis, or traces "
408-
"dependencies. Do not grep for function names directly — use codebase-memory-mcp "
409-
"trace_path first.\n"
410-
"---\n"
411-
"\n"
412-
"# Call Tracing & Impact Analysis\n"
413-
"\n"
414-
"Use codebase-memory-mcp tools to trace call paths:\n"
415-
"\n"
416-
"## Workflow\n"
417-
"1. `search_graph(name_pattern=\".*FuncName.*\")` — find exact function name\n"
418-
"2. `trace_path(function_name=\"FuncName\", direction=\"both\")` — trace callers + "
419-
"callees\n"
420-
"3. `detect_changes` — find what changed and assess risk_labels\n"
399+
"| Question | Tool call |\n"
400+
"|----------|----------|\n"
401+
"| Who calls X? | `trace_path(direction=\"inbound\")` |\n"
402+
"| What does X call? | `trace_path(direction=\"outbound\")` |\n"
403+
"| Full call context | `trace_path(direction=\"both\")` |\n"
404+
"| Find by name pattern | `search_graph(name_pattern=\"...\")` |\n"
405+
"| Dead code | `search_graph(max_degree=0, exclude_entry_points=true)` |\n"
406+
"| Cross-service edges | `query_graph` with Cypher |\n"
407+
"| Impact of local changes | `detect_changes()` |\n"
408+
"| Text search | `search_code` or Grep |\n"
421409
"\n"
422-
"## Direction Options\n"
423-
"- `inbound` — who calls this function?\n"
424-
"- `outbound` — what does this function call?\n"
425-
"- `both` — full context (callers + callees)\n";
426-
427-
static const char skill_quality_content[] =
428-
"---\n"
429-
"name: codebase-memory-quality\n"
430-
"description: Code quality analysis expert. ALWAYS invoke this skill when the user asks "
431-
"about dead code, unused functions, complexity, refactor candidates, or cleanup "
432-
"opportunities. Do not search files manually — use codebase-memory-mcp search_graph "
433-
"with degree filters first.\n"
434-
"---\n"
410+
"## Exploration Workflow\n"
411+
"1. `list_projects` — check if project is indexed\n"
412+
"2. `get_graph_schema` — understand node/edge types\n"
413+
"3. `search_graph(label=\"Function\", name_pattern=\".*Pattern.*\")` — find code\n"
414+
"4. `get_code_snippet(qualified_name=\"project.path.FuncName\")` — read source\n"
435415
"\n"
436-
"# Code Quality Analysis\n"
416+
"## Tracing Workflow\n"
417+
"1. `search_graph(name_pattern=\".*FuncName.*\")` — discover exact name\n"
418+
"2. `trace_path(function_name=\"FuncName\", direction=\"both\", depth=3)` — trace\n"
419+
"3. `detect_changes()` — map git diff to affected symbols\n"
437420
"\n"
438-
"Use codebase-memory-mcp tools for quality analysis:\n"
421+
"## Quality Analysis\n"
422+
"- Dead code: `search_graph(max_degree=0, exclude_entry_points=true)`\n"
423+
"- High fan-out: `search_graph(min_degree=10, relationship=\"CALLS\", "
424+
"direction=\"outbound\")`\n"
425+
"- High fan-in: `search_graph(min_degree=10, relationship=\"CALLS\", "
426+
"direction=\"inbound\")`\n"
439427
"\n"
440-
"## Dead Code Detection\n"
441-
"- `search_graph(max_degree=0, exclude_entry_points=true)` — find unreferenced functions\n"
442-
"- `search_graph(max_degree=0, label=\"Function\")` — unreferenced functions only\n"
443-
"\n"
444-
"## Complexity Analysis\n"
445-
"- `search_graph(min_degree=10)` — high fan-out functions\n"
446-
"- `search_graph(label=\"Function\", sort_by=\"degree\")` — most-connected functions\n";
447-
448-
static const char skill_reference_content[] =
449-
"---\n"
450-
"name: codebase-memory-reference\n"
451-
"description: Codebase-memory-mcp reference guide. ALWAYS invoke this skill when the user "
452-
"asks about MCP tools, graph queries, Cypher syntax, edge types, or how to use the "
453-
"knowledge graph. Do not guess tool parameters — load this reference first.\n"
454-
"---\n"
455-
"\n"
456-
"# Codebase Memory MCP Reference\n"
457-
"\n"
458-
"## 14 total MCP Tools\n"
459-
"- `index_repository` — index a project\n"
460-
"- `index_status` — check indexing progress\n"
461-
"- `detect_changes` — find what changed since last index\n"
462-
"- `search_graph` — find nodes by pattern\n"
463-
"- `search_code` — text search in source\n"
464-
"- `query_graph` — Cypher query language\n"
465-
"- `trace_path` — call chain traversal\n"
466-
"- `get_code_snippet` — read function source\n"
467-
"- `get_graph_schema` — node/edge type catalog\n"
468-
"- `get_architecture` — high-level summary\n"
469-
"- `list_projects` — indexed projects\n"
470-
"- `delete_project` — remove a project\n"
471-
"- `manage_adr` — architecture decision records\n"
472-
"- `ingest_traces` — import runtime traces\n"
428+
"## 14 MCP Tools\n"
429+
"`index_repository`, `index_status`, `list_projects`, `delete_project`,\n"
430+
"`search_graph`, `search_code`, `trace_path`, `detect_changes`,\n"
431+
"`query_graph`, `get_graph_schema`, `get_code_snippet`, `get_architecture`,\n"
432+
"`manage_adr`, `ingest_traces`\n"
473433
"\n"
474434
"## Edge Types\n"
475435
"CALLS, HTTP_CALLS, ASYNC_CALLS, IMPORTS, DEFINES, DEFINES_METHOD,\n"
476436
"HANDLES, IMPLEMENTS, CONTAINS_FILE, CONTAINS_FOLDER, CONTAINS_PACKAGE\n"
477437
"\n"
478-
"## Cypher Examples\n"
438+
"## Cypher Examples (for query_graph)\n"
479439
"```\n"
440+
"MATCH (a)-[r:HTTP_CALLS]->(b) RETURN a.name, b.name, r.url_path, "
441+
"r.confidence LIMIT 20\n"
480442
"MATCH (f:Function) WHERE f.name =~ '.*Handler.*' RETURN f.name, f.file_path\n"
481443
"MATCH (a)-[r:CALLS]->(b) WHERE a.name = 'main' RETURN b.name\n"
482-
"MATCH (a)-[r:HTTP_CALLS]->(b) RETURN a.name, b.name, r.url_path\n"
483-
"```\n";
444+
"```\n"
445+
"\n"
446+
"## Gotchas\n"
447+
"1. `search_graph(relationship=\"HTTP_CALLS\")` filters nodes by degree — "
448+
"use `query_graph` with Cypher to see actual edges.\n"
449+
"2. `query_graph` has a 200-row cap — use `search_graph` with degree filters "
450+
"for counting.\n"
451+
"3. `trace_path` needs exact names — use `search_graph(name_pattern=...)` first.\n"
452+
"4. `direction=\"outbound\"` misses cross-service callers — use "
453+
"`direction=\"both\"`.\n"
454+
"5. Results default to 10 per page — check `has_more` and use `offset`.\n";
484455

485456
static const char codex_instructions_content[] =
486457
"# Codebase Knowledge Graph\n"
@@ -496,11 +467,17 @@ static const char codex_instructions_content[] =
496467
"\n"
497468
"Always prefer graph tools over grep for code discovery.\n";
498469

470+
/* Old skill names — cleaned up during install to remove stale directories. */
471+
static const char *old_skill_names[] = {
472+
"codebase-memory-exploring",
473+
"codebase-memory-tracing",
474+
"codebase-memory-quality",
475+
"codebase-memory-reference",
476+
};
477+
enum { OLD_SKILL_COUNT = 4 };
478+
499479
static const cbm_skill_t skills[CBM_SKILL_COUNT] = {
500-
{"codebase-memory-exploring", skill_exploring_content},
501-
{"codebase-memory-tracing", skill_tracing_content},
502-
{"codebase-memory-quality", skill_quality_content},
503-
{"codebase-memory-reference", skill_reference_content},
480+
{"codebase-memory", skill_content},
504481
};
505482

506483
const cbm_skill_t *cbm_get_skills(void) {
@@ -577,6 +554,16 @@ int cbm_install_skills(const char *skills_dir, bool force, bool dry_run) {
577554
}
578555
int count = 0;
579556

557+
/* Clean up old 4-skill directories (consolidated into 1). */
558+
for (int i = 0; i < OLD_SKILL_COUNT; i++) {
559+
char old_path[CLI_BUF_1K];
560+
snprintf(old_path, sizeof(old_path), "%s/%s", skills_dir, old_skill_names[i]);
561+
struct stat st;
562+
if (stat(old_path, &st) == 0 && S_ISDIR(st.st_mode) && !dry_run) {
563+
rmdir_recursive(old_path);
564+
}
565+
}
566+
580567
for (int i = 0; i < CBM_SKILL_COUNT; i++) {
581568
char skill_path[CLI_BUF_1K];
582569
snprintf(skill_path, sizeof(skill_path), "%s/%s", skills_dir, skills[i].name);

src/cli/cli.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ int cbm_replace_binary(const char *path, const unsigned char *data, int len, int
5353
/* ── Skill file management ────────────────────────────────────── */
5454

5555
/* Number of skill files. */
56-
#define CBM_SKILL_COUNT 4
56+
#define CBM_SKILL_COUNT 1
5757

5858
/* Skill name/content pair. */
5959
typedef struct {
60-
const char *name; /* e.g. "codebase-memory-exploring" */
60+
const char *name; /* e.g. "codebase-memory" */
6161
const char *content; /* full SKILL.md content */
6262
} cbm_skill_t;
6363

tests/test_cli.c

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -547,41 +547,32 @@ TEST(cli_remove_old_monolithic_skill) {
547547
}
548548

549549
TEST(cli_skill_files_content) {
550-
/* Port of TestSkillFilesContent */
550+
/* Consolidated skill: all 4 former skills merged into one. */
551551
const cbm_skill_t *sk = cbm_get_skills();
552-
ASSERT_EQ(CBM_SKILL_COUNT, 4);
552+
ASSERT_EQ(CBM_SKILL_COUNT, 1);
553+
ASSERT(strcmp(sk[0].name, "codebase-memory") == 0);
554+
555+
/* Exploring capabilities */
556+
ASSERT(strstr(sk[0].content, "search_graph") != NULL);
557+
ASSERT(strstr(sk[0].content, "get_graph_schema") != NULL);
558+
559+
/* Tracing capabilities */
560+
ASSERT(strstr(sk[0].content, "trace_path") != NULL);
561+
ASSERT(strstr(sk[0].content, "direction") != NULL);
562+
ASSERT(strstr(sk[0].content, "detect_changes") != NULL);
563+
564+
/* Quality capabilities */
565+
ASSERT(strstr(sk[0].content, "max_degree=0") != NULL);
566+
ASSERT(strstr(sk[0].content, "exclude_entry_points") != NULL);
567+
568+
/* Reference capabilities */
569+
ASSERT(strstr(sk[0].content, "query_graph") != NULL);
570+
ASSERT(strstr(sk[0].content, "Cypher") != NULL);
571+
ASSERT(strstr(sk[0].content, "14 MCP Tools") != NULL);
572+
573+
/* Gotchas section */
574+
ASSERT(strstr(sk[0].content, "Gotchas") != NULL);
553575

554-
/* Check exploring skill */
555-
bool found_exploring = false, found_tracing = false;
556-
bool found_quality = false, found_reference = false;
557-
for (int i = 0; i < CBM_SKILL_COUNT; i++) {
558-
if (strcmp(sk[i].name, "codebase-memory-exploring") == 0) {
559-
found_exploring = true;
560-
ASSERT(strstr(sk[i].content, "search_graph") != NULL);
561-
ASSERT(strstr(sk[i].content, "get_graph_schema") != NULL);
562-
}
563-
if (strcmp(sk[i].name, "codebase-memory-tracing") == 0) {
564-
found_tracing = true;
565-
ASSERT(strstr(sk[i].content, "trace_path") != NULL);
566-
ASSERT(strstr(sk[i].content, "direction") != NULL);
567-
ASSERT(strstr(sk[i].content, "detect_changes") != NULL);
568-
}
569-
if (strcmp(sk[i].name, "codebase-memory-quality") == 0) {
570-
found_quality = true;
571-
ASSERT(strstr(sk[i].content, "max_degree=0") != NULL);
572-
ASSERT(strstr(sk[i].content, "exclude_entry_points") != NULL);
573-
}
574-
if (strcmp(sk[i].name, "codebase-memory-reference") == 0) {
575-
found_reference = true;
576-
ASSERT(strstr(sk[i].content, "query_graph") != NULL);
577-
ASSERT(strstr(sk[i].content, "Cypher") != NULL);
578-
ASSERT(strstr(sk[i].content, "14 total") != NULL);
579-
}
580-
}
581-
ASSERT_TRUE(found_exploring);
582-
ASSERT_TRUE(found_tracing);
583-
ASSERT_TRUE(found_quality);
584-
ASSERT_TRUE(found_reference);
585576
PASS();
586577
}
587578

@@ -2101,11 +2092,11 @@ TEST(cli_remove_gemini_hooks) {
21012092
* ═══════════════════════════════════════════════════════════════════ */
21022093

21032094
TEST(cli_skill_descriptions_directive) {
2104-
/* Verify all skill descriptions use directive pattern (ALWAYS invoke) */
2095+
/* Verify skill description has trigger phrases for agent matching */
21052096
const cbm_skill_t *sk = cbm_get_skills();
21062097
for (int i = 0; i < CBM_SKILL_COUNT; i++) {
2107-
ASSERT(strstr(sk[i].content, "ALWAYS") != NULL);
2108-
ASSERT(strstr(sk[i].content, "Do not") != NULL);
2098+
ASSERT(strstr(sk[i].content, "Triggers on:") != NULL);
2099+
ASSERT(strstr(sk[i].content, "search_graph") != NULL);
21092100
}
21102101
PASS();
21112102
}

0 commit comments

Comments
 (0)