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
+12
View File
@@ -37,6 +37,15 @@ import {
LARGE_REPO_FILE_THRESHOLD,
} from "../lib/code-intelligence";
function isolateGitRemote(repo: string, url: string): void {
const git = (...args: string[]) => execFileSync("git", args, {
cwd: repo, encoding: "utf8", timeout: 10_000,
}).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);
}
describe("capability matrix", () => {
test("every provider advertises the four required capabilities", () => {
for (const p of [new GbrainProvider(), new SourcebotProvider(), new GraphifyProvider()]) {
@@ -402,6 +411,7 @@ describe("consent unification — deny tier wins (R1)", () => {
const git = (...a: string[]) => execFileSync("git", a, { cwd: repo, timeout: 30_000 });
git("init", "-q", ".");
git("remote", "add", "origin", url);
isolateGitRemote(repo, url);
return repo;
}
const POLICY_BIN = path.join(import.meta.dir, "..", "bin", "gstack-gbrain-repo-policy");
@@ -500,6 +510,7 @@ describe("read-only repo policy blocks write-class CLI index (R2)", () => {
fs.mkdirSync(repo, { recursive: true });
execFileSync("git", ["init", "-q", "."], { cwd: repo });
execFileSync("git", ["remote", "add", "origin", URL], { cwd: repo });
isolateGitRemote(repo, URL);
setProvider("gbrain", env);
setConsent(repo, true, env);
execFileSync(POLICY_BIN, ["set", URL, "read-only"], { env, encoding: "utf-8" });
@@ -702,6 +713,7 @@ describe("CLI search consent gate (gbrain provider, honest refusal message)", ()
fs.mkdirSync(repo, { recursive: true });
execFileSync("git", ["init", "-q", "."], { cwd: repo });
execFileSync("git", ["remote", "add", "origin", URL], { cwd: repo });
isolateGitRemote(repo, URL);
env = { ...process.env, GSTACK_HOME: home, PATH: `${shimDir}:${process.env.PATH}` };
setProvider("gbrain", env);
setRoot("gbrain", repo, env);