-
Notifications
You must be signed in to change notification settings - Fork 8
Make ig ai-config agent-agnostic instead of hard-coding .claude/skills
#1664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
52cd22f
2c18475
c67709e
d4478a9
5784c4a
39eff3d
6c5d717
14e062d
026fb9e
3669211
f2f39a9
1379243
4fb65da
baee30a
56ccdf8
96f010b
9ca5b16
100f1e4
fd28dde
f2656af
62a33a4
9d5d801
d30a1cd
a892c47
ae4b10b
ee143b8
dd7dff4
e9f9424
2b5318e
bfc819f
713f181
af0430a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { addMcpServers, copyAISkillsToProject, GoogleAnalytics, Util, VS_CODE_MCP_PATH } from "@igniteui/cli-core"; | ||
| import { addMcpServers, AI_AGENT_LABELS, AI_AGENT_SKILLS_DIRS, AIAgentTarget, copyAgentInstructionFiles, copyAISkillsToProject, getSkillsDir, GoogleAnalytics, InquirerWrapper, Util, VS_CODE_MCP_PATH } from "@igniteui/cli-core"; | ||
| import { ArgumentsCamelCase, CommandModule } from "yargs"; | ||
|
|
||
| export function configureMCP(): void { | ||
|
|
@@ -11,8 +11,14 @@ export function configureMCP(): void { | |
| Util.log(Util.greenCheck() + ` MCP servers configured in ${VS_CODE_MCP_PATH}`); | ||
| } | ||
|
|
||
| export function configureSkills(): void { | ||
| const result = copyAISkillsToProject(); | ||
| export function configureSkills(agents: AIAgentTarget[]): void { | ||
| for (const agent of agents) { | ||
| configureSkillsForAgent(getSkillsDir(agent)); | ||
| } | ||
| } | ||
|
|
||
| function configureSkillsForAgent(skillsDir: string): void { | ||
| const result = copyAISkillsToProject(skillsDir); | ||
| if (result.found === 0) { | ||
| Util.warn("No AI skill files found. Make sure packages are installed (npm install) " + | ||
| "and your Ignite UI packages are up-to-date.", "yellow"); | ||
|
|
@@ -26,18 +32,61 @@ export function configureSkills(): void { | |
| } | ||
| } | ||
|
|
||
| export function configure(skills = true): void { | ||
| export async function configure(agents?: AIAgentTarget[], skills = true): Promise<void> { | ||
| if (!agents?.length) { | ||
| agents = await promptForAgents(); | ||
| } | ||
| if (!agents.length) return; | ||
| configureMCP(); | ||
| if (skills) { | ||
| configureSkills(); | ||
| configureSkills(agents); | ||
| } | ||
| copyAgentInstructionFiles(agents); | ||
| } | ||
|
|
||
| const AI_AGENT_CHOICES = Object.keys(AI_AGENT_SKILLS_DIRS) as AIAgentTarget[]; | ||
|
|
||
| const AI_AGENT_CHECKBOX_CHOICES = [ | ||
| { value: "none", name: "None (skip AI configuration)" }, | ||
| ...AI_AGENT_CHOICES.map(agent => ({ | ||
| value: agent, | ||
| name: AI_AGENT_LABELS[agent], | ||
| checked: agent === "generic" || agent === "claude" | ||
| })) | ||
| ]; | ||
|
|
||
| export async function promptForAgents(): Promise<AIAgentTarget[]> { | ||
| const selected = await InquirerWrapper.checkbox({ | ||
| message: "Which AI tools do you want to generate configuration files for?", | ||
| required: true, | ||
| choices: AI_AGENT_CHECKBOX_CHOICES | ||
| }); | ||
| return selected.includes("none") ? [] : selected as AIAgentTarget[]; | ||
| } | ||
|
Comment on lines
+47
to
65
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a bit iffy on prompt logic here, in the past it was concentrated behind the prompt class explicitly to contain the prompting solution behind a single facade if you will. The wrapper might be enough good enough for now, but need to see the rest. |
||
|
|
||
| const command: CommandModule = { | ||
| command: "ai-config", | ||
| describe: "Configures Ignite UI AI tooling (MCP servers and AI coding skills)", | ||
| builder: (yargs) => yargs, | ||
| async handler(_argv: ArgumentsCamelCase) { | ||
| builder: (yargs) => yargs | ||
| .usage("") | ||
| .option("agent", { | ||
| alias: "a", | ||
| describe: "AI agent(s) to configure skills for (determines the target skills directory)", | ||
| choices: AI_AGENT_CHOICES, | ||
| type: "array" | ||
| }), | ||
|
Comment on lines
+72
to
+77
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we roll that option into one thing? Kinda odd to be able to pass both - like |
||
| async handler(argv: ArgumentsCamelCase) { | ||
| let agents = argv.agent as AIAgentTarget[] | undefined; | ||
|
|
||
| if (!agents?.length) { | ||
| agents = await promptForAgents(); | ||
| } | ||
|
|
||
| if (!agents.length) { | ||
| Util.log("No AI configuration selected. Skipping."); | ||
| return; | ||
| } | ||
|
Comment on lines
+85
to
+88
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Erm, is there a case where there is no selection after the prompt? If there's a |
||
|
|
||
| GoogleAnalytics.post({ | ||
| t: "screenview", | ||
| cd: "MCP" | ||
|
|
@@ -46,10 +95,10 @@ const command: CommandModule = { | |
| GoogleAnalytics.post({ | ||
| t: "event", | ||
| ec: "$ig ai-config", | ||
| ea: "client: vscode" | ||
| ea: `agent: ${agents.join(", ")}` | ||
| }); | ||
|
|
||
| configure(); | ||
| await configure(agents); | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,12 @@ | ||
| import { GoogleAnalytics, PackageManager, ProjectConfig, ProjectLibrary, Util } from "@igniteui/cli-core"; | ||
| import { AI_AGENT_SKILLS_DIRS, AIAgentTarget, GoogleAnalytics, PackageManager, ProjectConfig, ProjectLibrary, Util } from "@igniteui/cli-core"; | ||
| import * as path from "path"; | ||
| import { PromptSession } from "./../PromptSession"; | ||
| import { NewCommandType, PositionalArgs } from "./types"; | ||
| import { TemplateManager } from "../TemplateManager"; | ||
| import { ArgumentsCamelCase, Choices } from "yargs"; | ||
| import { configure } from "./ai-config"; | ||
|
|
||
| const AI_AGENT_CHOICES = Object.keys(AI_AGENT_SKILLS_DIRS) as AIAgentTarget[]; | ||
|
|
||
| // explicit typing because `type: "string"` will be inferred as `type: string` which yargs will not like | ||
| const _framework: { | ||
|
|
@@ -59,6 +62,12 @@ const command: NewCommandType = { | |
| describe: "Project template", | ||
| type: "string" | ||
| }) | ||
| .option("agent", { | ||
| alias: "a", | ||
| describe: "AI agent(s) to configure skills for (determines the target skills directory)", | ||
| choices: AI_AGENT_CHOICES, | ||
| type: "array" | ||
| }) | ||
|
Comment on lines
+65
to
+70
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Depending on the behavior we want, might set defaults here too
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't we agree that if no agent is provided as an argument we prefer to prompt the user? |
||
| .example("$0 new my-app", "Scaffold a new project interactively") | ||
| .example("$0 new my-app -f angular -t igx-ts", "Scaffold an Ignite UI for Angular project"); | ||
| }, | ||
|
|
@@ -152,16 +161,20 @@ const command: NewCommandType = { | |
|
|
||
| Util.log(Util.greenCheck() + " Project Created"); | ||
|
|
||
| if (!argv["skip-git"] && !ProjectConfig.getConfig().skipGit) { | ||
| Util.gitInit(process.cwd(), argv.name); | ||
| } | ||
|
|
||
| if (!argv.skipInstall) { | ||
| process.chdir(argv.name); | ||
| await PackageManager.installPackages(); | ||
| process.chdir(".."); | ||
| } | ||
|
|
||
| process.chdir(argv.name); | ||
| await configure(argv.agent as AIAgentTarget[] | undefined); | ||
| process.chdir(".."); | ||
|
|
||
| if (!argv["skip-git"] && !ProjectConfig.getConfig().skipGit) { | ||
| Util.gitInit(process.cwd(), argv.name); | ||
| } | ||
|
|
||
| Util.log(""); | ||
| Util.log("Next Steps:"); | ||
| Util.log(` cd ${argv.name}`); | ||
|
|
||
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import { ControlExtraConfiguration, defaultDelimiters, ProjectTemplate } from "@igniteui/cli-core"; | ||
| import * as path from "path"; | ||
|
|
||
| // currently reusing hidden project impl as components/views pipeline go through registerInProject | ||
| // ideally would define a separate type/category for those partial files | ||
| export class IgrTsAiConfigPartial implements ProjectTemplate { | ||
| public id: string = "ai-config"; | ||
| public name = "ai-config"; | ||
| public description = "Ignite UI CLI AI config for React partial project files"; | ||
| public framework: string = "react"; | ||
| public projectType: string = "tsx"; | ||
| public dependencies: string[] = []; | ||
| public hasExtraConfiguration: boolean = false; | ||
| public isHidden: boolean = true; | ||
| public delimiters = defaultDelimiters; | ||
|
|
||
| public get templatePaths(): string[] { | ||
| return [path.join(__dirname, "files")]; | ||
| } | ||
|
|
||
| public generateConfig(_name: string, _theme: string, ..._options: any[]): {[key: string]: any} { | ||
| return { /* partials not using Util.processTemplates atm */ }; | ||
| } | ||
|
|
||
| public installModules(): void { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| public async upgradeIgniteUIPackages(_projectPath: string, _packagePath: string): Promise<boolean> { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| public getExtraConfiguration(): ControlExtraConfiguration[] { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| public setExtraConfiguration(_extraConfigKeys: {}) { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| } | ||
| export default new IgrTsAiConfigPartial(); |
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import { ControlExtraConfiguration, defaultDelimiters, ProjectTemplate } from "@igniteui/cli-core"; | ||
| import * as path from "path"; | ||
|
|
||
| // currently reusing hidden project impl as components/views pipeline go through registerInProject | ||
| // ideally would define a separate type/category for those partial files | ||
| export class IgcTsAiConfigPartial implements ProjectTemplate { | ||
|
|
||
| public id: string = "ai-config"; | ||
| public name = "ai-config"; | ||
| public description = "Ignite UI CLI AI config for Web Components partial project files"; | ||
| public framework: string = "webcomponents"; | ||
| public projectType: string = "igc-ts"; | ||
| public dependencies: string[]; | ||
| public hasExtraConfiguration: boolean = false; | ||
| public isHidden: boolean = true; | ||
| public delimiters = defaultDelimiters; | ||
|
|
||
| public get templatePaths(): string[] { | ||
| return [path.join(__dirname, "files")]; | ||
| } | ||
|
|
||
| installModules(): void { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| upgradeIgniteUIPackages(_projectPath: string, _packagePath: string): Promise<boolean> { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| generateConfig(_name: string, _theme: string, ..._options: any[]): { [key: string]: any; } { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| getExtraConfiguration(): ControlExtraConfiguration[] { | ||
| throw new Error("Method not implemented."); | ||
| } | ||
| setExtraConfiguration(_extraConfigKeys: {}) { | ||
| throw new Error("Method not implemented."); | ||
|
Comment on lines
+22
to
+35
|
||
| } | ||
| } | ||
| export default new IgcTsAiConfigPartial(); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was the point of the reusable
configureAI()method - to call that one as an entry for the AI config. Would you like to move that logic there instead of replacing it withconfigureMCP()which this calls too.Yes, you might need to change its parameters if you want the prompt external, but it can be in the method too. And yes, that last part might mean the method is not placed in the correct place in the
super. Also thestartoverride should be removed lol, it's overridden just for the Angular message lol.