Skip to content

Commit 2312621

Browse files
authored
feat(install-skills): add --agent parameter to select the agent for which to install them (#8)
1 parent a8ae378 commit 2312621

10 files changed

Lines changed: 742 additions & 13 deletions

README.md

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@ Built for **DevOps engineers**, **Platform engineers**, **Administrators**, and
4545

4646
### Operations
4747

48-
| Command | Description |
49-
|-----------------|---------------------------------------------------------|
50-
| `execute apply` | Apply pending changes |
51-
| `audit list` | List audit entries (snapshot or full history) |
52-
| `audit fix` | Fix a change's audit state (`APPLIED` or `ROLLED_BACK`) |
53-
| `issue list` | List changes with audit issues |
54-
| `issue get` | Get details and resolution guidance for an issue |
48+
| Command | Description |
49+
|------------------|----------------------------------------------------------------|
50+
| `execute apply` | Apply pending changes |
51+
| `audit list` | List audit entries (snapshot or full history) |
52+
| `audit fix` | Fix a change's audit state (`APPLIED` or `ROLLED_BACK`) |
53+
| `issue list` | List changes with audit issues |
54+
| `issue get` | Get details and resolution guidance for an issue |
55+
| `install-skills` | Install official Flamingock AI skills into the current project |
5556

5657
### Global Options
5758

@@ -127,6 +128,56 @@ flamingock audit list --jar ./my-app.jar -J -Xmx1g -- --spring.profiles.active=s
127128

128129
---
129130

131+
## 🧠 Installing AI Skills
132+
133+
Use `install-skills` to copy the official Flamingock skills into the directory for the AI assistant you use in the current project.
134+
135+
### Quick path
136+
137+
```bash
138+
# Default project-local install
139+
flamingock install-skills
140+
141+
# Claude project-local install
142+
flamingock install-skills --agent claude
143+
144+
# GitHub Copilot project-local install
145+
flamingock install-skills --agent github
146+
147+
# Cursor project-local install
148+
flamingock install-skills --agent cursor
149+
150+
# OpenCode project-local install
151+
flamingock install-skills --agent opencode
152+
153+
# Google Gemini project-local install
154+
flamingock install-skills --agent gemini
155+
156+
# Windsurf project-local install
157+
flamingock install-skills --agent windsurf
158+
159+
# PI Agent project-local install
160+
flamingock install-skills --agent pi
161+
```
162+
163+
### Current targets
164+
165+
| Command | Destination |
166+
|----------------------------------------------|----------------------|
167+
| `flamingock install-skills` | `./.agents/skills` |
168+
| `flamingock install-skills --agent claude` | `./.claude/skills` |
169+
| `flamingock install-skills --agent github` | `./.github/skills` |
170+
| `flamingock install-skills --agent cursor` | `./.cursor/skills` |
171+
| `flamingock install-skills --agent opencode` | `./.opencode/skills` |
172+
| `flamingock install-skills --agent gemini` | `./.gemini/skills` |
173+
| `flamingock install-skills --agent windsurf` | `./.windsurf/skills` |
174+
| `flamingock install-skills --agent pi` | `./.pi/skills` |
175+
176+
Note:
177+
- Paths are resolved relative to the directory where you run the command.
178+
179+
---
180+
130181
## 📦 Installation
131182

132183
Choose the option that best fits your platform:

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
group=io.flamingock
2-
version=1.2.0
2+
version=1.2.1

src/main/java/io/flamingock/cli/executor/command/InstallSkillsCommand.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public class InstallSkillsCommand implements Callable<Integer> {
4040
@Option(names = {"-g", "--global"}, description = "Install skills globally (not implemented yet)")
4141
private boolean global;
4242

43+
@Option(names = {"-a", "--agent"},
44+
description = "Target AI assistant: claude, github, cursor, opencode, gemini, windsurf, pi")
45+
private String agent;
46+
4347
private final SkillsInstallationTargetResolver targetResolver;
4448
private final SkillsInstallationPipeline pipeline;
4549
private final Path workingDirectory;
@@ -69,7 +73,7 @@ public InstallSkillsCommand() {
6973
@Override
7074
public Integer call() {
7175
try {
72-
List<SkillsInstallationTarget> targets = targetResolver.resolveTargets(workingDirectory.toAbsolutePath().normalize(), global);
76+
List<SkillsInstallationTarget> targets = targetResolver.resolveTargets(workingDirectory.toAbsolutePath().normalize(), global, agent);
7377
SkillsInstallationResult result = pipeline.install(targets);
7478
ConsoleFormatter.printInfo(buildSuccessMessage(result));
7579
return 0;

src/main/java/io/flamingock/cli/executor/skills/SkillsInstallationPipeline.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ public SkillsInstallationResult install(List<SkillsInstallationTarget> targets)
110110
snapshotRoot,
111111
path -> path.getFileName().toString().startsWith(SKILL_DIRECTORY_PREFIX)
112112
);
113+
for (Path skillDirectory : skillDirectories) {
114+
FileSystemUtils.deleteRecursively(skillDirectory.resolve("evals"));
115+
}
113116
List<String> installedSkills = new ArrayList<>();
114117
for (Path skillDirectory : skillDirectories) {
115118
installedSkills.add(skillDirectory.getFileName().toString());

src/main/java/io/flamingock/cli/executor/skills/SkillsInstallationTarget.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,84 @@ public record SkillsInstallationTarget(String identifier, Path destinationSkills
4040
public static SkillsInstallationTarget local(Path destinationSkillsDir) {
4141
return new SkillsInstallationTarget("local", destinationSkillsDir);
4242
}
43+
44+
/**
45+
* Creates an agent-specific installation target for the <code>agents</code> assistant.
46+
*
47+
* @param destinationSkillsDir agent destination directory
48+
* @return agents installation target
49+
*/
50+
public static SkillsInstallationTarget agents(Path destinationSkillsDir) {
51+
return new SkillsInstallationTarget("local:agents", destinationSkillsDir);
52+
}
53+
54+
/**
55+
* Creates an agent-specific installation target for the <code>claude</code> assistant.
56+
*
57+
* @param destinationSkillsDir claude destination directory
58+
* @return claude installation target
59+
*/
60+
public static SkillsInstallationTarget claude(Path destinationSkillsDir) {
61+
return new SkillsInstallationTarget("local:claude", destinationSkillsDir);
62+
}
63+
64+
/**
65+
* Creates an agent-specific installation target for the <code>github</code> assistant.
66+
*
67+
* @param destinationSkillsDir github destination directory
68+
* @return github installation target
69+
*/
70+
public static SkillsInstallationTarget github(Path destinationSkillsDir) {
71+
return new SkillsInstallationTarget("local:github", destinationSkillsDir);
72+
}
73+
74+
/**
75+
* Creates an agent-specific installation target for the <code>cursor</code> assistant.
76+
*
77+
* @param destinationSkillsDir cursor destination directory
78+
* @return cursor installation target
79+
*/
80+
public static SkillsInstallationTarget cursor(Path destinationSkillsDir) {
81+
return new SkillsInstallationTarget("local:cursor", destinationSkillsDir);
82+
}
83+
84+
/**
85+
* Creates an agent-specific installation target for the <code>opencode</code> assistant.
86+
*
87+
* @param destinationSkillsDir opencode destination directory
88+
* @return opencode installation target
89+
*/
90+
public static SkillsInstallationTarget opencode(Path destinationSkillsDir) {
91+
return new SkillsInstallationTarget("local:opencode", destinationSkillsDir);
92+
}
93+
94+
/**
95+
* Creates an agent-specific installation target for the <code>gemini</code> assistant.
96+
*
97+
* @param destinationSkillsDir gemini destination directory
98+
* @return gemini installation target
99+
*/
100+
public static SkillsInstallationTarget gemini(Path destinationSkillsDir) {
101+
return new SkillsInstallationTarget("local:gemini", destinationSkillsDir);
102+
}
103+
104+
/**
105+
* Creates an agent-specific installation target for the <code>windsurf</code> assistant.
106+
*
107+
* @param destinationSkillsDir windsurf destination directory
108+
* @return windsurf installation target
109+
*/
110+
public static SkillsInstallationTarget windsurf(Path destinationSkillsDir) {
111+
return new SkillsInstallationTarget("local:windsurf", destinationSkillsDir);
112+
}
113+
114+
/**
115+
* Creates an agent-specific installation target for the <code>pi agent</code> assistant.
116+
*
117+
* @param destinationSkillsDir pi agent destination directory
118+
* @return pi agent installation target
119+
*/
120+
public static SkillsInstallationTarget pi(Path destinationSkillsDir) {
121+
return new SkillsInstallationTarget("local:pi", destinationSkillsDir);
122+
}
43123
}

src/main/java/io/flamingock/cli/executor/skills/SkillsInstallationTargetResolver.java

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
public class SkillsInstallationTargetResolver {
2727

2828
private static final String[] LOCAL_SKILLS_PATH = {".agents", "skills"};
29+
private static final String[] CLAUDE_PATH = {".claude", "skills"};
30+
private static final String[] GITHUB_PATH = {".github", "skills"};
31+
private static final String[] CURSOR_PATH = {".cursor", "skills"};
32+
private static final String[] OPENCODE_PATH = {".opencode", "skills"};
33+
private static final String[] GEMINI_PATH = {".gemini", "skills"};
34+
private static final String[] WINDSURF_PATH = {".windsurf", "skills"};
35+
private static final String[] PI_PATH = {".pi", "skills"};
2936
private static final String GLOBAL_MODE_NOT_IMPLEMENTED =
3037
"Global skills installation is not implemented yet. Run 'flamingock install-skills' to install into ./.agents/skills.";
3138

@@ -40,18 +47,65 @@ public SkillsInstallationTargetResolver() {
4047
}
4148

4249
/**
43-
* Resolves the skills installation targets for the requested mode.
50+
* Resolves the skills installation targets for the requested mode (default agent).
4451
*
4552
* @param workingDirectory current command working directory
4653
* @param global whether global mode was requested
4754
* @return resolved installation targets
4855
*/
4956
public List<SkillsInstallationTarget> resolveTargets(Path workingDirectory, boolean global) {
57+
return resolveTargets(workingDirectory, global, null);
58+
}
59+
60+
/**
61+
* Resolves the skills installation targets for the requested mode and agent.
62+
*
63+
* @param workingDirectory current command working directory
64+
* @param global whether global mode was requested
65+
* @param agent target AI assistant identifier (claude, github, cursor, opencode, gemini, windsurf, pi)
66+
* @return resolved installation targets
67+
*/
68+
public List<SkillsInstallationTarget> resolveTargets(Path workingDirectory, boolean global, String agent) {
5069
if (global) {
5170
throw new IllegalStateException(GLOBAL_MODE_NOT_IMPLEMENTED);
5271
}
5372

54-
Path destination = directoryResolver.resolveDirectory(workingDirectory, LOCAL_SKILLS_PATH);
55-
return List.of(SkillsInstallationTarget.local(destination));
73+
if (agent == null) {
74+
Path destination = directoryResolver.resolveDirectory(workingDirectory, LOCAL_SKILLS_PATH);
75+
return List.of(SkillsInstallationTarget.agents(destination));
76+
}
77+
78+
return switch (agent) {
79+
case "claude" -> {
80+
Path destination = directoryResolver.resolveDirectory(workingDirectory, CLAUDE_PATH);
81+
yield List.of(SkillsInstallationTarget.claude(destination));
82+
}
83+
case "github" -> {
84+
Path destination = directoryResolver.resolveDirectory(workingDirectory, GITHUB_PATH);
85+
yield List.of(SkillsInstallationTarget.github(destination));
86+
}
87+
case "cursor" -> {
88+
Path destination = directoryResolver.resolveDirectory(workingDirectory, CURSOR_PATH);
89+
yield List.of(SkillsInstallationTarget.cursor(destination));
90+
}
91+
case "opencode" -> {
92+
Path destination = directoryResolver.resolveDirectory(workingDirectory, OPENCODE_PATH);
93+
yield List.of(SkillsInstallationTarget.opencode(destination));
94+
}
95+
case "gemini" -> {
96+
Path destination = directoryResolver.resolveDirectory(workingDirectory, GEMINI_PATH);
97+
yield List.of(SkillsInstallationTarget.gemini(destination));
98+
}
99+
case "windsurf" -> {
100+
Path destination = directoryResolver.resolveDirectory(workingDirectory, WINDSURF_PATH);
101+
yield List.of(SkillsInstallationTarget.windsurf(destination));
102+
}
103+
case "pi" -> {
104+
Path destination = directoryResolver.resolveDirectory(workingDirectory, PI_PATH);
105+
yield List.of(SkillsInstallationTarget.pi(destination));
106+
}
107+
default -> throw new IllegalStateException(
108+
"Unsupported agent: '" + agent + "'. Supported values: claude, github, cursor, opencode, gemini, windsurf, pi.");
109+
};
56110
}
57111
}

0 commit comments

Comments
 (0)