This is an automated email from the ASF dual-hosted git repository. luigidemasi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-kit.git
commit fdb3a95c630a038d0394d1ad307cac4a203c5288 Author: Jinal Sarvaiya <[email protected]> AuthorDate: Tue Oct 20 15:13:02 2026 +0100 fix #243: address sourcery review --- .../camelkit/generator/ClaudeGenerator.java | 17 ++++++++++ .../camelkit/generator/SkillResourceInstaller.java | 4 +-- .../camelkit/service/DoctorService.java | 2 +- .../camelkit/generator/ClaudeGeneratorTest.java | 37 ++++++++++++++++++---- .../camelkit/service/DoctorServiceTest.java | 15 +++++++++ 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/ClaudeGenerator.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/ClaudeGenerator.java index 4bea2c8f..22cdd921 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/ClaudeGenerator.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/ClaudeGenerator.java @@ -1,5 +1,6 @@ package io.github.luigidemasi.camelkit.generator; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; @@ -13,14 +14,30 @@ public class ClaudeGenerator extends DefaultGenerator { private final QuteTemplateEngine templateEngine = new QuteTemplateEngine(); + private static final String LEGACY_COMMAND_DIR = ".claude/commands"; + @Override public void generate(InitContext ctx) throws Exception { super.generate(ctx); + removeLegacyCommandStubs(ctx); generateClaudeMd(ctx); generateSettings(ctx); generateSubagents(ctx); } + private void removeLegacyCommandStubs(InitContext ctx) throws IOException { + Path legacyDir = ctx.projectDir().resolve(LEGACY_COMMAND_DIR); + if (!Files.isDirectory(legacyDir)) { + return; + } + try (var files = Files.list(legacyDir)) { + for (Path file : files.filter(f -> f.getFileName().toString().startsWith("camel-") + && f.getFileName().toString().endsWith(".md")).toList()) { + GeneratedAssetCleaner.deleteRegularFile(ctx, file); + } + } + } + /** Installs the registry-declared project subagents, currently the read-only Ship worker. */ private void generateSubagents(InitContext ctx) throws Exception { int subagents = 0; diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/SkillResourceInstaller.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/SkillResourceInstaller.java index 3832a8e1..a17b0b4b 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/SkillResourceInstaller.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/generator/SkillResourceInstaller.java @@ -158,8 +158,8 @@ class SkillResourceInstaller { if (AgentGeneratorStrategy.BOB2.descriptorValue().equals(ctx.agentName()) || AgentGeneratorStrategy.QWEN.descriptorValue().equals(ctx.agentName()) || AgentGeneratorStrategy.CLAUDE.descriptorValue().equals(ctx.agentName())) { - // Bob Shell's native skill picker hides false values; same-name command stubs cannot override them. - boolean exposeCommand = AgentGeneratorStrategy.BOB2.descriptorValue().equals(ctx.agentName()) + boolean exposeCommand = (AgentGeneratorStrategy.BOB2.descriptorValue().equals(ctx.agentName()) + || AgentGeneratorStrategy.CLAUDE.descriptorValue().equals(ctx.agentName())) && workflow.commands().stream().anyMatch(command -> command.userFacing() && command.skill().equals(skillName)); addHyphenatedUserInvocableMetadata(destination, exposeCommand); diff --git a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java index 6c30626c..a76807c0 100644 --- a/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java +++ b/camel-kit-core/src/main/java/io/github/luigidemasi/camelkit/service/DoctorService.java @@ -141,7 +141,7 @@ public class DoctorService { AgentConfig agent = AgentRegistry.get(agentName); String folder = config.getProperty("agent.folder", "").trim(); - if (agent.folder().equals(folder)) { + if (agent.folder().equals(folder) || agent.commandDirectory().equals(folder)) { findings.add(DoctorFinding.pass("config", ".camel-kit/config.properties", "Agent configuration selects " + agent.name(), "No action required.")); diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ClaudeGeneratorTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ClaudeGeneratorTest.java index 054e4506..7a3a718d 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ClaudeGeneratorTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/generator/ClaudeGeneratorTest.java @@ -135,13 +135,19 @@ class ClaudeGeneratorTest { InitContext ctx = createContext(); new ClaudeGenerator().generate(ctx); - String start = Files.readString(ctx.skillsDir().resolve("camel-start/SKILL.md")); - assertTrue(start.contains("user-invocable: true"), - "camel-start must have hyphenated user-invocable for Claude Code discovery"); + for (String userFacingSkill : Set.of("camel-start", "camel-brainstorm", "camel-migrate", + "camel-plan", "camel-execute", "camel-validate", "camel-ship", + "camel-knowledge", "camel-debug")) { + String skill = Files.readString(ctx.skillsDir().resolve(userFacingSkill + "/SKILL.md")); + assertTrue(skill.contains("user-invocable: true"), + userFacingSkill + " must have user-invocable: true for Claude Code discovery"); + } - String brainstorm = Files.readString(ctx.skillsDir().resolve("camel-brainstorm/SKILL.md")); - assertTrue(brainstorm.contains("user-invocable: false"), - "camel-brainstorm must have hyphenated user-invocable: false for Claude Code"); + for (String internalSkill : INTERNAL_SKILLS) { + String skill = Files.readString(ctx.skillsDir().resolve(internalSkill + "/SKILL.md")); + assertTrue(skill.contains("user-invocable: false"), + internalSkill + " must have user-invocable: false for Claude Code"); + } } @Test @@ -157,4 +163,23 @@ class ClaudeGeneratorTest { skillName + " description must start with 'Use when' for Claude Code discovery"); } } + + @Test + void removesLegacyCommandStubsOnRegeneration() throws Exception { + InitContext ctx = createContext(); + Path legacyDir = tempDir.resolve(".claude/commands"); + Files.createDirectories(legacyDir); + Files.writeString(legacyDir.resolve("camel-start.md"), "legacy stub"); + Files.writeString(legacyDir.resolve("camel-migrate.md"), "legacy stub"); + Files.writeString(legacyDir.resolve("user-custom.md"), "user file"); + + new ClaudeGenerator().generate(ctx); + + assertFalse(Files.exists(legacyDir.resolve("camel-start.md")), + "Legacy camel-start command stub must be removed"); + assertFalse(Files.exists(legacyDir.resolve("camel-migrate.md")), + "Legacy camel-migrate command stub must be removed"); + assertTrue(Files.exists(legacyDir.resolve("user-custom.md")), + "Non-camel user files must be preserved"); + } } diff --git a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java index 08a1224e..d6964d7a 100644 --- a/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java +++ b/camel-kit-core/src/test/java/io/github/luigidemasi/camelkit/service/DoctorServiceTest.java @@ -1732,6 +1732,21 @@ class DoctorServiceTest { return hasFinding(result, status, category, null, null); } + @Test + void claudeDoctorAcceptsLegacyCommandDirectoryFolder() throws Exception { + Path root = tempDir.resolve("legacy-claude"); + createHealthyWorkspace(root, CLAUDE); + Path config = root.resolve(".camel-kit/config.properties"); + String content = Files.readString(config); + Files.writeString(config, content.replace( + "agent.folder=" + AgentRegistry.get(CLAUDE).folder(), + "agent.folder=.claude/commands")); + + DoctorResult result = new DoctorService().inspect(new DoctorRequest(root)); + assertTrue(hasFinding(result, DoctorFinding.Status.PASS, "config", "Agent configuration selects", null), + "Doctor must accept legacy .claude/commands folder for Claude"); + } + private boolean hasFinding( DoctorResult result, DoctorFinding.Status status,
