fix(cli): classify planned skill installs

Add a skill_dirs_planned array to the install --plan receipt and route planned Claude Code skill directory writes there instead of instruction_files_planned.

This keeps the no-mutation install receipt accurate for agents reviewing planned changes before install. Real install behavior and hook upsert paths are unchanged.

Validation: make -f Makefile.cbm build/c/test-runner; CBM_ONLY_SUITE=cli ./build/c/test-runner; bash scripts/check-source-safety.sh; git diff --check; make -f Makefile.cbm cbm; HOME=/private/tmp/cbm-install-plan-smoke-20260630 ./build/c/codebase-memory-mcp install --plan.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
Andrew Hundt
2026-06-30 05:28:24 -04:00
parent 809b1db87a
commit ec650bb3ce
2 changed files with 16 additions and 1 deletions
+4
View File
@@ -4070,11 +4070,14 @@ char *cbm_build_install_plan_json(const char *home, const char *binary_path) {
yyjson_mut_val *configs = yyjson_mut_arr(doc);
yyjson_mut_val *instrs = yyjson_mut_arr(doc);
yyjson_mut_val *skill_dirs = yyjson_mut_arr(doc);
yyjson_mut_val *hooks = yyjson_mut_arr(doc);
for (int i = 0; i < plan.count; i++) {
cbm_plan_entry_t *e = &plan.items[i];
if (strcmp(e->kind, "mcp_config") == 0) {
yyjson_mut_arr_add_strcpy(doc, configs, e->path);
} else if (strcmp(e->kind, "skills") == 0) {
yyjson_mut_arr_add_strcpy(doc, skill_dirs, e->path);
} else if (strcmp(e->kind, "hook") == 0) {
yyjson_mut_val *h = yyjson_mut_obj(doc);
yyjson_mut_obj_add_strcpy(doc, h, "agent", e->agent);
@@ -4086,6 +4089,7 @@ char *cbm_build_install_plan_json(const char *home, const char *binary_path) {
}
yyjson_mut_obj_add_val(doc, root, "config_files_planned", configs);
yyjson_mut_obj_add_val(doc, root, "instruction_files_planned", instrs);
yyjson_mut_obj_add_val(doc, root, "skill_dirs_planned", skill_dirs);
yyjson_mut_obj_add_val(doc, root, "hooks_planned", hooks);
yyjson_mut_obj_add_bool(doc, root, "writes_started", false);
yyjson_mut_obj_add_bool(doc, root, "network_after_install", false);
+12 -1
View File
@@ -1679,8 +1679,10 @@ TEST(cli_install_plan_receipt_no_mutation_issue388) {
if (!cbm_mkdtemp(tmpdir))
FAIL("cbm_mkdtemp failed");
/* Make Cursor + Codex "detected". */
/* Make Claude Code + Cursor + Codex "detected". */
char dir[512];
snprintf(dir, sizeof(dir), "%s/.claude", tmpdir);
test_mkdirp(dir);
snprintf(dir, sizeof(dir), "%s/.cursor", tmpdir);
test_mkdirp(dir);
snprintf(dir, sizeof(dir), "%s/.codex", tmpdir);
@@ -1692,8 +1694,17 @@ TEST(cli_install_plan_receipt_no_mutation_issue388) {
ASSERT(strstr(json, "writes_started") != NULL);
ASSERT(strstr(json, "next_safe_command") != NULL);
ASSERT(strstr(json, "cursor") != NULL);
ASSERT(strstr(json, "skill_dirs_planned") != NULL);
ASSERT(strstr(json, ".claude/skills") != NULL);
ASSERT(strstr(json, ".cursor/mcp.json") != NULL);
ASSERT(strstr(json, ".codex/config.toml") != NULL);
const char *instrs = strstr(json, "\"instruction_files_planned\"");
ASSERT_NOT_NULL(instrs);
const char *skill_dirs = strstr(json, "\"skill_dirs_planned\"");
ASSERT_NOT_NULL(skill_dirs);
ASSERT(instrs < skill_dirs);
const char *misclassified = strstr(instrs, ".claude/skills");
ASSERT(misclassified == NULL || misclassified > skill_dirs);
free(json);
/* Critical: building the plan must NOT have created any config file. */