oh-my-promptfoo 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5048,7 +5048,7 @@ init_esm();
5048
5048
  var import_context_async_hooks = __toESM(require_src(), 1);
5049
5049
  init_esm3();
5050
5050
  var import_sdk_trace_node = __toESM(require_src2(), 1);
5051
- import { lstatSync, statSync as statSync2 } from "fs";
5051
+ import { lstatSync, readFileSync, statSync as statSync2 } from "fs";
5052
5052
  import { stat } from "fs/promises";
5053
5053
  import { basename, dirname as dirname2, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
5054
5054
  import { pathToFileURL as pathToFileURL3 } from "url";
@@ -6889,6 +6889,21 @@ async function runNative(frame, signal) {
6889
6889
  }
6890
6890
  return response;
6891
6891
  }
6892
+ var skillParentDirectories = [".agents", ".claude", ".github"];
6893
+ function workspaceSkillDirectories(workingDir) {
6894
+ const directories = [];
6895
+ for (const name of skillParentDirectories) {
6896
+ const parent = resolve3(workingDir, name);
6897
+ const skills = resolve3(parent, "skills");
6898
+ try {
6899
+ if (lstatSync(parent).isDirectory() && lstatSync(skills).isDirectory())
6900
+ directories.push(skills);
6901
+ } catch (error) {
6902
+ if (error.code !== "ENOENT") throw error;
6903
+ }
6904
+ }
6905
+ return directories;
6906
+ }
6892
6907
  function observedSkillRead(toolName, args, workingDir) {
6893
6908
  if (!["read", "read_file", "view"].includes(toolName) || !args || typeof args !== "object" || Array.isArray(args))
6894
6909
  return;
@@ -6898,17 +6913,20 @@ function observedSkillRead(toolName, args, workingDir) {
6898
6913
  const candidate = resolve3(workingDir, path4);
6899
6914
  const skillDirectory = dirname2(candidate);
6900
6915
  const skillsDirectory = dirname2(skillDirectory);
6901
- if (basename(skillsDirectory) !== "skills" || ![".agents", ".claude", ".github"].includes(basename(dirname2(skillsDirectory))))
6916
+ if (basename(skillsDirectory) !== "skills" || !skillParentDirectories.includes(basename(dirname2(skillsDirectory))))
6902
6917
  return;
6903
6918
  try {
6904
- const root = statSync2(workingDir, { bigint: true });
6919
+ const rootPath = resolve3(workingDir);
6920
+ const root = statSync2(rootPath, { bigint: true });
6905
6921
  if (root.ino === 0n) return;
6906
6922
  let cursor = candidate;
6907
6923
  let file = true;
6908
6924
  while (true) {
6909
6925
  const info = lstatSync(cursor, { bigint: true });
6910
- if (info.isSymbolicLink() || file && !info.isFile()) return;
6911
- if (info.dev === root.dev && info.ino === root.ino)
6926
+ const allowedAlias = info.isSymbolicLink() && cursor === rootPath;
6927
+ if (info.isSymbolicLink() && !allowedAlias || file && !info.isFile()) return;
6928
+ const physical = allowedAlias ? statSync2(cursor, { bigint: true }) : info;
6929
+ if (physical.dev === root.dev && physical.ino === root.ino)
6912
6930
  return { name: basename(skillDirectory), path: candidate, source: "read-tool" };
6913
6931
  const parent = dirname2(cursor);
6914
6932
  if (parent === cursor) return;
@@ -6919,10 +6937,25 @@ function observedSkillRead(toolName, args, workingDir) {
6919
6937
  return;
6920
6938
  }
6921
6939
  }
6940
+ function verifiedSkillLoaderRecord(entry, workingDir) {
6941
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return;
6942
+ const { name, path: path4, content } = entry;
6943
+ if (typeof name !== "string" || typeof path4 !== "string" || typeof content !== "string") return;
6944
+ const local = observedSkillRead("read_file", { path: path4 }, workingDir);
6945
+ if (!local || local.name !== name) return;
6946
+ try {
6947
+ const body = readFileSync(local.path, "utf8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, "").replace(/\r\n/g, "\n").trim();
6948
+ if (!body || body !== content.replace(/\r\n/g, "\n").trim()) return;
6949
+ return { ...local, source: "sdk-skill-loader" };
6950
+ } catch {
6951
+ return;
6952
+ }
6953
+ }
6922
6954
  async function runCopilot(frame, signal) {
6923
6955
  const sdk = await import(pathToFileURL3(resolvePeer(frame.basePath, "@github/copilot-sdk")).href);
6924
6956
  if (typeof sdk.CopilotClient !== "function")
6925
6957
  throw new Error("Install the supported @github/copilot-sdk@1.0.6 peer");
6958
+ const skillDirectories = workspaceSkillDirectories(frame.workingDir);
6926
6959
  const client = new sdk.CopilotClient({
6927
6960
  workingDirectory: frame.workingDir,
6928
6961
  env: process.env,
@@ -6968,6 +7001,8 @@ async function runCopilot(frame, signal) {
6968
7001
  ...frame.config.provider ? { provider: frame.config.provider } : {},
6969
7002
  onPermissionRequest: approve,
6970
7003
  enableConfigDiscovery: false,
7004
+ // Keep general config discovery off; expose only physical workspace skill directories.
7005
+ ...skillDirectories.length ? { enableSkills: true, skillDirectories } : {},
6971
7006
  infiniteSessions: { enabled: false }
6972
7007
  });
6973
7008
  unsubscribe = session.on((event) => {
@@ -7031,6 +7066,18 @@ async function runCopilot(frame, signal) {
7031
7066
  frame.config.timeoutMs ?? 9e5
7032
7067
  );
7033
7068
  if (signal.aborted) throw new Error("Copilot call aborted");
7069
+ if (!sessionError) {
7070
+ try {
7071
+ const invoked = await session.rpc?.skills?.getInvoked?.();
7072
+ if (Array.isArray(invoked?.skills) && invoked.skills.length) {
7073
+ for (const entry of invoked.skills) {
7074
+ const loaded = verifiedSkillLoaderRecord(entry, frame.workingDir);
7075
+ if (loaded) skillCalls.push(loaded);
7076
+ }
7077
+ }
7078
+ } catch {
7079
+ }
7080
+ }
7034
7081
  if (sessionError)
7035
7082
  result = { error: sessionError, ...output ? { output } : {}, tokenUsage: usage, cost };
7036
7083
  else {