Skip to content

Commit 74358cb

Browse files
damyanpetevCopilot
andauthored
fix(ai-config): force skills copy read to always use physical FS (#1656)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ea289b1 commit 74358cb

4 files changed

Lines changed: 379 additions & 355 deletions

File tree

.vscode/launch.json

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -355,22 +355,14 @@
355355
"type": "node",
356356

357357
// "cwd": "<absolute directory of the project, created with ng cli, on which the 'ng' schematic is applied>"
358-
"cwd": "C:\\Users\\User\\Desktop\\ng_test\\test_proj",
358+
"cwd": "${workspaceFolder}/output/test_proj",
359+
"program": "${env:AppData}/npm/node_modules/@angular/cli/bin/ng",
360+
"preLaunchTask": "build",
359361
"args": [
360-
"-r",
361-
362-
// you need to install ts-node for the test project
363-
"ts-node/register",
364-
365-
// "<path/to/ng>", "g",
366-
"${env:AppData}/npm/node_modules/@angular/cli/bin/ng", "g",
367-
362+
"g",
368363
// "<../../relative/path/from/cwd/to>/igniteui-cli/packages/ng-schematics/src/collection.json:cli-config"
369-
"../../../../../work/git/igniteui-cli/packages/ng-schematics/src/collection.json:cli-config"
370-
],
371-
"env": {
372-
"TS_NODE_PROJECT": "${workspaceFolder}/packages/ng-schematics/tsconfig.json"
373-
}
364+
"../../packages/ng-schematics/src/collection.json:cli-config"
365+
]
374366
},
375367
{
376368
// in order to test schematics, you need to have ignitui-angular package already installed in the test project

packages/core/util/ai-skills.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { FS_TOKEN, IFileSystem } from "../types/FileSystem";
44
import { NPM_ANGULAR, NPM_REACT, NPM_WEBCOMPONENTS, resolvePackage, UPGRADEABLE_PACKAGES } from "../update/package-resolve";
55
import { App } from "./App";
66
import { detectFrameworkFromPackageJson } from "./detect-framework";
7+
import { FsFileSystem } from "./FileSystem";
78
import { TEMPLATE_MANAGER } from "./GlobalConstants";
89
import { ProjectConfig } from "./ProjectConfig";
910
import { Util } from "./Util";
@@ -73,11 +74,14 @@ function resolveSkillsRoots(): string[] {
7374

7475
/**
7576
* Copies skill files from the installed Ignite UI package(s) into .claude/skills/.
76-
* Works with both real FS (CLI) and virtual Tree FS (schematics) through IFileSystem.
7777
*/
7878
export function copyAISkillsToProject(): AISkillsCopyResult {
7979
const result: AISkillsCopyResult = { found: 0, skipped: 0, failed: 0 };
80-
const fs = App.container.get<IFileSystem>(FS_TOKEN);
80+
// Source reads (glob + readFile) always use physical FS - skill files can
81+
// come from sources outside the project virtual tree (external/global package):
82+
const srcFs = new FsFileSystem();
83+
// Destination writes respect the App FS (which may be virtual):
84+
const destFs = App.container.get<IFileSystem>(FS_TOKEN);
8185
const skillsRoots = resolveSkillsRoots();
8286

8387
if (!skillsRoots.length) {
@@ -87,7 +91,7 @@ export function copyAISkillsToProject(): AISkillsCopyResult {
8791
const multiRoot = skillsRoots.length > 1;
8892

8993
for (const skillsRoot of skillsRoots) {
90-
const rawPaths = fs.glob(skillsRoot, "**/*");
94+
const rawPaths = srcFs.glob(skillsRoot, "**/*");
9195
const pkgDirName = multiRoot ? path.basename(path.dirname(skillsRoot)) : "";
9296

9397
for (const p of rawPaths) {
@@ -101,18 +105,18 @@ export function copyAISkillsToProject(): AISkillsCopyResult {
101105
? `${CLAUDE_SKILLS_DIR}/${pkgDirName}/${rel}`
102106
: `${CLAUDE_SKILLS_DIR}/${rel}`;
103107

104-
const newContent = fs.readFile(p);
108+
const newContent = srcFs.readFile(p);
105109
try {
106-
if (fs.fileExists(dest)) {
107-
const existingContent = fs.readFile(dest);
110+
if (destFs.fileExists(dest)) {
111+
const existingContent = destFs.readFile(dest);
108112
if (existingContent === newContent) {
109113
result.skipped++;
110114
continue;
111115
}
112-
fs.writeFile(dest, newContent);
116+
destFs.writeFile(dest, newContent);
113117
Util.log(`${Util.greenCheck()} Updated ${dest}`);
114118
} else {
115-
fs.writeFile(dest, newContent);
119+
destFs.writeFile(dest, newContent);
116120
Util.log(`${Util.greenCheck()} Created ${dest}`);
117121
}
118122
} catch {

spec/unit/ai-config-spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as path from "path";
2-
import { App, Config, FS_TOKEN, GoogleAnalytics, IFileSystem, ProjectConfig, TEMPLATE_MANAGER, Util } from "@igniteui/cli-core";
2+
import { App, Config, FS_TOKEN, FsFileSystem, GoogleAnalytics, IFileSystem, ProjectConfig, TEMPLATE_MANAGER, Util } from "@igniteui/cli-core";
33
import { configureMCP, configureSkills } from "../../packages/cli/lib/commands/ai-config";
44
import * as aiConfig from "../../packages/cli/lib/commands/ai-config";
55

@@ -180,6 +180,11 @@ describe("Unit - ai-config command", () => {
180180
} as unknown as IFileSystem;
181181

182182
spyOn(App.container, "get").and.returnValue(mockFs);
183+
// srcFs reads (FsFileSystem.prototype) for source content
184+
spyOn(FsFileSystem.prototype, "glob").and.callFake((d: string) =>
185+
d === angularSkillsDir ? [skillFile] : []
186+
);
187+
spyOn(FsFileSystem.prototype, "readFile").and.returnValue("skill content");
183188
setupAngularConfig();
184189

185190
configureSkills();
@@ -207,6 +212,11 @@ describe("Unit - ai-config command", () => {
207212
} as unknown as IFileSystem;
208213

209214
spyOn(App.container, "get").and.returnValue(mockFs);
215+
// srcFs reads (FsFileSystem.prototype) for source content
216+
spyOn(FsFileSystem.prototype, "glob").and.callFake((d: string) =>
217+
d === angularSkillsDir ? [skillFile] : []
218+
);
219+
spyOn(FsFileSystem.prototype, "readFile").and.returnValue(content);
210220
setupAngularConfig();
211221

212222
configureSkills();
@@ -232,6 +242,11 @@ describe("Unit - ai-config command", () => {
232242
} as unknown as IFileSystem;
233243

234244
spyOn(App.container, "get").and.returnValue(mockFs);
245+
// srcFs reads (FsFileSystem.prototype) for source content
246+
spyOn(FsFileSystem.prototype, "glob").and.callFake((d: string) =>
247+
d === angularSkillsDir ? [skillFile] : []
248+
);
249+
spyOn(FsFileSystem.prototype, "readFile").and.returnValue("skill content");
235250
setupAngularConfig();
236251

237252
configureSkills();

0 commit comments

Comments
 (0)