v1.91.1.0 fix: harden Impeccable plugin discovery (#2978)

* fix(design-detect): find impeccable installed as a Claude Code plugin

The design-detector probe only ever checked <root>/<SKILL_ROOTS>/skills/impeccable/,
never the Claude Code plugin-cache layout
(<root>/.claude/plugins/cache/<marketplace>/<plugin>/<version>/skills/impeccable/).
A plugin-installed impeccable was therefore invisible: IMPECCABLE_SKILL stayed
absent, the launcher was never found (so the NOT_CACHED run hint never fired),
and its sibling engine was never considered.

Add a plugin-cache walk alongside the existing SKILL_ROOTS walk, sharing the
same presence/launcher/repo-local-exclusion/sibling-engine logic via an
extracted checkSkillDir() helper so both paths stay behaviorally identical.

Fixes #2838

* refactor(design-detect): consolidate newestSemverDir onto safeReaddir

Both did the identical try/catch-around-readdirSync; newestSemverDir now
reuses the new safeReaddir helper instead of duplicating it.

* fix: harden Impeccable plugin discovery and regression fixtures

* test: supply eval mode to the integrated detector callback adapter

---------

Co-authored-by: Som Samantray <som.samantray@gmail.com>
This commit is contained in:
Garry Tan
2026-09-25 14:32:12 -04:00
committed by GitHub
co-authored by Som Samantray
parent 7b534d3e90
commit 2a113ae7e6
18 changed files with 938 additions and 40 deletions
+14
View File
@@ -28,6 +28,17 @@ function makeTestHome(): string {
return mkdtempSync(join(tmpdir(), "gstack-memory-ingest-"));
}
function isolateGitRemote(repo: string, url: string): void {
const git = (...args: string[]) => {
const result = spawnSync("git", args, { cwd: repo, encoding: "utf8", timeout: 10_000 });
expect(result.status).toBe(0);
return result.stdout.trim();
};
expect(git("config", "--get", "remote.origin.url")).toBe(url);
git("config", "--local", `url.${url}.insteadOf`, url);
expect(git("remote", "get-url", "origin")).toBe(url);
}
function runScript(args: string[], env: Record<string, string> = {}): { stdout: string; stderr: string; exitCode: number } {
const result = spawnSync("bun", [SCRIPT, ...args], {
encoding: "utf-8",
@@ -918,6 +929,7 @@ describe("#2394: probe applies the same attribution gate as prepare", () => {
mkdirSync(repo, { recursive: true });
spawnSync("git", ["-C", repo, "init", "-q"], { encoding: "utf-8", timeout: 30_000 });
spawnSync("git", ["-C", repo, "remote", "add", "origin", "https://github.com/foo/bar.git"], { encoding: "utf-8", timeout: 30_000 });
isolateGitRemote(repo, "https://github.com/foo/bar.git");
return repo;
}
@@ -998,6 +1010,7 @@ describe("#2394: probe applies the same attribution gate as prepare", () => {
mkdirSync(attributableCwd, { recursive: true });
spawnSync("git", ["-C", attributableCwd, "init", "-q"], { encoding: "utf-8", timeout: 30_000 });
spawnSync("git", ["-C", attributableCwd, "remote", "add", "origin", "https://github.com/foo/bar.git"], { encoding: "utf-8", timeout: 30_000 });
isolateGitRemote(attributableCwd, "https://github.com/foo/bar.git");
const ts = new Date().toISOString();
const cwdLine = `{"type":"user","message":{"role":"user","content":"hello"},"timestamp":"${ts}","cwd":"${attributableCwd.replace(/\\/g, "\\\\")}"}\n`;
@@ -1082,6 +1095,7 @@ describe("#2392: transcript ingest honors per-remote trust policy", () => {
mkdirSync(repo, { recursive: true });
spawnSync("git", ["-C", repo, "init", "-q"], { encoding: "utf-8", timeout: 30_000 });
spawnSync("git", ["-C", repo, "remote", "add", "origin", remoteUrl], { encoding: "utf-8", timeout: 30_000 });
isolateGitRemote(repo, remoteUrl);
return repo;
}