@kyo-so/cli 0.8.0 → 0.9.0

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.
package/dist/bin/kyoso.js CHANGED
@@ -7587,7 +7587,7 @@ ${lanes.join(`
7587
7587
  getCurrentDirectory,
7588
7588
  getAccessibleSortedChildDirectories,
7589
7589
  fileSystemEntryExists,
7590
- realpath,
7590
+ realpath: realpath2,
7591
7591
  setTimeout: setTimeout2,
7592
7592
  clearTimeout: clearTimeout2
7593
7593
  }) {
@@ -7763,13 +7763,13 @@ ${lanes.join(`
7763
7763
  const parentWatcher = cache.get(parentDirPath);
7764
7764
  if (!parentWatcher)
7765
7765
  return false;
7766
- const target = normalizePath(realpath(parentDir));
7766
+ const target = normalizePath(realpath2(parentDir));
7767
7767
  let hasChanges;
7768
7768
  let newChildWatches;
7769
7769
  if (filePathComparer(target, parentDir) === 0) {
7770
7770
  hasChanges = enumerateInsertsAndDeletes(fileSystemEntryExists(parentDir, 1) ? mapDefined(getAccessibleSortedChildDirectories(parentDir), (child) => {
7771
7771
  const childFullName = getNormalizedAbsolutePath(child, parentDir);
7772
- return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(realpath(childFullName))) === 0 ? childFullName : undefined;
7772
+ return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(realpath2(childFullName))) === 0 ? childFullName : undefined;
7773
7773
  }) : emptyArray, parentWatcher.childWatches, (child, childWatcher) => filePathComparer(child, childWatcher.dirName), createAndAddChildDirectoryWatcher, closeFileWatcher, addChildDirectoryWatcher);
7774
7774
  } else if (parentWatcher.targetWatcher && filePathComparer(target, parentWatcher.targetWatcher.dirName) === 0) {
7775
7775
  hasChanges = false;
@@ -7843,7 +7843,7 @@ ${lanes.join(`
7843
7843
  getCurrentDirectory,
7844
7844
  fsSupportsRecursiveFsWatch,
7845
7845
  getAccessibleSortedChildDirectories,
7846
- realpath,
7846
+ realpath: realpath2,
7847
7847
  tscWatchFile,
7848
7848
  useNonPollingWatchers,
7849
7849
  tscWatchDirectory,
@@ -7928,7 +7928,7 @@ ${lanes.join(`
7928
7928
  fileSystemEntryExists,
7929
7929
  getAccessibleSortedChildDirectories,
7930
7930
  watchDirectory: nonRecursiveWatchDirectory,
7931
- realpath,
7931
+ realpath: realpath2,
7932
7932
  setTimeout: setTimeout2,
7933
7933
  clearTimeout: clearTimeout2
7934
7934
  });
@@ -8104,7 +8104,7 @@ ${lanes.join(`
8104
8104
  fileSystemEntryExists,
8105
8105
  fsSupportsRecursiveFsWatch,
8106
8106
  getAccessibleSortedChildDirectories: (path) => getAccessibleFileSystemEntries(path).directories,
8107
- realpath,
8107
+ realpath: realpath2,
8108
8108
  tscWatchFile: process.env.TSC_WATCHFILE,
8109
8109
  useNonPollingWatchers: !!process.env.TSC_NONPOLLING_WATCHER,
8110
8110
  tscWatchDirectory: process.env.TSC_WATCHDIRECTORY,
@@ -8178,7 +8178,7 @@ ${lanes.join(`
8178
8178
  enableCPUProfiler,
8179
8179
  disableCPUProfiler,
8180
8180
  cpuProfilingEnabled: () => !!activeSession || contains(process.execArgv, "--cpu-prof") || contains(process.execArgv, "--prof"),
8181
- realpath,
8181
+ realpath: realpath2,
8182
8182
  debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(process.execArgv, (arg) => /^--(?:inspect|debug)(?:-brk)?(?:=\d+)?$/i.test(arg)) || !!process.recordreplay,
8183
8183
  tryEnableSourceMapsForHost() {
8184
8184
  try {
@@ -8391,7 +8391,7 @@ ${lanes.join(`
8391
8391
  }
8392
8392
  }
8393
8393
  function readDirectory(path, extensions, excludes, includes, depth) {
8394
- return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
8394
+ return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries, realpath2);
8395
8395
  }
8396
8396
  function fileSystemEntryExists(path, entryKind) {
8397
8397
  const stat = statSync(path);
@@ -8419,7 +8419,7 @@ ${lanes.join(`
8419
8419
  function fsRealPathHandlingLongPath(path) {
8420
8420
  return path.length < 260 ? _fs.realpathSync.native(path) : _fs.realpathSync(path);
8421
8421
  }
8422
- function realpath(path) {
8422
+ function realpath2(path) {
8423
8423
  try {
8424
8424
  return fsRealpath(path);
8425
8425
  } catch {
@@ -19724,18 +19724,18 @@ ${lanes.join(`
19724
19724
  diagnostics.add(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, hostErrorMessage));
19725
19725
  }, sourceFiles, data);
19726
19726
  }
19727
- function ensureDirectoriesExist(directoryPath, createDirectory, directoryExists) {
19727
+ function ensureDirectoriesExist(directoryPath, createDirectory2, directoryExists) {
19728
19728
  if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
19729
19729
  const parentDirectory = getDirectoryPath(directoryPath);
19730
- ensureDirectoriesExist(parentDirectory, createDirectory, directoryExists);
19731
- createDirectory(directoryPath);
19730
+ ensureDirectoriesExist(parentDirectory, createDirectory2, directoryExists);
19731
+ createDirectory2(directoryPath);
19732
19732
  }
19733
19733
  }
19734
- function writeFileEnsuringDirectories(path, data, writeByteOrderMark, writeFile22, createDirectory, directoryExists) {
19734
+ function writeFileEnsuringDirectories(path, data, writeByteOrderMark, writeFile22, createDirectory2, directoryExists) {
19735
19735
  try {
19736
19736
  writeFile22(path, data, writeByteOrderMark);
19737
19737
  } catch {
19738
- ensureDirectoriesExist(getDirectoryPath(normalizePath(path)), createDirectory, directoryExists);
19738
+ ensureDirectoriesExist(getDirectoryPath(normalizePath(path)), createDirectory2, directoryExists);
19739
19739
  writeFile22(path, data, writeByteOrderMark);
19740
19740
  }
19741
19741
  }
@@ -21670,7 +21670,7 @@ ${lanes.join(`
21670
21670
  function getRegexFromPattern(pattern, useCaseSensitiveFileNames2) {
21671
21671
  return new RegExp(pattern, useCaseSensitiveFileNames2 ? "" : "i");
21672
21672
  }
21673
- function matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries, realpath) {
21673
+ function matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries, realpath2) {
21674
21674
  path = normalizePath(path);
21675
21675
  currentDirectory = normalizePath(currentDirectory);
21676
21676
  const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames2, currentDirectory);
@@ -21685,7 +21685,7 @@ ${lanes.join(`
21685
21685
  }
21686
21686
  return flatten(results);
21687
21687
  function visitDirectory(path2, absolutePath, depth2) {
21688
- const canonicalPath = toCanonical(realpath(absolutePath));
21688
+ const canonicalPath = toCanonical(realpath2(absolutePath));
21689
21689
  if (visited.has(canonicalPath))
21690
21690
  return;
21691
21691
  visited.set(canonicalPath, true);
@@ -41674,11 +41674,11 @@ ${lanes.join(`
41674
41674
  if (i < rootLength) {
41675
41675
  return;
41676
41676
  }
41677
- const sep = directory.lastIndexOf(directorySeparator, i - 1);
41678
- if (sep === -1) {
41677
+ const sep3 = directory.lastIndexOf(directorySeparator, i - 1);
41678
+ if (sep3 === -1) {
41679
41679
  return;
41680
41680
  }
41681
- return directory.substr(0, Math.max(sep, rootLength));
41681
+ return directory.substr(0, Math.max(sep3, rootLength));
41682
41682
  }
41683
41683
  }
41684
41684
  }
@@ -98915,14 +98915,14 @@ ${lanes.join(`
98915
98915
  }
98916
98916
  }
98917
98917
  function createImportCallExpressionAMD(arg, containsLexicalThis) {
98918
- const resolve = factory2.createUniqueName("resolve");
98918
+ const resolve3 = factory2.createUniqueName("resolve");
98919
98919
  const reject = factory2.createUniqueName("reject");
98920
98920
  const parameters = [
98921
- factory2.createParameterDeclaration(undefined, undefined, resolve),
98921
+ factory2.createParameterDeclaration(undefined, undefined, resolve3),
98922
98922
  factory2.createParameterDeclaration(undefined, undefined, reject)
98923
98923
  ];
98924
98924
  const body = factory2.createBlock([
98925
- factory2.createExpressionStatement(factory2.createCallExpression(factory2.createIdentifier("require"), undefined, [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve, reject]))
98925
+ factory2.createExpressionStatement(factory2.createCallExpression(factory2.createIdentifier("require"), undefined, [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve3, reject]))
98926
98926
  ]);
98927
98927
  let func;
98928
98928
  if (languageVersion >= 2) {
@@ -107027,12 +107027,12 @@ ${lanes.join(`
107027
107027
  directoryExists: host.directoryExists && directoryExists,
107028
107028
  getDirectories,
107029
107029
  readDirectory,
107030
- createDirectory: host.createDirectory && createDirectory,
107030
+ createDirectory: host.createDirectory && createDirectory2,
107031
107031
  writeFile: host.writeFile && writeFile22,
107032
107032
  addOrDeleteFileOrDirectory,
107033
107033
  addOrDeleteFile,
107034
107034
  clearCache,
107035
- realpath: host.realpath && realpath
107035
+ realpath: host.realpath && realpath2
107036
107036
  };
107037
107037
  function toPath3(fileName) {
107038
107038
  return toPath(fileName, currentDirectory, getCanonicalFileName);
@@ -107104,7 +107104,7 @@ ${lanes.join(`
107104
107104
  const path = toPath3(dirPath);
107105
107105
  return cachedReadDirectoryResult.has(ensureTrailingDirectorySeparator(path)) || host.directoryExists(dirPath);
107106
107106
  }
107107
- function createDirectory(dirPath) {
107107
+ function createDirectory2(dirPath) {
107108
107108
  const path = toPath3(dirPath);
107109
107109
  const result = getCachedFileSystemEntriesForBaseDir(path);
107110
107110
  if (result) {
@@ -107130,7 +107130,7 @@ ${lanes.join(`
107130
107130
  const rootResult = tryReadDirectory2(rootDir, rootDirPath);
107131
107131
  let rootSymLinkResult;
107132
107132
  if (rootResult !== undefined) {
107133
- return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries, realpath);
107133
+ return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames2, currentDirectory, depth, getFileSystemEntries, realpath2);
107134
107134
  }
107135
107135
  return host.readDirectory(rootDir, extensions, excludes, includes, depth);
107136
107136
  function getFileSystemEntries(dir) {
@@ -107153,7 +107153,7 @@ ${lanes.join(`
107153
107153
  return result;
107154
107154
  }
107155
107155
  }
107156
- function realpath(s) {
107156
+ function realpath2(s) {
107157
107157
  return host.realpath ? host.realpath(s) : s;
107158
107158
  }
107159
107159
  function clearFirstAncestorEntry(fileOrDirectoryPath) {
@@ -107527,11 +107527,11 @@ ${lanes.join(`
107527
107527
  return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes) : undefined;
107528
107528
  };
107529
107529
  }
107530
- function createWriteFileMeasuringIO(actualWriteFile, createDirectory, directoryExists) {
107530
+ function createWriteFileMeasuringIO(actualWriteFile, createDirectory2, directoryExists) {
107531
107531
  return (fileName, data, writeByteOrderMark, onError) => {
107532
107532
  try {
107533
107533
  mark("beforeIOWrite");
107534
- writeFileEnsuringDirectories(fileName, data, writeByteOrderMark, actualWriteFile, createDirectory, directoryExists);
107534
+ writeFileEnsuringDirectories(fileName, data, writeByteOrderMark, actualWriteFile, createDirectory2, directoryExists);
107535
107535
  mark("afterIOWrite");
107536
107536
  measure("I/O Write", "beforeIOWrite", "afterIOWrite");
107537
107537
  } catch (e) {
@@ -107558,7 +107558,7 @@ ${lanes.join(`
107558
107558
  return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
107559
107559
  }
107560
107560
  const newLine = getNewLineCharacter(options);
107561
- const realpath = system.realpath && ((path) => system.realpath(path));
107561
+ const realpath2 = system.realpath && ((path) => system.realpath(path));
107562
107562
  const compilerHost = {
107563
107563
  getSourceFile: createGetSourceFile((fileName) => compilerHost.readFile(fileName), setParentNodes),
107564
107564
  getDefaultLibLocation,
@@ -107574,7 +107574,7 @@ ${lanes.join(`
107574
107574
  directoryExists: (directoryName) => system.directoryExists(directoryName),
107575
107575
  getEnvironmentVariable: (name) => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
107576
107576
  getDirectories: (path) => system.getDirectories(path),
107577
- realpath,
107577
+ realpath: realpath2,
107578
107578
  readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth),
107579
107579
  createDirectory: (d) => system.createDirectory(d),
107580
107580
  createHash: maybeBind(system, system.createHash)
@@ -108611,10 +108611,10 @@ ${lanes.join(`
108611
108611
  }
108612
108612
  function getDefaultLibFilePriority(a) {
108613
108613
  if (containsPath(defaultLibraryPath, a.fileName, false)) {
108614
- const basename = getBaseFileName(a.fileName);
108615
- if (basename === "lib.d.ts" || basename === "lib.es6.d.ts")
108614
+ const basename2 = getBaseFileName(a.fileName);
108615
+ if (basename2 === "lib.d.ts" || basename2 === "lib.es6.d.ts")
108616
108616
  return 0;
108617
- const name = removeSuffix(removePrefix(basename, "lib."), ".d.ts");
108617
+ const name = removeSuffix(removePrefix(basename2, "lib."), ".d.ts");
108618
108618
  const index = libs.indexOf(name);
108619
108619
  if (index !== -1)
108620
108620
  return index + 1;
@@ -160210,9 +160210,9 @@ ${options.prefix}` : `
160210
160210
  if (this.host.realpath) {
160211
160211
  Debug.assert(!!this.containingProjects.length);
160212
160212
  const project = this.containingProjects[0];
160213
- const realpath = this.host.realpath(this.path);
160214
- if (realpath) {
160215
- this.realpath = project.toPath(realpath);
160213
+ const realpath2 = this.host.realpath(this.path);
160214
+ if (realpath2) {
160215
+ this.realpath = project.toPath(realpath2);
160216
160216
  if (this.realpath !== this.path) {
160217
160217
  project.projectService.realpathToScriptInfos.add(this.realpath, this);
160218
160218
  }
@@ -161298,8 +161298,8 @@ ${options.prefix}` : `
161298
161298
  }
161299
161299
  };
161300
161300
  for (const file2 of files) {
161301
- const basename = getBaseFileName(file2);
161302
- if (basename === "package.json" || basename === "bower.json") {
161301
+ const basename2 = getBaseFileName(file2);
161302
+ if (basename2 === "package.json" || basename2 === "bower.json") {
161303
161303
  createProjectWatcher(file2, "FileWatcher");
161304
161304
  continue;
161305
161305
  }
@@ -163486,9 +163486,9 @@ ${options.prefix}` : `
163486
163486
  this.filenameToScriptInfo.delete(info.path);
163487
163487
  this.filenameToScriptInfoVersion.set(info.path, info.textStorage.version);
163488
163488
  this.stopWatchingScriptInfo(info);
163489
- const realpath = info.getRealpathIfDifferent();
163490
- if (realpath) {
163491
- this.realpathToScriptInfos.remove(realpath, info);
163489
+ const realpath2 = info.getRealpathIfDifferent();
163490
+ if (realpath2) {
163491
+ this.realpathToScriptInfos.remove(realpath2, info);
163492
163492
  }
163493
163493
  info.closeSourceMapFileWatcher();
163494
163494
  }
@@ -164146,9 +164146,9 @@ All files are: ${JSON.stringify(names)}`, "Err");
164146
164146
  getSymlinkedProjects(info) {
164147
164147
  let projects;
164148
164148
  if (this.realpathToScriptInfos) {
164149
- const realpath = info.getRealpathIfDifferent();
164150
- if (realpath) {
164151
- forEach(this.realpathToScriptInfos.get(realpath), combineProjects);
164149
+ const realpath2 = info.getRealpathIfDifferent();
164150
+ if (realpath2) {
164151
+ forEach(this.realpathToScriptInfos.get(realpath2), combineProjects);
164152
164152
  }
164153
164153
  forEach(this.realpathToScriptInfos.get(info.path), combineProjects);
164154
164154
  }
@@ -164186,8 +164186,8 @@ All files are: ${JSON.stringify(names)}`, "Err");
164186
164186
  const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
164187
164187
  if (!fileOrDirectoryPath)
164188
164188
  return;
164189
- const basename = getBaseFileName(fileOrDirectoryPath);
164190
- if (((_a3 = result.affectedModuleSpecifierCacheProjects) == null ? undefined : _a3.size) && (basename === "package.json" || basename === "node_modules")) {
164189
+ const basename2 = getBaseFileName(fileOrDirectoryPath);
164190
+ if (((_a3 = result.affectedModuleSpecifierCacheProjects) == null ? undefined : _a3.size) && (basename2 === "package.json" || basename2 === "node_modules")) {
164191
164191
  result.affectedModuleSpecifierCacheProjects.forEach((project) => {
164192
164192
  var _a22;
164193
164193
  (_a22 = project.getModuleSpecifierCache()) == null || _a22.clear();
@@ -169234,8 +169234,8 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
169234
169234
  installPackage(options) {
169235
169235
  this.packageInstallId++;
169236
169236
  const request = { kind: "installPackage", ...options, id: this.packageInstallId };
169237
- const promise2 = new Promise((resolve, reject) => {
169238
- (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map)).set(this.packageInstallId, { resolve, reject });
169237
+ const promise2 = new Promise((resolve3, reject) => {
169238
+ (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map)).set(this.packageInstallId, { resolve: resolve3, reject });
169239
169239
  });
169240
169240
  this.installer.send(request);
169241
169241
  return promise2;
@@ -169495,7 +169495,7 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
169495
169495
  });
169496
169496
 
169497
169497
  // src/cli/main.ts
169498
- import { spawnSync as spawnSync2 } from "node:child_process";
169498
+ import { spawnSync as spawnSync3 } from "node:child_process";
169499
169499
 
169500
169500
  // src/cli/args.ts
169501
169501
  function parseArgs(argv) {
@@ -169599,14 +169599,364 @@ function languageFromPath(path) {
169599
169599
  }
169600
169600
 
169601
169601
  // src/cli/doctor.ts
169602
- import { accessSync } from "node:fs";
169603
- import { delimiter as delimiter2, resolve as resolve4 } from "node:path";
169602
+ import { accessSync as accessSync2 } from "node:fs";
169603
+ import { delimiter as delimiter3, resolve as resolve8 } from "node:path";
169604
+
169605
+ // src/audit/stateRoot.ts
169606
+ import { createHash } from "node:crypto";
169607
+ import { lstat, mkdir as mkdir2, realpath } from "node:fs/promises";
169608
+ import { basename, dirname as dirname2, isAbsolute, join, resolve as resolve2 } from "node:path";
169609
+
169610
+ // src/context/pathPolicy.ts
169611
+ import { normalize, sep } from "node:path";
169612
+
169613
+ // src/core/errors.ts
169614
+ class KyosoRequestError extends Error {
169615
+ code;
169616
+ constructor(message, code) {
169617
+ super(message);
169618
+ this.code = code;
169619
+ this.name = "KyosoRequestError";
169620
+ }
169621
+ }
169622
+
169623
+ // src/context/pathPolicy.ts
169624
+ function normalizeRelativePath(path) {
169625
+ const normalized = normalize(path).replaceAll("\\", "/");
169626
+ if (normalized === "." || normalized.startsWith("..") || normalized.includes(`${sep}..${sep}`) || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized)) {
169627
+ throw new KyosoRequestError(`Path escapes workspace root: ${path}`, "INVALID_PATH");
169628
+ }
169629
+ return normalized;
169630
+ }
169631
+ function isDeniedPath(path, denyPatterns) {
169632
+ return matchesPathPattern(path, denyPatterns, "deny");
169633
+ }
169634
+ function isAllowedPath(path, allowPatterns) {
169635
+ if (allowPatterns.length === 0)
169636
+ return true;
169637
+ return matchesPathPattern(path, allowPatterns, "allow");
169638
+ }
169639
+ function matchesPathPattern(path, patterns, mode) {
169640
+ const normalized = normalizeRelativePath(path);
169641
+ const segments = normalized.split("/");
169642
+ return patterns.some((pattern) => {
169643
+ const normalizedPattern = normalizeRelativePath(pattern);
169644
+ if (normalizedPattern.endsWith(".*") && !normalizedPattern.includes("/")) {
169645
+ const base = normalizedPattern.slice(0, -2);
169646
+ if (mode === "allow") {
169647
+ const firstSegment = segments[0] ?? "";
169648
+ return firstSegment === base || firstSegment.startsWith(`${base}.`);
169649
+ }
169650
+ return segments.some((segment) => segment === base || segment.startsWith(`${base}.`));
169651
+ }
169652
+ if (normalizedPattern.includes("*")) {
169653
+ const escaped = normalizedPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
169654
+ const regex = mode === "allow" ? new RegExp(`^${escaped}($|/)`) : new RegExp(`(^|/)${escaped}($|/)`);
169655
+ return regex.test(normalized);
169656
+ }
169657
+ if (!normalizedPattern.includes("/")) {
169658
+ if (mode === "allow") {
169659
+ return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
169660
+ }
169661
+ return segments.includes(normalizedPattern);
169662
+ }
169663
+ if (mode === "allow") {
169664
+ return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
169665
+ }
169666
+ return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`) || normalized.endsWith(`/${normalizedPattern}`) || normalized.includes(`/${normalizedPattern}/`);
169667
+ });
169668
+ }
169669
+
169670
+ // src/core/constants.ts
169671
+ var DEFAULT_AGENT_TIMEOUT_MS = 120000;
169672
+ var RAW_OUTPUT_MAX_CHARS = 16384;
169673
+ var TRACE_DIR = ".kyoso/traces";
169674
+ var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
169675
+ var KYOSO_VERSION = "0.9.0";
169676
+
169677
+ // src/utils/pathContainment.ts
169678
+ import { resolve, sep as sep2 } from "node:path";
169679
+ function isPathWithin(candidate, root) {
169680
+ const resolvedCandidate = resolve(candidate);
169681
+ const resolvedRoot = resolve(root);
169682
+ const boundary = resolvedRoot.endsWith(sep2) ? resolvedRoot : `${resolvedRoot}${sep2}`;
169683
+ return resolvedCandidate === resolvedRoot || resolvedCandidate.startsWith(boundary);
169684
+ }
169685
+
169686
+ // src/audit/stateRoot.ts
169687
+ var PRIVATE_DIRECTORY_MODE = 448;
169688
+ var UNSAFE_DIRECTORY_MODE = 18;
169689
+ var STICKY_DIRECTORY_MODE = 512;
169690
+ var AUDIT_WARNING_DIRECTORY_IGNORED = "AUDIT_DIRECTORY_IGNORED: Audit directory is invalid; using the default logical directory.";
169691
+ var AUDIT_WARNING_UNSUPPORTED_PLATFORM = "AUDIT_DISABLED_UNSUPPORTED_PLATFORM: Audit trace writing is unavailable on this platform.";
169692
+ var AUDIT_WARNING_UNSUPPORTED_CAPABILITY = "AUDIT_DISABLED_UNSUPPORTED_CAPABILITY: Audit trace writing requires unavailable filesystem capabilities.";
169693
+ var AUDIT_WARNING_UNSAFE_STATE_ROOT = "AUDIT_DISABLED_UNSAFE_STATE_ROOT: Audit state root could not be verified.";
169694
+ async function resolveAuditStateRoot(options) {
169695
+ const warnings = [];
169696
+ const logicalDirectory = validateAuditDirectory(options.directory, warnings);
169697
+ const platform = options.platform ?? process.platform;
169698
+ if (platform === "win32") {
169699
+ return { warnings: [...warnings, AUDIT_WARNING_UNSUPPORTED_PLATFORM] };
169700
+ }
169701
+ const uid = getCurrentUid(options.getuid);
169702
+ if (uid === undefined) {
169703
+ return { warnings: [...warnings, AUDIT_WARNING_UNSUPPORTED_CAPABILITY] };
169704
+ }
169705
+ try {
169706
+ const workspaceRoot = await realpath(resolve2(options.cwd));
169707
+ const candidate = resolveStateBaseCandidate(options.env ?? process.env);
169708
+ if (!candidate)
169709
+ throw new Error("missing trusted state base");
169710
+ const stateBase = await ensureTrustedStateBase({
169711
+ candidate,
169712
+ workspaceRoot,
169713
+ uid
169714
+ });
169715
+ const kyosoRoot = await ensureTrustedDirectory({
169716
+ root: stateBase,
169717
+ segments: ["kyoso"],
169718
+ uid,
169719
+ workspaceRoot
169720
+ });
169721
+ return {
169722
+ stateBase,
169723
+ kyosoRoot,
169724
+ workspaceRoot,
169725
+ workspaceHash: createHash("sha256").update(workspaceRoot).digest("hex"),
169726
+ logicalDirectory,
169727
+ uid,
169728
+ warnings
169729
+ };
169730
+ } catch {
169731
+ return { warnings: [...warnings, AUDIT_WARNING_UNSAFE_STATE_ROOT] };
169732
+ }
169733
+ }
169734
+ async function inspectAuditStateRootCapability(options) {
169735
+ const platform = options.platform ?? process.platform;
169736
+ if (platform === "win32" || getCurrentUid(options.getuid) === undefined) {
169737
+ return { available: false };
169738
+ }
169739
+ try {
169740
+ const uid = getCurrentUid(options.getuid);
169741
+ if (uid === undefined)
169742
+ return { available: false };
169743
+ const workspaceRoot = await realpath(resolve2(options.cwd));
169744
+ const candidate = resolveStateBaseCandidate(options.env ?? process.env);
169745
+ if (!candidate || isPathWithin(candidate, workspaceRoot)) {
169746
+ return { available: false };
169747
+ }
169748
+ const existing = await findExistingAncestor(candidate);
169749
+ assertSafeDirectory(existing.path, uid, await lstat(existing.path), {
169750
+ allowFilesystemRoot: true
169751
+ });
169752
+ const realExisting = await realpath(existing.path);
169753
+ if (isPathWithin(realExisting, workspaceRoot))
169754
+ return { available: false };
169755
+ await assertTrustedAncestorChain(realExisting, uid);
169756
+ if (existing.missingSegments.length === 0) {
169757
+ const kyosoRoot = join(realExisting, "kyoso");
169758
+ const kyosoStat = await optionalLstat(kyosoRoot);
169759
+ if (kyosoStat)
169760
+ assertSafeDirectory(kyosoRoot, uid, kyosoStat);
169761
+ }
169762
+ return { available: true };
169763
+ } catch {
169764
+ return { available: false };
169765
+ }
169766
+ }
169767
+ async function ensureTrustedDirectory(options) {
169768
+ const realRoot = await realpath(options.root);
169769
+ assertSafeDirectory(realRoot, options.uid, await lstat(realRoot));
169770
+ if (options.workspaceRoot && isPathWithin(realRoot, options.workspaceRoot)) {
169771
+ throw new Error("trusted directory resolves inside workspace");
169772
+ }
169773
+ await assertTrustedAncestorChain(realRoot, options.uid);
169774
+ let current = realRoot;
169775
+ for (const segment of options.segments) {
169776
+ if (!isSafePathSegment(segment))
169777
+ throw new Error("unsafe path segment");
169778
+ const next = join(current, segment);
169779
+ let entry = await optionalLstat(next);
169780
+ if (!entry) {
169781
+ await createDirectory(next);
169782
+ entry = await lstat(next);
169783
+ }
169784
+ assertSafeDirectory(next, options.uid, entry);
169785
+ const realNext = await realpath(next);
169786
+ if (!isPathWithin(realNext, realRoot)) {
169787
+ throw new Error("managed directory escaped trusted root");
169788
+ }
169789
+ if (options.workspaceRoot && isPathWithin(realNext, options.workspaceRoot)) {
169790
+ throw new Error("managed directory resolves inside workspace");
169791
+ }
169792
+ current = realNext;
169793
+ }
169794
+ return current;
169795
+ }
169796
+ function isResolvedAuditStateRoot(resolution) {
169797
+ return "kyosoRoot" in resolution;
169798
+ }
169799
+ function validateAuditDirectory(directory, warnings) {
169800
+ try {
169801
+ if (directory.trim().length === 0 || isAbsolute(directory) || directory.split(/[\\/]+/).includes("..")) {
169802
+ throw new Error("unsafe logical directory");
169803
+ }
169804
+ const normalized = normalizeRelativePath(directory);
169805
+ if (normalized === "." || normalized.split("/").includes("..")) {
169806
+ throw new Error("unsafe logical directory");
169807
+ }
169808
+ return normalized;
169809
+ } catch {
169810
+ warnings.push(AUDIT_WARNING_DIRECTORY_IGNORED);
169811
+ return TRACE_DIR;
169812
+ }
169813
+ }
169814
+ function resolveStateBaseCandidate(env) {
169815
+ const xdgStateHome = env.XDG_STATE_HOME?.trim();
169816
+ if (xdgStateHome && isAbsolute(xdgStateHome)) {
169817
+ return resolve2(xdgStateHome);
169818
+ }
169819
+ const home = env.HOME?.trim();
169820
+ if (!home || !isAbsolute(home))
169821
+ return;
169822
+ return join(resolve2(home), ".local", "state");
169823
+ }
169824
+ async function ensureTrustedStateBase(options) {
169825
+ if (isPathWithin(options.candidate, options.workspaceRoot)) {
169826
+ throw new Error("state base is inside workspace");
169827
+ }
169828
+ const existing = await findExistingAncestor(options.candidate);
169829
+ assertSafeDirectory(existing.path, options.uid, await lstat(existing.path), {
169830
+ allowFilesystemRoot: true
169831
+ });
169832
+ const realExisting = await realpath(existing.path);
169833
+ if (isPathWithin(realExisting, options.workspaceRoot)) {
169834
+ throw new Error("state base resolves inside workspace");
169835
+ }
169836
+ await assertTrustedAncestorChain(realExisting, options.uid);
169837
+ let current = realExisting;
169838
+ for (const segment of existing.missingSegments) {
169839
+ current = join(current, segment);
169840
+ await createDirectory(current);
169841
+ const entry = await lstat(current);
169842
+ assertSafeDirectory(current, options.uid, entry);
169843
+ const realCurrent = await realpath(current);
169844
+ if (!isPathWithin(realCurrent, realExisting)) {
169845
+ throw new Error("state base changed while being created");
169846
+ }
169847
+ if (isPathWithin(realCurrent, options.workspaceRoot)) {
169848
+ throw new Error("state base resolves inside workspace");
169849
+ }
169850
+ current = realCurrent;
169851
+ }
169852
+ const stateBase = await realpath(current);
169853
+ assertSafeDirectory(stateBase, options.uid, await lstat(stateBase));
169854
+ if (isPathWithin(stateBase, options.workspaceRoot)) {
169855
+ throw new Error("state base resolves inside workspace");
169856
+ }
169857
+ await assertTrustedAncestorChain(stateBase, options.uid);
169858
+ return stateBase;
169859
+ }
169860
+ async function findExistingAncestor(candidate) {
169861
+ let current = resolve2(candidate);
169862
+ const missingSegments = [];
169863
+ while (true) {
169864
+ const entry = await optionalLstat(current);
169865
+ if (entry) {
169866
+ if (entry.isSymbolicLink() || !entry.isDirectory()) {
169867
+ throw new Error("state base ancestor is unsafe");
169868
+ }
169869
+ return { path: current, missingSegments };
169870
+ }
169871
+ const parent = dirname2(current);
169872
+ if (parent === current)
169873
+ throw new Error("state base has no existing ancestor");
169874
+ missingSegments.unshift(basename(current));
169875
+ current = parent;
169876
+ }
169877
+ }
169878
+ function assertSafeDirectory(path, uid, entry, options = {}) {
169879
+ const stat = entry;
169880
+ if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
169881
+ throw new Error("state directory is unsafe");
169882
+ }
169883
+ if (numericStatValue(stat.uid) !== uid && !(options.allowFilesystemRoot && isFilesystemRoot(path))) {
169884
+ throw new Error("state directory owner is unsafe");
169885
+ }
169886
+ const mode = numericStatValue(stat.mode);
169887
+ if ((mode & UNSAFE_DIRECTORY_MODE) !== 0) {
169888
+ throw new Error("state directory mode is unsafe");
169889
+ }
169890
+ }
169891
+ function numericStatValue(value) {
169892
+ return typeof value === "number" ? value : Number(value);
169893
+ }
169894
+ function getCurrentUid(getuid) {
169895
+ const resolveUid = getuid ?? (typeof process.getuid === "function" ? process.getuid.bind(process) : undefined);
169896
+ try {
169897
+ const uid = resolveUid?.();
169898
+ return typeof uid === "number" && Number.isInteger(uid) && uid >= 0 ? uid : undefined;
169899
+ } catch {
169900
+ return;
169901
+ }
169902
+ }
169903
+ function isFilesystemRoot(path) {
169904
+ return dirname2(path) === path;
169905
+ }
169906
+ async function assertTrustedAncestorChain(path, uid) {
169907
+ let child = path;
169908
+ while (!isFilesystemRoot(child)) {
169909
+ const parent = dirname2(child);
169910
+ const [parentStat, childStat] = await Promise.all([
169911
+ lstat(parent),
169912
+ lstat(child)
169913
+ ]);
169914
+ if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) {
169915
+ throw new Error("state directory ancestor is unsafe");
169916
+ }
169917
+ const parentMode = numericStatValue(parentStat.mode);
169918
+ const parentUid = numericStatValue(parentStat.uid);
169919
+ const childUid = numericStatValue(childStat.uid);
169920
+ const parentWritableByOthers = (parentMode & UNSAFE_DIRECTORY_MODE) !== 0;
169921
+ const stickyChildEntry = (parentMode & STICKY_DIRECTORY_MODE) !== 0 && childUid === uid;
169922
+ if (parentUid !== uid && parentUid !== 0 || parentWritableByOthers && !stickyChildEntry) {
169923
+ throw new Error("state directory ancestor permissions are unsafe");
169924
+ }
169925
+ child = parent;
169926
+ }
169927
+ }
169928
+ function isSafePathSegment(segment) {
169929
+ return segment.length > 0 && segment !== "." && segment !== ".." && !segment.includes("/") && !segment.includes("\\");
169930
+ }
169931
+ async function optionalLstat(path) {
169932
+ try {
169933
+ return await lstat(path);
169934
+ } catch (error) {
169935
+ if (isMissingPathError(error))
169936
+ return;
169937
+ throw error;
169938
+ }
169939
+ }
169940
+ async function createDirectory(path) {
169941
+ try {
169942
+ await mkdir2(path, { mode: PRIVATE_DIRECTORY_MODE });
169943
+ } catch (error) {
169944
+ if (!isAlreadyExistsError(error))
169945
+ throw error;
169946
+ }
169947
+ }
169948
+ function isMissingPathError(error) {
169949
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
169950
+ }
169951
+ function isAlreadyExistsError(error) {
169952
+ return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
169953
+ }
169604
169954
 
169605
169955
  // src/config/loadConfig.ts
169606
169956
  import { access as access2, readFile as readFile4 } from "node:fs/promises";
169607
169957
  import { homedir as homedir2 } from "node:os";
169608
169958
  import { stderr, stdin } from "node:process";
169609
- import { extname as extname3, join as join2, resolve as resolve2 } from "node:path";
169959
+ import { extname as extname3, join as join3, resolve as resolve4 } from "node:path";
169610
169960
  import { createInterface as createInterface2 } from "node:readline/promises";
169611
169961
 
169612
169962
  // src/config/defaultConfig.ts
@@ -184360,12 +184710,6 @@ var kyosoConfigSecuritySensitivePrefixes = [
184360
184710
  // src/security/redact.ts
184361
184711
  var REDACTION = "[KYOSO_REDACTED]";
184362
184712
 
184363
- // src/core/constants.ts
184364
- var DEFAULT_AGENT_TIMEOUT_MS = 120000;
184365
- var RAW_OUTPUT_MAX_CHARS = 16384;
184366
- var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
184367
- var KYOSO_VERSION = "0.8.0";
184368
-
184369
184713
  // src/security/sanitizeText.ts
184370
184714
  var SENSITIVE_TEXT_PATTERNS = [
184371
184715
  /\bsk-(?:proj-)?[A-Za-z0-9_-]{8,}\b/g,
@@ -184833,7 +185177,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
184833
185177
  }
184834
185178
  return ptr;
184835
185179
  }
184836
- function skipUntil(str, ptr, sep, end, banNewLines = false) {
185180
+ function skipUntil(str, ptr, sep3, end, banNewLines = false) {
184837
185181
  if (!end) {
184838
185182
  ptr = indexOfNewline(str, ptr);
184839
185183
  return ptr < 0 ? str.length : ptr;
@@ -184842,7 +185186,7 @@ function skipUntil(str, ptr, sep, end, banNewLines = false) {
184842
185186
  let c = str[i];
184843
185187
  if (c === "#") {
184844
185188
  i = indexOfNewline(str, i);
184845
- } else if (c === sep) {
185189
+ } else if (c === sep3) {
184846
185190
  return i + 1;
184847
185191
  } else if (c === end || banNewLines && (c === `
184848
185192
  ` || c === "\r" && str[i + 1] === `
@@ -185340,7 +185684,7 @@ function formatTomlError(configPath, error51) {
185340
185684
  // src/config/tsConfigLoader.ts
185341
185685
  var import_typescript = __toESM(require_typescript(), 1);
185342
185686
  import { createRequire as createRequire2 } from "node:module";
185343
- import { dirname as dirname2, extname as extname2 } from "node:path";
185687
+ import { dirname as dirname3, extname as extname2 } from "node:path";
185344
185688
  import { pathToFileURL } from "node:url";
185345
185689
  import vm from "node:vm";
185346
185690
 
@@ -185374,20 +185718,20 @@ function loadTypeScriptConfig(filePath, source) {
185374
185718
  ${transpiled}
185375
185719
  })`, { filename: filePath });
185376
185720
  const runner = script.runInThisContext();
185377
- runner(module.exports, require2, module, filePath, dirname2(filePath), defineConfig);
185721
+ runner(module.exports, require2, module, filePath, dirname3(filePath), defineConfig);
185378
185722
  return module.exports;
185379
185723
  }
185380
185724
 
185381
185725
  // src/config/trustedConfig.ts
185382
- import { createHash } from "node:crypto";
185383
- import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
185726
+ import { createHash as createHash2 } from "node:crypto";
185727
+ import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
185384
185728
  import { homedir } from "node:os";
185385
- import { dirname as dirname3, join, resolve } from "node:path";
185729
+ import { dirname as dirname4, join as join2, resolve as resolve3 } from "node:path";
185386
185730
  function hashConfigSource(source) {
185387
- return createHash("sha256").update(source).digest("hex");
185731
+ return createHash2("sha256").update(source).digest("hex");
185388
185732
  }
185389
185733
  function defaultTrustedConfigStorePath(env = process.env) {
185390
- return env.KYOSO_TRUST_STORE_PATH ? resolve(env.KYOSO_TRUST_STORE_PATH) : join(env.HOME ? resolve(env.HOME) : homedir(), ".kyoso", "trusted-configs.json");
185734
+ return env.KYOSO_TRUST_STORE_PATH ? resolve3(env.KYOSO_TRUST_STORE_PATH) : join2(env.HOME ? resolve3(env.HOME) : homedir(), ".kyoso", "trusted-configs.json");
185391
185735
  }
185392
185736
  async function isTrustedConfig(storePath, configPath, configHash) {
185393
185737
  const store = await readTrustedConfigStore(storePath);
@@ -185396,7 +185740,7 @@ async function isTrustedConfig(storePath, configPath, configHash) {
185396
185740
  async function trustConfig(storePath, configPath, configHash) {
185397
185741
  const store = await readTrustedConfigStore(storePath);
185398
185742
  store[configPath] = configHash;
185399
- await mkdir2(dirname3(storePath), { recursive: true });
185743
+ await mkdir3(dirname4(storePath), { recursive: true });
185400
185744
  await writeFile2(storePath, `${JSON.stringify(store, null, 2)}
185401
185745
  `, {
185402
185746
  encoding: "utf8",
@@ -185408,7 +185752,7 @@ async function readTrustedConfigStore(storePath) {
185408
185752
  try {
185409
185753
  parsed = JSON.parse(await readFile3(storePath, "utf8"));
185410
185754
  } catch (error51) {
185411
- if (isMissingPathError(error51))
185755
+ if (isMissingPathError2(error51))
185412
185756
  return {};
185413
185757
  if (error51 instanceof SyntaxError)
185414
185758
  return {};
@@ -185426,7 +185770,7 @@ async function readTrustedConfigStore(storePath) {
185426
185770
  function isRecord2(value) {
185427
185771
  return typeof value === "object" && value !== null && !Array.isArray(value);
185428
185772
  }
185429
- function isMissingPathError(error51) {
185773
+ function isMissingPathError2(error51) {
185430
185774
  return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
185431
185775
  }
185432
185776
 
@@ -185457,7 +185801,7 @@ async function loadConfig(options = {}) {
185457
185801
  sources.push({ path: globalConfigPath, layer: "global_toml" });
185458
185802
  }
185459
185803
  if (options.configPath) {
185460
- const explicitConfigPath = resolve2(cwd, options.configPath);
185804
+ const explicitConfigPath = resolve4(cwd, options.configPath);
185461
185805
  if (!await exists2(explicitConfigPath)) {
185462
185806
  throw new Error(`Config file not found: ${explicitConfigPath} (from --config)`);
185463
185807
  }
@@ -185474,8 +185818,8 @@ async function loadConfig(options = {}) {
185474
185818
  sources.push(loaded.source);
185475
185819
  warnings.push(...loaded.warnings);
185476
185820
  } else {
185477
- const projectTomlPath = resolve2(cwd, "kyoso.toml");
185478
- const projectTsPath = resolve2(cwd, "kyoso.config.ts");
185821
+ const projectTomlPath = resolve4(cwd, "kyoso.toml");
185822
+ const projectTsPath = resolve4(cwd, "kyoso.config.ts");
185479
185823
  const hasProjectToml = await exists2(projectTomlPath);
185480
185824
  const hasProjectTs = await exists2(projectTsPath);
185481
185825
  if (hasProjectToml) {
@@ -185511,8 +185855,8 @@ async function loadConfig(options = {}) {
185511
185855
  };
185512
185856
  }
185513
185857
  function resolveGlobalTomlConfigPath(env = process.env) {
185514
- const configHome = env.XDG_CONFIG_HOME ? resolve2(env.XDG_CONFIG_HOME) : join2(env.HOME ? resolve2(env.HOME) : homedir2(), ".config");
185515
- return join2(configHome, "kyoso", "config.toml");
185858
+ const configHome = env.XDG_CONFIG_HOME ? resolve4(env.XDG_CONFIG_HOME) : join3(env.HOME ? resolve4(env.HOME) : homedir2(), ".config");
185859
+ return join3(configHome, "kyoso", "config.toml");
185516
185860
  }
185517
185861
  function collectGlobalConfigWarnings(configPath, config2) {
185518
185862
  const unknownSettings = flattenLeaves(config2).map((leaf) => leaf.path).filter((path) => !isKnownGlobalConfigPath(path)).map((path) => ({
@@ -185947,23 +186291,1174 @@ function hasEnv(env, key) {
185947
186291
  return typeof env[key] === "string" && env[key].trim().length > 0;
185948
186292
  }
185949
186293
 
185950
- // src/cli/setup.ts
186294
+ // src/cli/codexPluginDetector.ts
185951
186295
  import { spawnSync } from "node:child_process";
185952
- import { existsSync, readFileSync } from "node:fs";
185953
- import { cp, mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
186296
+
186297
+ // src/cli/pluginRuntimeContract.ts
186298
+ var PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION = 1;
186299
+ var MINIMUM_SUPPORTED_CODEX_VERSION = "0.144.0-alpha.4";
186300
+ var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
186301
+ distribution: {
186302
+ pluginVersion: "0.1.0",
186303
+ mcpCommand: "npx",
186304
+ mcpPackagePin: "@kyo-so/cli@0.8.0"
186305
+ },
186306
+ marketplace: {
186307
+ name: "kyoso",
186308
+ listed: true,
186309
+ pluginId: "kyoso@kyoso",
186310
+ selector: "kyoso@kyoso",
186311
+ installPolicy: "AVAILABLE",
186312
+ authPolicy: "ON_USE"
186313
+ },
186314
+ transitions: {
186315
+ beforeAdd: { installed: false, enabled: false },
186316
+ afterAdd: { installed: true, enabled: true },
186317
+ disabled: { installed: true, enabled: false },
186318
+ afterRemove: { installed: false, enabled: false },
186319
+ installPluginId: "kyoso@kyoso",
186320
+ removePluginId: "kyoso@kyoso"
186321
+ },
186322
+ mcp: {
186323
+ default: {
186324
+ enabled: true,
186325
+ command: "$NODE",
186326
+ args: ["$PROBE_SERVER", "$OBSERVATION"],
186327
+ envVars: [
186328
+ "OPENAI_API_KEY",
186329
+ "CODEX_API_KEY",
186330
+ "CODEX_HOME",
186331
+ "CODEX_ACCESS_TOKEN",
186332
+ "ANTHROPIC_API_KEY",
186333
+ "CLAUDE_CODE_OAUTH_TOKEN"
186334
+ ],
186335
+ cwd: null,
186336
+ startupTimeoutSec: 20,
186337
+ toolTimeoutSec: 360
186338
+ },
186339
+ pluginOverride: {
186340
+ enabled: false,
186341
+ command: "$NODE",
186342
+ args: ["$PROBE_SERVER", "$OBSERVATION"],
186343
+ envVars: [
186344
+ "OPENAI_API_KEY",
186345
+ "CODEX_API_KEY",
186346
+ "CODEX_HOME",
186347
+ "CODEX_ACCESS_TOKEN",
186348
+ "ANTHROPIC_API_KEY",
186349
+ "CLAUDE_CODE_OAUTH_TOKEN"
186350
+ ],
186351
+ cwd: null,
186352
+ startupTimeoutSec: 20,
186353
+ toolTimeoutSec: 360
186354
+ },
186355
+ manualOverride: {
186356
+ enabled: true,
186357
+ command: "manual-kyoso",
186358
+ args: ["mcp"],
186359
+ envVars: [],
186360
+ cwd: null,
186361
+ startupTimeoutSec: null,
186362
+ toolTimeoutSec: null
186363
+ }
186364
+ },
186365
+ appServer: {
186366
+ default: {
186367
+ serverFound: true,
186368
+ toolNames: ["probe_environment"],
186369
+ authStatus: "unsupported",
186370
+ skillFound: false,
186371
+ skillEnabled: null,
186372
+ skillHasKyosoMcpDependency: false
186373
+ },
186374
+ pluginOverride: {
186375
+ serverFound: true,
186376
+ toolNames: [],
186377
+ authStatus: "unsupported",
186378
+ skillFound: false,
186379
+ skillEnabled: null,
186380
+ skillHasKyosoMcpDependency: false,
186381
+ mcpObservationWritten: false
186382
+ }
186383
+ },
186384
+ environment: {
186385
+ cwdIsWorkspace: true,
186386
+ homeInherited: true,
186387
+ codexHomeForwarded: true,
186388
+ tempDirectoryInherited: true,
186389
+ pathInherited: true,
186390
+ allowlistedEnvForwarded: {
186391
+ OPENAI_API_KEY: true,
186392
+ CODEX_API_KEY: true,
186393
+ CODEX_ACCESS_TOKEN: true,
186394
+ ANTHROPIC_API_KEY: true,
186395
+ CLAUDE_CODE_OAUTH_TOKEN: true
186396
+ },
186397
+ deniedSentinelForwarded: false
186398
+ },
186399
+ isolation: {
186400
+ distinctHomeAndCodexHome: true,
186401
+ distinctWorkspace: true
186402
+ }
186403
+ };
186404
+ var PLUGIN_LIST_JSON_SCHEMA = {
186405
+ collections: ["installed", "available"],
186406
+ entry: {
186407
+ pluginId: "kyoso@kyoso",
186408
+ requiredFields: ["pluginId", "installed", "enabled"]
186409
+ }
186410
+ };
186411
+ var pluginRuntimeContract = {
186412
+ schemaVersion: PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION,
186413
+ minimumSupportedCodexVersion: MINIMUM_SUPPORTED_CODEX_VERSION,
186414
+ pluginId: "kyoso@kyoso",
186415
+ pluginListTopLevelKeys: PLUGIN_LIST_JSON_SCHEMA.collections,
186416
+ expectedContract: PLUGIN_RUNTIME_EXPECTED_CONTRACT
186417
+ };
186418
+
186419
+ // src/cli/codexPluginDetector.ts
186420
+ var CODEX_PLUGIN_INSPECTION_TIMEOUT_MS = 5000;
186421
+ var MAX_CODEX_INSPECTION_TIMEOUT_MS = 1e4;
186422
+ var CODEX_INSPECTION_MAX_BUFFER_BYTES = 1024 * 1024;
186423
+ function inspectCodexPlugin(options) {
186424
+ const input2 = normalizeOptions(options);
186425
+ const versionResult = input2.runCodex(commandFor(input2, ["--version"]));
186426
+ if (versionResult.kind !== "completed") {
186427
+ return unsupported("version", failureReason(versionResult));
186428
+ }
186429
+ const codexVersion = parseCodexVersion(versionResult.stdout);
186430
+ if (!codexVersion)
186431
+ return unsupported("version", "invalid_output");
186432
+ if (compareSemver(codexVersion, MINIMUM_SUPPORTED_CODEX_VERSION) < 0) {
186433
+ return unsupported("version", "unsupported_version");
186434
+ }
186435
+ const pluginListResult = input2.runCodex(commandFor(input2, ["plugin", "list", "--json"]));
186436
+ if (pluginListResult.kind !== "completed") {
186437
+ return unsupported("plugin_list", failureReason(pluginListResult));
186438
+ }
186439
+ const parsed = parseJson(pluginListResult.stdout);
186440
+ if (parsed === undefined)
186441
+ return unsupported("plugin_list", "invalid_output");
186442
+ const plugin = parsePluginList(parsed);
186443
+ if (!plugin)
186444
+ return unsupported("plugin_list", "unknown_schema");
186445
+ return { status: "supported", codexVersion, plugin };
186446
+ }
186447
+ function inspectCodexMcpList(options) {
186448
+ const input2 = normalizeOptions(options);
186449
+ const result = input2.runCodex(commandFor(input2, ["mcp", "list", "--json"]));
186450
+ if (result.kind !== "completed") {
186451
+ return unsupported("mcp_list", failureReason(result));
186452
+ }
186453
+ const parsed = parseJson(result.stdout);
186454
+ if (parsed === undefined)
186455
+ return unsupported("mcp_list", "invalid_output");
186456
+ const kyoso = parseMcpList(parsed);
186457
+ if (!kyoso)
186458
+ return unsupported("mcp_list", "unknown_schema");
186459
+ return { status: "supported", kyoso };
186460
+ }
186461
+ function formatCodexInspectionFailure(failure) {
186462
+ const operation = {
186463
+ version: "Codex version",
186464
+ plugin_list: "Codex Plugin list",
186465
+ mcp_list: "Codex MCP list"
186466
+ }[failure.operation];
186467
+ const reason = {
186468
+ unavailable: "is unavailable",
186469
+ timeout: "timed out",
186470
+ command_failed: "could not be inspected",
186471
+ invalid_output: "returned unsupported output",
186472
+ unknown_schema: "returned an unsupported schema",
186473
+ unsupported_version: "is below the supported version"
186474
+ }[failure.reason];
186475
+ return `${operation} ${reason}.`;
186476
+ }
186477
+ function normalizeOptions(options) {
186478
+ return {
186479
+ cwd: options.cwd,
186480
+ env: options.env ?? process.env,
186481
+ timeoutMs: boundedTimeout(options.timeoutMs),
186482
+ runCodex: options.runCodex ?? runCodex
186483
+ };
186484
+ }
186485
+ function boundedTimeout(timeoutMs) {
186486
+ if (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
186487
+ return CODEX_PLUGIN_INSPECTION_TIMEOUT_MS;
186488
+ }
186489
+ return Math.min(timeoutMs, MAX_CODEX_INSPECTION_TIMEOUT_MS);
186490
+ }
186491
+ function commandFor(options, args) {
186492
+ return {
186493
+ args,
186494
+ cwd: options.cwd,
186495
+ env: options.env,
186496
+ timeoutMs: options.timeoutMs
186497
+ };
186498
+ }
186499
+ function runCodex(command) {
186500
+ const result = spawnSync("codex", command.args, {
186501
+ cwd: command.cwd,
186502
+ env: command.env,
186503
+ encoding: "utf8",
186504
+ timeout: command.timeoutMs,
186505
+ maxBuffer: CODEX_INSPECTION_MAX_BUFFER_BYTES,
186506
+ windowsHide: true
186507
+ });
186508
+ const errorCode = commandErrorCode(result.error);
186509
+ if (errorCode === "ENOENT")
186510
+ return { kind: "unavailable" };
186511
+ if (errorCode === "ETIMEDOUT")
186512
+ return { kind: "timeout" };
186513
+ if (result.error || result.status === null || result.status !== 0) {
186514
+ return { kind: "failed" };
186515
+ }
186516
+ return {
186517
+ kind: "completed",
186518
+ exitCode: result.status,
186519
+ stdout: String(result.stdout ?? "")
186520
+ };
186521
+ }
186522
+ function commandErrorCode(error51) {
186523
+ if (typeof error51 !== "object" || error51 === null || !("code" in error51) || typeof error51.code !== "string") {
186524
+ return;
186525
+ }
186526
+ return error51.code;
186527
+ }
186528
+ function failureReason(result) {
186529
+ if (result.kind === "unavailable")
186530
+ return "unavailable";
186531
+ if (result.kind === "timeout")
186532
+ return "timeout";
186533
+ return "command_failed";
186534
+ }
186535
+ function unsupported(operation, reason) {
186536
+ return { status: "unsupported", failure: { operation, reason } };
186537
+ }
186538
+ function parseJson(value) {
186539
+ try {
186540
+ return JSON.parse(value);
186541
+ } catch {
186542
+ return;
186543
+ }
186544
+ }
186545
+ function parsePluginList(value) {
186546
+ if (!isRecord5(value))
186547
+ return;
186548
+ const allowedKeys = new Set(PLUGIN_LIST_JSON_SCHEMA.collections);
186549
+ if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
186550
+ return;
186551
+ }
186552
+ const installed = parsePluginEntries(value.installed);
186553
+ const available = parsePluginEntries(value.available);
186554
+ if (!installed || !available)
186555
+ return;
186556
+ const matches = [...installed, ...available].filter((entry2) => entry2.pluginId === PLUGIN_RUNTIME_EXPECTED_CONTRACT.marketplace.pluginId);
186557
+ if (matches.length > 1)
186558
+ return;
186559
+ if (matches.length === 0) {
186560
+ return pluginState(false, false);
186561
+ }
186562
+ const entry = matches[0];
186563
+ if (!entry.installed && entry.enabled)
186564
+ return;
186565
+ return pluginState(entry.installed, entry.enabled);
186566
+ }
186567
+ function parsePluginEntries(value) {
186568
+ if (value === undefined)
186569
+ return [];
186570
+ if (!Array.isArray(value))
186571
+ return;
186572
+ const entries = [];
186573
+ for (const item of value) {
186574
+ if (!isRecord5(item))
186575
+ return;
186576
+ if (typeof item.pluginId !== "string" || typeof item.installed !== "boolean" || typeof item.enabled !== "boolean") {
186577
+ return;
186578
+ }
186579
+ entries.push({
186580
+ pluginId: item.pluginId,
186581
+ installed: item.installed,
186582
+ enabled: item.enabled
186583
+ });
186584
+ }
186585
+ return entries;
186586
+ }
186587
+ function pluginState(installed, enabled) {
186588
+ return {
186589
+ pluginId: PLUGIN_RUNTIME_EXPECTED_CONTRACT.marketplace.pluginId,
186590
+ installed,
186591
+ enabled,
186592
+ state: installed ? enabled ? "enabled" : "disabled" : "not_installed"
186593
+ };
186594
+ }
186595
+ function parseMcpList(value) {
186596
+ if (!Array.isArray(value))
186597
+ return;
186598
+ const matches = [];
186599
+ for (const item of value) {
186600
+ if (!isRecord5(item) || typeof item.name !== "string")
186601
+ return "unknown";
186602
+ if (item.name !== "kyoso")
186603
+ continue;
186604
+ if (typeof item.enabled !== "boolean")
186605
+ return "unknown";
186606
+ matches.push(item.enabled);
186607
+ }
186608
+ if (matches.length === 0)
186609
+ return "missing";
186610
+ if (matches.length > 1)
186611
+ return "unknown";
186612
+ return matches[0] ? "enabled" : "disabled";
186613
+ }
186614
+ function parseCodexVersion(output2) {
186615
+ for (const line of output2.split(/\r?\n/)) {
186616
+ const match = /^codex-cli\s+(\S+)\s*$/.exec(line.trim());
186617
+ const version2 = match?.[1];
186618
+ if (version2 && parseSemver(version2))
186619
+ return version2;
186620
+ }
186621
+ return;
186622
+ }
186623
+ function compareSemver(left, right) {
186624
+ const parsedLeft = parseSemver(left);
186625
+ const parsedRight = parseSemver(right);
186626
+ if (!parsedLeft || !parsedRight)
186627
+ return 0;
186628
+ for (const key of ["major", "minor", "patch"]) {
186629
+ if (parsedLeft[key] !== parsedRight[key]) {
186630
+ return parsedLeft[key] < parsedRight[key] ? -1 : 1;
186631
+ }
186632
+ }
186633
+ return comparePrerelease(parsedLeft.prerelease, parsedRight.prerelease);
186634
+ }
186635
+ function parseSemver(value) {
186636
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
186637
+ if (!match)
186638
+ return;
186639
+ const major = Number(match[1]);
186640
+ const minor = Number(match[2]);
186641
+ const patch = Number(match[3]);
186642
+ if (!Number.isSafeInteger(major) || !Number.isSafeInteger(minor) || !Number.isSafeInteger(patch)) {
186643
+ return;
186644
+ }
186645
+ return {
186646
+ major,
186647
+ minor,
186648
+ patch,
186649
+ prerelease: match[4]?.split(".") ?? []
186650
+ };
186651
+ }
186652
+ function comparePrerelease(left, right) {
186653
+ if (left.length === 0 && right.length === 0)
186654
+ return 0;
186655
+ if (left.length === 0)
186656
+ return 1;
186657
+ if (right.length === 0)
186658
+ return -1;
186659
+ const count = Math.max(left.length, right.length);
186660
+ for (let index = 0;index < count; index += 1) {
186661
+ const leftPart = left[index];
186662
+ const rightPart = right[index];
186663
+ if (leftPart === undefined)
186664
+ return -1;
186665
+ if (rightPart === undefined)
186666
+ return 1;
186667
+ if (leftPart === rightPart)
186668
+ continue;
186669
+ const leftNumeric = /^\d+$/.test(leftPart);
186670
+ const rightNumeric = /^\d+$/.test(rightPart);
186671
+ if (leftNumeric && rightNumeric) {
186672
+ return Number(leftPart) < Number(rightPart) ? -1 : 1;
186673
+ }
186674
+ if (leftNumeric)
186675
+ return -1;
186676
+ if (rightNumeric)
186677
+ return 1;
186678
+ return leftPart < rightPart ? -1 : 1;
186679
+ }
186680
+ return 0;
186681
+ }
186682
+ function isRecord5(value) {
186683
+ return typeof value === "object" && value !== null && !Array.isArray(value);
186684
+ }
186685
+
186686
+ // src/cli/integration.ts
186687
+ import {
186688
+ accessSync,
186689
+ constants,
186690
+ existsSync,
186691
+ readFileSync,
186692
+ realpathSync
186693
+ } from "node:fs";
186694
+ import { tmpdir } from "node:os";
186695
+ import {
186696
+ delimiter,
186697
+ dirname as dirname5,
186698
+ extname as extname4,
186699
+ isAbsolute as isAbsolute2,
186700
+ join as join4,
186701
+ relative,
186702
+ resolve as resolve5
186703
+ } from "node:path";
186704
+ function detectCli(options) {
186705
+ const env = options.env ?? process.env;
186706
+ const platform = options.platform ?? process.platform;
186707
+ return {
186708
+ kyoso: detectInstalledKyoso(options.cwd, env, platform),
186709
+ npx: commandExists("npx", env, platform),
186710
+ bunx: commandExists("bunx", env, platform)
186711
+ };
186712
+ }
186713
+ function determineNonPluginIntegration(options) {
186714
+ const warnings = manualMcpWarnings(options.manualMcpStatus);
186715
+ if (options.manualMcpStatus === "unknown") {
186716
+ return { mode: "unknown", warnings };
186717
+ }
186718
+ if (options.manualMcpStatus === "enabled") {
186719
+ return {
186720
+ mode: options.hasSkill ? "manual-mcp" : "mcp-only",
186721
+ warnings
186722
+ };
186723
+ }
186724
+ const cliWarnings = cliIdentityWarnings(options.cli.kyoso);
186725
+ warnings.push(...cliWarnings);
186726
+ if (options.hasSkill) {
186727
+ if (options.cli.kyoso.kind === "installed") {
186728
+ return { mode: "cli-skill", warnings };
186729
+ }
186730
+ if (options.cli.npx || options.cli.bunx) {
186731
+ warnings.push("Package-runner fallback may require network access and can drift between versions.");
186732
+ return { mode: "skill-on-demand", warnings };
186733
+ }
186734
+ if (options.cli.kyoso.kind === "unknown") {
186735
+ return { mode: "unknown", warnings };
186736
+ }
186737
+ warnings.push("The Skill is installed, but no supported Kyoso CLI execution path was found.");
186738
+ return { mode: "missing", warnings };
186739
+ }
186740
+ if (options.cli.kyoso.kind === "installed") {
186741
+ return { mode: "cli-only", warnings };
186742
+ }
186743
+ if (options.cli.kyoso.kind === "unknown") {
186744
+ return { mode: "unknown", warnings };
186745
+ }
186746
+ return { mode: "missing", warnings };
186747
+ }
186748
+ function formatCliAvailability(cli) {
186749
+ if (cli.kind === "installed") {
186750
+ return `installed @kyo-so/cli ${cli.version} (${cli.scope})`;
186751
+ }
186752
+ if (cli.kind === "transient") {
186753
+ return "temporary runner (not treated as an installed CLI)";
186754
+ }
186755
+ if (cli.kind === "unknown") {
186756
+ return "unrecognized PATH entry (not treated as an installed CLI)";
186757
+ }
186758
+ return "missing";
186759
+ }
186760
+ function detectInstalledKyoso(cwd, env, platform) {
186761
+ const executable = resolveCommand("kyoso", env, platform);
186762
+ if (!executable)
186763
+ return { kind: "missing" };
186764
+ let realExecutable;
186765
+ try {
186766
+ realExecutable = realpathSync(executable);
186767
+ } catch {
186768
+ return { kind: "unknown" };
186769
+ }
186770
+ if (isTransientPath(realExecutable, cwd))
186771
+ return { kind: "transient" };
186772
+ const packageInfo = findKyosoPackage(realExecutable);
186773
+ if (!packageInfo)
186774
+ return { kind: "unknown" };
186775
+ return {
186776
+ kind: "installed",
186777
+ version: packageInfo.version,
186778
+ scope: isWithin(packageInfo.directory, realPathOrResolved(cwd)) ? "project" : "global"
186779
+ };
186780
+ }
186781
+ function findKyosoPackage(executable) {
186782
+ let directory = dirname5(executable);
186783
+ for (;; ) {
186784
+ const packagePath = join4(directory, "package.json");
186785
+ if (existsSync(packagePath)) {
186786
+ try {
186787
+ const parsed = JSON.parse(readFileSync(packagePath, "utf8"));
186788
+ if (isRecord6(parsed) && parsed.name === "@kyo-so/cli" && typeof parsed.version === "string" && parsed.version.trim().length > 0) {
186789
+ return { directory, version: parsed.version };
186790
+ }
186791
+ } catch {}
186792
+ }
186793
+ const parent = dirname5(directory);
186794
+ if (parent === directory)
186795
+ return;
186796
+ directory = parent;
186797
+ }
186798
+ }
186799
+ function isTransientPath(path, cwd) {
186800
+ if (path.split(/[\\/]/).includes("_npx"))
186801
+ return true;
186802
+ const temporaryRoot = realPathOrResolved(tmpdir());
186803
+ return isWithin(path, temporaryRoot) && !isWithin(path, realPathOrResolved(cwd));
186804
+ }
186805
+ function realPathOrResolved(path) {
186806
+ try {
186807
+ return realpathSync(path);
186808
+ } catch {
186809
+ return resolve5(path);
186810
+ }
186811
+ }
186812
+ function manualMcpWarnings(status) {
186813
+ if (status === "disabled")
186814
+ return ["Manual MCP registration is disabled."];
186815
+ if (status === "unknown") {
186816
+ return [
186817
+ "Manual MCP registration could not be safely classified from its configuration."
186818
+ ];
186819
+ }
186820
+ return [];
186821
+ }
186822
+ function cliIdentityWarnings(cli) {
186823
+ if (cli.kind === "transient") {
186824
+ return [
186825
+ "A temporary Kyoso runner was found on PATH and is not treated as an installed CLI."
186826
+ ];
186827
+ }
186828
+ if (cli.kind === "unknown") {
186829
+ return [
186830
+ "A PATH entry named kyoso does not identify itself as @kyo-so/cli and is not treated as an installed CLI."
186831
+ ];
186832
+ }
186833
+ return [];
186834
+ }
186835
+ function commandExists(command, env, platform) {
186836
+ return resolveCommand(command, env, platform) !== undefined;
186837
+ }
186838
+ function resolveCommand(command, env, platform) {
186839
+ for (const path of env.PATH?.split(delimiter) ?? []) {
186840
+ if (path.length === 0)
186841
+ continue;
186842
+ for (const name of commandNames(command, env, platform)) {
186843
+ const candidate = join4(path, name);
186844
+ try {
186845
+ accessSync(candidate, constants.X_OK);
186846
+ return candidate;
186847
+ } catch {}
186848
+ }
186849
+ }
186850
+ return;
186851
+ }
186852
+ function commandNames(command, env, platform) {
186853
+ if (platform !== "win32" || extname4(command).length > 0)
186854
+ return [command];
186855
+ const names = [command];
186856
+ const seen = new Set([command.toLowerCase()]);
186857
+ for (const extension of env.PATHEXT?.split(";") ?? []) {
186858
+ const normalized = extension.trim();
186859
+ if (!normalized.startsWith(".") || /[\\/]/.test(normalized))
186860
+ continue;
186861
+ const candidate = `${command}${normalized}`;
186862
+ const key = candidate.toLowerCase();
186863
+ if (seen.has(key))
186864
+ continue;
186865
+ seen.add(key);
186866
+ names.push(candidate);
186867
+ }
186868
+ return names;
186869
+ }
186870
+ function isWithin(path, parent) {
186871
+ const relativePath = relative(resolve5(parent), resolve5(path));
186872
+ return relativePath === "" || !relativePath.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && relativePath !== ".." && !isAbsolute2(relativePath);
186873
+ }
186874
+ function isRecord6(value) {
186875
+ return typeof value === "object" && value !== null && !Array.isArray(value);
186876
+ }
186877
+
186878
+ // src/cli/setup.ts
186879
+ import { spawnSync as spawnSync2 } from "node:child_process";
186880
+ import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "node:fs";
186881
+ import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
185954
186882
  import { homedir as homedir3 } from "node:os";
185955
- import { delimiter, dirname as dirname4, join as join3 } from "node:path";
186883
+ import { delimiter as delimiter2, dirname as dirname7, join as join6, resolve as resolve7 } from "node:path";
185956
186884
  import { fileURLToPath } from "node:url";
186885
+
186886
+ // src/cli/skillInstall.ts
186887
+ import { createHash as createHash3, randomUUID } from "node:crypto";
186888
+ import {
186889
+ copyFile,
186890
+ link,
186891
+ lstat as lstat2,
186892
+ mkdir as mkdir4,
186893
+ mkdtemp,
186894
+ readFile as readFile5,
186895
+ readdir,
186896
+ realpath as realpath2,
186897
+ rename,
186898
+ rm,
186899
+ writeFile as writeFile3
186900
+ } from "node:fs/promises";
186901
+ import {
186902
+ basename as basename2,
186903
+ dirname as dirname6,
186904
+ isAbsolute as isAbsolute3,
186905
+ join as join5,
186906
+ relative as relative2,
186907
+ resolve as resolve6,
186908
+ sep as sep3
186909
+ } from "node:path";
186910
+
186911
+ // src/cli/knownSkillDigests.ts
186912
+ var CURRENT_SKILL_DIGEST = "sha256:98e715396ee3e1994c29ab49bc489197f242091c8ac0e06e182c0eccd5ab277e";
186913
+ var KNOWN_SKILL_DIGESTS_BY_VERSION = {
186914
+ "0.8.0": [
186915
+ {
186916
+ digest: "sha256:b16ea3f8141a01399b96dee650365d99df2b8c5fc99184d9cb22d5d72c106fd8",
186917
+ kind: "historical"
186918
+ }
186919
+ ]
186920
+ };
186921
+ function knownSkillDigest(digest) {
186922
+ if (digest === CURRENT_SKILL_DIGEST) {
186923
+ return { version: KYOSO_VERSION, kind: "current" };
186924
+ }
186925
+ for (const [version2, entries] of Object.entries(KNOWN_SKILL_DIGESTS_BY_VERSION)) {
186926
+ const entry = entries.find((candidate) => candidate.digest === digest);
186927
+ if (entry)
186928
+ return { version: version2, kind: entry.kind };
186929
+ }
186930
+ return;
186931
+ }
186932
+
186933
+ // src/cli/skillInstall.ts
186934
+ var SKILL_INSTALL_MARKER = ".kyoso-install.json";
186935
+ var SKILL_INSTALL_BACKUP = ".kyoso-review.backup";
186936
+ var SKILL_INSTALL_TRANSACTION = ".kyoso-review.install-transaction.json";
186937
+ async function ensureManagedSkill(options) {
186938
+ const sourceDigest = await hashSkillDirectory(options.sourceDir);
186939
+ if (sourceDigest !== CURRENT_SKILL_DIGEST) {
186940
+ throw new Error(`Bundled kyoso-review digest is ${sourceDigest}; update knownSkillDigests.ts before distributing it.`);
186941
+ }
186942
+ const safeDestination = await resolveSafeDestination(options.trustedRoot, options.destinationDir);
186943
+ const destinationDir = safeDestination.destinationDir;
186944
+ const recovery = await recoverInterruptedReplacement({
186945
+ destinationDir,
186946
+ trustedRoot: safeDestination.realRoot,
186947
+ write: options.write
186948
+ });
186949
+ if (recovery.status !== "none") {
186950
+ return {
186951
+ status: recovery.status,
186952
+ path: destinationDir,
186953
+ detail: recovery.detail
186954
+ };
186955
+ }
186956
+ const destinationStat = await optionalLstat2(destinationDir);
186957
+ if (!destinationStat) {
186958
+ const detail2 = copyDetail(options.sourceDir, destinationDir, "create");
186959
+ if (!options.write) {
186960
+ return { status: "dry-run", path: destinationDir, detail: detail2 };
186961
+ }
186962
+ await replaceSkillDirectory({
186963
+ sourceDir: options.sourceDir,
186964
+ destinationDir,
186965
+ trustedRoot: safeDestination.realRoot,
186966
+ destinationExists: false
186967
+ });
186968
+ return { status: "created", path: destinationDir, detail: detail2 };
186969
+ }
186970
+ if (!destinationStat.isDirectory()) {
186971
+ if (!options.force) {
186972
+ return conflictResult(destinationDir, "destination exists and is not a directory");
186973
+ }
186974
+ const detail2 = copyDetail(options.sourceDir, destinationDir, "force replace");
186975
+ if (!options.write) {
186976
+ return { status: "dry-run", path: destinationDir, detail: detail2 };
186977
+ }
186978
+ await replaceSkillDirectory({
186979
+ sourceDir: options.sourceDir,
186980
+ destinationDir,
186981
+ trustedRoot: safeDestination.realRoot,
186982
+ destinationExists: true
186983
+ });
186984
+ return { status: "updated", path: destinationDir, detail: detail2 };
186985
+ }
186986
+ if (await realpath2(options.sourceDir) === await realpath2(destinationDir)) {
186987
+ return {
186988
+ status: "skipped",
186989
+ path: destinationDir,
186990
+ detail: "canonical source is already the destination"
186991
+ };
186992
+ }
186993
+ const actualDigest = await hashSkillDirectory(destinationDir);
186994
+ const marker = await readInstallMarker(destinationDir);
186995
+ if (options.force) {
186996
+ const detail2 = [
186997
+ `force replace ${destinationDir}`,
186998
+ `from ${actualDigest}`,
186999
+ `to ${CURRENT_SKILL_DIGEST}`
187000
+ ].join(`
187001
+ `);
187002
+ if (!options.write) {
187003
+ return { status: "dry-run", path: destinationDir, detail: detail2 };
187004
+ }
187005
+ await replaceSkillDirectory({
187006
+ sourceDir: options.sourceDir,
187007
+ destinationDir,
187008
+ trustedRoot: safeDestination.realRoot,
187009
+ destinationExists: true
187010
+ });
187011
+ return { status: "updated", path: destinationDir, detail: detail2 };
187012
+ }
187013
+ if (marker.kind === "invalid") {
187014
+ return conflictResult(destinationDir, marker.reason);
187015
+ }
187016
+ if (marker.kind === "valid") {
187017
+ if (marker.value.digest !== actualDigest) {
187018
+ return conflictResult(destinationDir, `managed digest ${marker.value.digest} does not match installed digest ${actualDigest}`);
187019
+ }
187020
+ if (actualDigest === CURRENT_SKILL_DIGEST && marker.value.cliVersion === KYOSO_VERSION) {
187021
+ return {
187022
+ status: "skipped",
187023
+ path: destinationDir,
187024
+ detail: `managed skill is current (${CURRENT_SKILL_DIGEST})`
187025
+ };
187026
+ }
187027
+ if (actualDigest === CURRENT_SKILL_DIGEST) {
187028
+ const detail3 = `refresh install marker to CLI ${KYOSO_VERSION}`;
187029
+ if (!options.write) {
187030
+ return { status: "dry-run", path: destinationDir, detail: detail3 };
187031
+ }
187032
+ await writeInstallMarker(destinationDir);
187033
+ return { status: "updated", path: destinationDir, detail: detail3 };
187034
+ }
187035
+ const detail2 = [
187036
+ `update managed skill from ${actualDigest}`,
187037
+ `to ${CURRENT_SKILL_DIGEST}`
187038
+ ].join(`
187039
+ `);
187040
+ if (!options.write) {
187041
+ return { status: "dry-run", path: destinationDir, detail: detail2 };
187042
+ }
187043
+ await replaceSkillDirectory({
187044
+ sourceDir: options.sourceDir,
187045
+ destinationDir,
187046
+ trustedRoot: safeDestination.realRoot,
187047
+ destinationExists: true
187048
+ });
187049
+ return { status: "updated", path: destinationDir, detail: detail2 };
187050
+ }
187051
+ const known = knownSkillDigest(actualDigest);
187052
+ if (!known) {
187053
+ return conflictResult(destinationDir, `unmanaged skill digest ${actualDigest} is not recognized`);
187054
+ }
187055
+ if (actualDigest === CURRENT_SKILL_DIGEST) {
187056
+ const detail2 = `adopt existing ${known.version} skill and write ${SKILL_INSTALL_MARKER}`;
187057
+ if (!options.write) {
187058
+ return { status: "dry-run", path: destinationDir, detail: detail2 };
187059
+ }
187060
+ await writeInstallMarker(destinationDir);
187061
+ return { status: "updated", path: destinationDir, detail: detail2 };
187062
+ }
187063
+ const detail = [
187064
+ `adopt known ${known.version} ${known.kind} skill (${actualDigest})`,
187065
+ `update to ${CURRENT_SKILL_DIGEST}`
187066
+ ].join(`
187067
+ `);
187068
+ if (!options.write) {
187069
+ return { status: "dry-run", path: destinationDir, detail };
187070
+ }
187071
+ await replaceSkillDirectory({
187072
+ sourceDir: options.sourceDir,
187073
+ destinationDir,
187074
+ trustedRoot: safeDestination.realRoot,
187075
+ destinationExists: true
187076
+ });
187077
+ return { status: "updated", path: destinationDir, detail };
187078
+ }
187079
+ async function hashSkillDirectory(directory) {
187080
+ const rootStat = await lstat2(directory);
187081
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
187082
+ throw new Error(`Skill root must be a regular directory: ${directory}`);
187083
+ }
187084
+ const files = await listRegularFiles(directory);
187085
+ files.sort((left, right) => Buffer.compare(Buffer.from(left.relativePath), Buffer.from(right.relativePath)));
187086
+ const hash2 = createHash3("sha256");
187087
+ for (const file2 of files) {
187088
+ const bytes = await readFile5(file2.absolutePath);
187089
+ hash2.update(file2.relativePath);
187090
+ hash2.update("\x00");
187091
+ hash2.update(String(bytes.byteLength));
187092
+ hash2.update("\x00");
187093
+ hash2.update(bytes);
187094
+ }
187095
+ return `sha256:${hash2.digest("hex")}`;
187096
+ }
187097
+ async function listRegularFiles(directory, prefix = "") {
187098
+ const result = [];
187099
+ const entries = await readdir(directory);
187100
+ for (const name of entries) {
187101
+ const relativePath = prefix.length > 0 ? `${prefix}/${name}` : name;
187102
+ const absolutePath = join5(directory, name);
187103
+ const stat = await lstat2(absolutePath);
187104
+ if (stat.isSymbolicLink()) {
187105
+ throw new Error(`Skill directory contains a symlink: ${relativePath}`);
187106
+ }
187107
+ if (relativePath === SKILL_INSTALL_MARKER) {
187108
+ if (!stat.isFile()) {
187109
+ throw new Error(`${SKILL_INSTALL_MARKER} must be a regular file`);
187110
+ }
187111
+ continue;
187112
+ }
187113
+ if (stat.isDirectory()) {
187114
+ result.push(...await listRegularFiles(absolutePath, relativePath));
187115
+ continue;
187116
+ }
187117
+ if (!stat.isFile()) {
187118
+ throw new Error(`Skill directory contains a non-regular file: ${relativePath}`);
187119
+ }
187120
+ result.push({ relativePath, absolutePath });
187121
+ }
187122
+ return result;
187123
+ }
187124
+ async function resolveSafeDestination(trustedRoot, destinationDir) {
187125
+ const logicalRoot = resolve6(trustedRoot);
187126
+ const logicalDestination = resolve6(destinationDir);
187127
+ const relativeDestination = relative2(logicalRoot, logicalDestination);
187128
+ if (relativeDestination === "" || !isPathWithin(logicalDestination, logicalRoot)) {
187129
+ throw new Error(`Skill destination escapes trusted install root: ${destinationDir}`);
187130
+ }
187131
+ const realRoot = await realpath2(logicalRoot);
187132
+ const safeDestination = resolve6(realRoot, relativeDestination);
187133
+ if (!isPathWithin(safeDestination, realRoot)) {
187134
+ throw new Error(`Skill destination escapes trusted install root: ${destinationDir}`);
187135
+ }
187136
+ await assertExistingSegmentsSafe(realRoot, safeDestination);
187137
+ return { realRoot, destinationDir: safeDestination };
187138
+ }
187139
+ async function assertExistingSegmentsSafe(realRoot, destination) {
187140
+ const segments = relative2(realRoot, destination).split(sep3).filter(Boolean);
187141
+ let current = realRoot;
187142
+ for (const segment of segments) {
187143
+ current = join5(current, segment);
187144
+ const stat = await optionalLstat2(current);
187145
+ if (!stat)
187146
+ return;
187147
+ if (stat.isSymbolicLink()) {
187148
+ throw new Error(`Skill install path contains a symlink: ${current}`);
187149
+ }
187150
+ if (current !== destination && !stat.isDirectory()) {
187151
+ throw new Error(`Skill install parent is not a directory: ${current}`);
187152
+ }
187153
+ }
187154
+ }
187155
+ async function ensureSafeParent(realRoot, destinationDir) {
187156
+ const parent = dirname6(destinationDir);
187157
+ const relativeParent = relative2(realRoot, parent);
187158
+ if (relativeParent === ".." || relativeParent.startsWith(`..${sep3}`) || isAbsolute3(relativeParent)) {
187159
+ throw new Error(`Skill install parent escapes trusted root: ${parent}`);
187160
+ }
187161
+ const segments = relative2(realRoot, parent).split(sep3).filter(Boolean);
187162
+ let current = realRoot;
187163
+ for (const segment of segments) {
187164
+ current = join5(current, segment);
187165
+ let stat = await optionalLstat2(current);
187166
+ if (!stat) {
187167
+ await mkdir4(current);
187168
+ stat = await lstat2(current);
187169
+ }
187170
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
187171
+ throw new Error(`Skill install parent is unsafe: ${current}`);
187172
+ }
187173
+ }
187174
+ }
187175
+ async function replaceSkillDirectory(options) {
187176
+ await ensureSafeParent(options.trustedRoot, options.destinationDir);
187177
+ await assertExistingSegmentsSafe(options.trustedRoot, options.destinationDir);
187178
+ const parent = dirname6(options.destinationDir);
187179
+ const mutationGuard = await captureMutationGuard(parent);
187180
+ const stage = await mkdtemp(join5(parent, ".kyoso-review.stage-"));
187181
+ const backup = join5(parent, SKILL_INSTALL_BACKUP);
187182
+ const transaction = join5(parent, SKILL_INSTALL_TRANSACTION);
187183
+ let backupCreated = false;
187184
+ let transactionCreated = false;
187185
+ try {
187186
+ await copySkillFiles(options.sourceDir, stage);
187187
+ await writeInstallMarker(stage);
187188
+ const stagedDigest = await hashSkillDirectory(stage);
187189
+ if (stagedDigest !== CURRENT_SKILL_DIGEST) {
187190
+ throw new Error(`Staged kyoso-review digest mismatch: ${stagedDigest} != ${CURRENT_SKILL_DIGEST}`);
187191
+ }
187192
+ if (options.destinationExists) {
187193
+ await assertMutationGuard(mutationGuard);
187194
+ if (await optionalLstat2(backup) || await optionalLstat2(transaction)) {
187195
+ throw interruptedReplacementConflict(options.destinationDir, backup);
187196
+ }
187197
+ await writeReplacementTransaction(transaction, options.destinationDir);
187198
+ transactionCreated = true;
187199
+ await assertMutationGuard(mutationGuard);
187200
+ await rename(options.destinationDir, backup);
187201
+ backupCreated = true;
187202
+ await assertMutationGuard(mutationGuard);
187203
+ }
187204
+ try {
187205
+ await assertMutationGuard(mutationGuard);
187206
+ await rename(stage, options.destinationDir);
187207
+ await assertMutationGuard(mutationGuard);
187208
+ } catch (error51) {
187209
+ if (backupCreated && !await optionalLstat2(options.destinationDir)) {
187210
+ await assertMutationGuard(mutationGuard);
187211
+ await rename(backup, options.destinationDir);
187212
+ backupCreated = false;
187213
+ await assertMutationGuard(mutationGuard);
187214
+ await rm(transaction, { force: true });
187215
+ transactionCreated = false;
187216
+ await assertMutationGuard(mutationGuard);
187217
+ }
187218
+ throw error51;
187219
+ }
187220
+ if (backupCreated) {
187221
+ await assertMutationGuard(mutationGuard);
187222
+ await rm(backup, { recursive: true, force: true });
187223
+ backupCreated = false;
187224
+ await assertMutationGuard(mutationGuard);
187225
+ await rm(transaction, { force: true });
187226
+ transactionCreated = false;
187227
+ await assertMutationGuard(mutationGuard);
187228
+ }
187229
+ } finally {
187230
+ await assertMutationGuard(mutationGuard);
187231
+ await rm(stage, { recursive: true, force: true });
187232
+ if (backupCreated && !await optionalLstat2(options.destinationDir)) {
187233
+ await assertMutationGuard(mutationGuard);
187234
+ await rename(backup, options.destinationDir);
187235
+ backupCreated = false;
187236
+ await assertMutationGuard(mutationGuard);
187237
+ }
187238
+ if (transactionCreated && !backupCreated) {
187239
+ await rm(transaction, { force: true });
187240
+ }
187241
+ }
187242
+ }
187243
+ async function captureMutationGuard(parent) {
187244
+ const stat = await lstat2(parent);
187245
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
187246
+ throw new Error(`Skill install parent is unsafe: ${parent}`);
187247
+ }
187248
+ const realParent = await realpath2(parent);
187249
+ if (realParent !== parent) {
187250
+ throw new Error(`Skill install parent changed during setup: ${parent}`);
187251
+ }
187252
+ return {
187253
+ parent,
187254
+ realParent,
187255
+ device: stat.dev,
187256
+ inode: stat.ino
187257
+ };
187258
+ }
187259
+ async function assertMutationGuard(guard) {
187260
+ const stat = await optionalLstat2(guard.parent);
187261
+ if (!stat || stat.isSymbolicLink() || !stat.isDirectory() || stat.dev !== guard.device || stat.ino !== guard.inode || await realpath2(guard.parent) !== guard.realParent) {
187262
+ throw new Error(`Skill install parent changed during setup: ${guard.parent}. No further path mutation was attempted.`);
187263
+ }
187264
+ }
187265
+ async function recoverInterruptedReplacement(options) {
187266
+ const parent = dirname6(options.destinationDir);
187267
+ const backup = join5(parent, SKILL_INSTALL_BACKUP);
187268
+ const transaction = join5(parent, SKILL_INSTALL_TRANSACTION);
187269
+ const backupStat = await optionalLstat2(backup);
187270
+ const transactionStat = await optionalLstat2(transaction);
187271
+ if (!backupStat && !transactionStat)
187272
+ return { status: "none" };
187273
+ if (backupStat?.isSymbolicLink()) {
187274
+ throw new Error(`Interrupted Skill backup is a symlink: ${backup}`);
187275
+ }
187276
+ if (transactionStat?.isSymbolicLink() || !transactionStat?.isFile()) {
187277
+ throw new Error(`Interrupted Skill transaction marker is not a regular file: ${transaction}`);
187278
+ }
187279
+ if (!backupStat) {
187280
+ await assertExistingSegmentsSafe(options.trustedRoot, transaction);
187281
+ } else {
187282
+ await assertExistingSegmentsSafe(options.trustedRoot, backup);
187283
+ }
187284
+ const parsed = await readReplacementTransaction(transaction);
187285
+ if (parsed.destinationName !== basename2(options.destinationDir) || parsed.backupName !== SKILL_INSTALL_BACKUP) {
187286
+ throw new Error(`Interrupted Skill transaction does not match ${options.destinationDir}: ${transaction}`);
187287
+ }
187288
+ const destinationStat = await optionalLstat2(options.destinationDir);
187289
+ if (backupStat && destinationStat) {
187290
+ throw interruptedReplacementConflict(options.destinationDir, backup);
187291
+ }
187292
+ if (!backupStat && !destinationStat) {
187293
+ throw new Error(`Interrupted Skill transaction has neither destination nor backup: ${transaction}`);
187294
+ }
187295
+ if (!options.write) {
187296
+ return {
187297
+ status: "dry-run",
187298
+ detail: backupStat ? `recover interrupted skill replacement from ${SKILL_INSTALL_BACKUP}` : `clean completed Skill replacement marker ${SKILL_INSTALL_TRANSACTION}`
187299
+ };
187300
+ }
187301
+ const mutationGuard = await captureMutationGuard(parent);
187302
+ await assertMutationGuard(mutationGuard);
187303
+ if (backupStat) {
187304
+ await rename(backup, options.destinationDir);
187305
+ }
187306
+ await assertMutationGuard(mutationGuard);
187307
+ await rm(transaction, { force: true });
187308
+ await assertMutationGuard(mutationGuard);
187309
+ return {
187310
+ status: "updated",
187311
+ detail: backupStat ? `recovered interrupted skill replacement from ${SKILL_INSTALL_BACKUP}; rerun setup to apply the requested install` : `cleaned completed Skill replacement marker ${SKILL_INSTALL_TRANSACTION}; rerun setup to verify the installed Skill`
187312
+ };
187313
+ }
187314
+ function interruptedReplacementConflict(destinationDir, backup) {
187315
+ return new Error(`Interrupted Skill replacement is ambiguous because both ${destinationDir} and ${backup} exist. Inspect both directories and remove ${backup} only after preserving any needed files.`);
187316
+ }
187317
+ async function writeReplacementTransaction(transactionPath, destinationDir) {
187318
+ const transaction = {
187319
+ schemaVersion: 1,
187320
+ installer: "@kyo-so/cli",
187321
+ destinationName: basename2(destinationDir),
187322
+ backupName: SKILL_INSTALL_BACKUP
187323
+ };
187324
+ const temporaryPath = `${transactionPath}.tmp-${randomUUID()}`;
187325
+ await writeFile3(temporaryPath, `${JSON.stringify(transaction, null, 2)}
187326
+ `, {
187327
+ encoding: "utf8",
187328
+ flag: "wx"
187329
+ });
187330
+ try {
187331
+ await link(temporaryPath, transactionPath);
187332
+ } finally {
187333
+ await rm(temporaryPath, { force: true });
187334
+ }
187335
+ }
187336
+ async function readReplacementTransaction(transactionPath) {
187337
+ let parsed;
187338
+ try {
187339
+ parsed = JSON.parse(await readFile5(transactionPath, "utf8"));
187340
+ } catch (error51) {
187341
+ throw new Error(`Interrupted Skill transaction is invalid: ${transactionPath}: ${error51 instanceof Error ? error51.message : String(error51)}`);
187342
+ }
187343
+ if (!isReplacementTransaction(parsed)) {
187344
+ throw new Error(`Interrupted Skill transaction has an invalid schema: ${transactionPath}`);
187345
+ }
187346
+ return parsed;
187347
+ }
187348
+ function isReplacementTransaction(value) {
187349
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
187350
+ return false;
187351
+ }
187352
+ const candidate = value;
187353
+ return candidate.schemaVersion === 1 && candidate.installer === "@kyo-so/cli" && typeof candidate.destinationName === "string" && candidate.destinationName.length > 0 && candidate.backupName === SKILL_INSTALL_BACKUP;
187354
+ }
187355
+ async function copySkillFiles(sourceDir, destinationDir) {
187356
+ for (const file2 of await listRegularFiles(sourceDir)) {
187357
+ const destination = join5(destinationDir, ...file2.relativePath.split("/"));
187358
+ await mkdir4(dirname6(destination), { recursive: true });
187359
+ await copyFile(file2.absolutePath, destination);
187360
+ }
187361
+ }
187362
+ async function readInstallMarker(destinationDir) {
187363
+ const markerPath = join5(destinationDir, SKILL_INSTALL_MARKER);
187364
+ const markerStat = await optionalLstat2(markerPath);
187365
+ if (!markerStat)
187366
+ return { kind: "missing" };
187367
+ if (markerStat.isSymbolicLink() || !markerStat.isFile()) {
187368
+ return {
187369
+ kind: "invalid",
187370
+ reason: `${SKILL_INSTALL_MARKER} is not a regular file`
187371
+ };
187372
+ }
187373
+ try {
187374
+ const parsed = JSON.parse(await readFile5(markerPath, "utf8"));
187375
+ if (!isInstallMarker(parsed)) {
187376
+ return {
187377
+ kind: "invalid",
187378
+ reason: `${SKILL_INSTALL_MARKER} has an invalid schema`
187379
+ };
187380
+ }
187381
+ return { kind: "valid", value: parsed };
187382
+ } catch (error51) {
187383
+ return {
187384
+ kind: "invalid",
187385
+ reason: `${SKILL_INSTALL_MARKER} is invalid: ${error51 instanceof Error ? error51.message : String(error51)}`
187386
+ };
187387
+ }
187388
+ }
187389
+ async function writeInstallMarker(destinationDir) {
187390
+ const marker = {
187391
+ schemaVersion: 1,
187392
+ installer: "@kyo-so/cli",
187393
+ cliVersion: KYOSO_VERSION,
187394
+ digest: CURRENT_SKILL_DIGEST
187395
+ };
187396
+ const markerPath = join5(destinationDir, SKILL_INSTALL_MARKER);
187397
+ const temporaryPath = join5(destinationDir, `${SKILL_INSTALL_MARKER}.tmp-${randomUUID()}`);
187398
+ await writeFile3(temporaryPath, `${JSON.stringify(marker, null, 2)}
187399
+ `, {
187400
+ encoding: "utf8",
187401
+ flag: "wx"
187402
+ });
187403
+ try {
187404
+ await rename(temporaryPath, markerPath);
187405
+ } finally {
187406
+ await rm(temporaryPath, { force: true });
187407
+ }
187408
+ }
187409
+ function isInstallMarker(value) {
187410
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
187411
+ return false;
187412
+ }
187413
+ const candidate = value;
187414
+ return candidate.schemaVersion === 1 && candidate.installer === "@kyo-so/cli" && typeof candidate.cliVersion === "string" && candidate.cliVersion.length > 0 && typeof candidate.digest === "string" && /^sha256:[0-9a-f]{64}$/.test(candidate.digest);
187415
+ }
187416
+ function copyDetail(sourceDir, destinationDir, action) {
187417
+ return [
187418
+ `${action} ${sourceDir}`,
187419
+ `to ${destinationDir}`,
187420
+ `digest ${CURRENT_SKILL_DIGEST}`
187421
+ ].join(`
187422
+ `);
187423
+ }
187424
+ function conflictResult(destinationDir, reason) {
187425
+ return {
187426
+ status: "conflict",
187427
+ path: destinationDir,
187428
+ detail: `${reason}
187429
+ Existing skill was not changed. Rerun with --force to replace only this skill directory.`
187430
+ };
187431
+ }
187432
+ async function optionalLstat2(path) {
187433
+ try {
187434
+ return await lstat2(path);
187435
+ } catch (error51) {
187436
+ if (isMissingPathError3(error51))
187437
+ return;
187438
+ throw error51;
187439
+ }
187440
+ }
187441
+ function isMissingPathError3(error51) {
187442
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
187443
+ }
187444
+
187445
+ // src/cli/setup.ts
185957
187446
  async function runSetup(options) {
185958
187447
  const client = parseClient(options.client);
187448
+ validateSkillOnlyOptions(options, client);
185959
187449
  const runner = parseRunner(options.runner);
185960
187450
  const command = options.command ? parseCommandSpec(options.command) : commandForRunner(runner);
187451
+ const env = options.env ?? process.env;
187452
+ const home = resolveUserHome(env);
185961
187453
  const context = {
185962
187454
  cwd: options.cwd,
185963
- home: options.env?.HOME ?? homedir3(),
185964
- env: options.env ?? process.env,
187455
+ home,
187456
+ codexHome: dirname7(resolveCodexConfigPath(env, options.cwd)),
187457
+ env,
185965
187458
  write: options.write,
185966
187459
  scope: options.global ? "global" : "project",
187460
+ skillOnly: options.skillOnly ?? false,
187461
+ force: options.force ?? false,
185967
187462
  mcpCommand: command,
185968
187463
  sourceSkillDir: resolveBundledSkillDir()
185969
187464
  };
@@ -186006,50 +187501,103 @@ function buildClaudeMcpEntry(command) {
186006
187501
  function skillDestination(client, scope, cwd, home) {
186007
187502
  if (client === "codex") {
186008
187503
  const root2 = scope === "global" ? home : cwd;
186009
- return join3(root2, ".agents", "skills", "kyoso-review");
187504
+ return join6(root2, ".agents", "skills", "kyoso-review");
186010
187505
  }
186011
187506
  const root = scope === "global" ? home : cwd;
186012
- return join3(root, ".claude", "skills", "kyoso-review");
187507
+ return join6(root, ".claude", "skills", "kyoso-review");
187508
+ }
187509
+ function resolveUserHome(env = process.env) {
187510
+ return env.HOME ? resolve7(env.HOME) : homedir3();
187511
+ }
187512
+ function resolveCodexConfigPath(env = process.env, cwd = process.cwd()) {
187513
+ const codexHome = env.CODEX_HOME ? resolve7(cwd, env.CODEX_HOME) : join6(resolveUserHome(env), ".codex");
187514
+ return join6(codexHome, "config.toml");
187515
+ }
187516
+ function detectCodexPluginMcpOverride(options) {
187517
+ const env = options.env ?? process.env;
187518
+ const path = resolveCodexConfigPath(env, options.cwd);
187519
+ if (!existsSync2(path))
187520
+ return { status: "missing", path };
187521
+ try {
187522
+ const parsed = parse5(readTextSync(path));
187523
+ if (hasUnprobedProjectIntegrationOverride(parsed, options.cwd, resolveUserHome(env))) {
187524
+ return { status: "unknown", path };
187525
+ }
187526
+ return {
187527
+ status: nestedMcpEntryStatus(parsed, [
187528
+ "plugins",
187529
+ "kyoso@kyoso",
187530
+ "mcp_servers",
187531
+ "kyoso"
187532
+ ]),
187533
+ path
187534
+ };
187535
+ } catch {
187536
+ return { status: "unknown", path };
187537
+ }
186013
187538
  }
186014
187539
  function detectSetup(options) {
186015
- const home = options.home ?? homedir3();
187540
+ const env = options.env ?? process.env;
187541
+ const home = options.home ? resolve7(options.home) : resolveUserHome(env);
187542
+ const codexConfigPath = options.codexHome ? join6(resolve7(options.codexHome), "config.toml") : options.home ? join6(home, ".codex", "config.toml") : resolveCodexConfigPath(env, options.cwd);
187543
+ const codexMcp = detectCodexMcp(codexConfigPath, options.cwd, home);
187544
+ const claudeMcp = mergeMcpDetections([
187545
+ detectClaudeMcp(join6(options.cwd, ".mcp.json"), options.cwd, home),
187546
+ detectClaudeMcp(join6(home, ".claude.json"), options.cwd, home)
187547
+ ]);
187548
+ const codexSkillPaths = existingSkillPaths([
187549
+ join6(options.cwd, ".agents", "skills", "kyoso-review"),
187550
+ join6(home, ".agents", "skills", "kyoso-review")
187551
+ ]);
187552
+ const claudeSkillPaths = existingSkillPaths([
187553
+ join6(options.cwd, ".claude", "skills", "kyoso-review"),
187554
+ join6(home, ".claude", "skills", "kyoso-review")
187555
+ ]);
186016
187556
  return {
186017
187557
  codex: {
186018
- mcp: hasCodexMcp(join3(home, ".codex", "config.toml")),
186019
- skill: existsSync(join3(options.cwd, ".agents", "skills", "kyoso-review", "SKILL.md")) || existsSync(join3(home, ".agents", "skills", "kyoso-review", "SKILL.md"))
187558
+ mcp: codexMcp.status === "enabled",
187559
+ skill: codexSkillPaths.length > 0,
187560
+ manualMcpStatus: codexMcp.status,
187561
+ mcpPaths: codexMcp.paths,
187562
+ skillPaths: codexSkillPaths
186020
187563
  },
186021
187564
  "claude-code": {
186022
- mcp: hasClaudeMcp(join3(options.cwd, ".mcp.json")) || hasClaudeMcp(join3(home, ".claude.json")),
186023
- skill: existsSync(join3(options.cwd, ".claude", "skills", "kyoso-review", "SKILL.md")) || existsSync(join3(home, ".claude", "skills", "kyoso-review", "SKILL.md"))
187565
+ mcp: claudeMcp.status === "enabled",
187566
+ skill: claudeSkillPaths.length > 0,
187567
+ manualMcpStatus: claudeMcp.status,
187568
+ mcpPaths: claudeMcp.paths,
187569
+ skillPaths: claudeSkillPaths
186024
187570
  }
186025
187571
  };
186026
187572
  }
186027
187573
  async function setupCodex(context) {
187574
+ if (context.skillOnly) {
187575
+ return [
187576
+ await ensureSkill(context, "codex", "Codex skill"),
187577
+ ...singleAgentAdvice(context, "codex")
187578
+ ];
187579
+ }
186028
187580
  return [
186029
187581
  await ensureCodexMcp(context),
186030
- await ensureSkill({
186031
- title: "Codex skill",
186032
- sourceDir: context.sourceSkillDir,
186033
- destinationDir: skillDestination("codex", context.scope, context.cwd, context.home),
186034
- write: context.write
186035
- }),
187582
+ await ensureSkill(context, "codex", "Codex skill"),
186036
187583
  ...singleAgentAdvice(context, "codex")
186037
187584
  ];
186038
187585
  }
186039
187586
  async function setupClaudeCode(context) {
187587
+ if (context.skillOnly) {
187588
+ return [
187589
+ await ensureSkill(context, "claude-code", "Claude Code skill"),
187590
+ ...singleAgentAdvice(context, "claude-code")
187591
+ ];
187592
+ }
186040
187593
  return [
186041
187594
  await ensureClaudeMcp(context),
186042
- await ensureSkill({
186043
- title: "Claude Code skill",
186044
- sourceDir: context.sourceSkillDir,
186045
- destinationDir: skillDestination("claude-code", context.scope, context.cwd, context.home),
186046
- write: context.write
186047
- }),
187595
+ await ensureSkill(context, "claude-code", "Claude Code skill"),
186048
187596
  ...singleAgentAdvice(context, "claude-code")
186049
187597
  ];
186050
187598
  }
186051
187599
  async function ensureCodexMcp(context) {
186052
- const configPath = join3(context.home, ".codex", "config.toml");
187600
+ const configPath = join6(context.codexHome, "config.toml");
186053
187601
  const snippet = buildCodexMcpToml(context.mcpCommand);
186054
187602
  const current = await readOptionalFile(configPath);
186055
187603
  if (hasCodexMcpContent(current)) {
@@ -186057,7 +187605,7 @@ async function ensureCodexMcp(context) {
186057
187605
  title: "Codex MCP",
186058
187606
  status: "skipped",
186059
187607
  path: configPath,
186060
- detail: "existing [mcp_servers.kyoso] kept"
187608
+ detail: codexMcpStatusFromContent(current) === "disabled" ? disabledCodexMcpDetail(configPath) : "existing [mcp_servers.kyoso] kept"
186061
187609
  };
186062
187610
  }
186063
187611
  const detail = diffForAppend(configPath, snippet);
@@ -186068,8 +187616,8 @@ async function ensureCodexMcp(context) {
186068
187616
  `) ? `
186069
187617
 
186070
187618
  ` : "";
186071
- await mkdir3(dirname4(configPath), { recursive: true });
186072
- await writeFile3(configPath, `${current}${separator}${snippet}`, "utf8");
187619
+ await mkdir5(dirname7(configPath), { recursive: true });
187620
+ await writeFile4(configPath, `${current}${separator}${snippet}`, "utf8");
186073
187621
  return {
186074
187622
  title: "Codex MCP",
186075
187623
  status: current.length > 0 ? "updated" : "created",
@@ -186081,15 +187629,15 @@ async function ensureClaudeMcp(context) {
186081
187629
  if (context.scope === "global") {
186082
187630
  return ensureClaudeGlobalMcp(context);
186083
187631
  }
186084
- const configPath = join3(context.cwd, ".mcp.json");
187632
+ const configPath = join6(context.cwd, ".mcp.json");
186085
187633
  const current = await readJsonObject(configPath);
186086
187634
  const mcpServers = recordValue(current.mcpServers);
186087
- if (isRecord5(mcpServers.kyoso)) {
187635
+ if (isRecord7(mcpServers.kyoso)) {
186088
187636
  return {
186089
187637
  title: "Claude Code MCP",
186090
187638
  status: "skipped",
186091
187639
  path: configPath,
186092
- detail: "existing mcpServers.kyoso kept"
187640
+ detail: mcpEntryStatus(mcpServers.kyoso) === "disabled" ? disabledClaudeMcpDetail(configPath) : "existing mcpServers.kyoso kept"
186093
187641
  };
186094
187642
  }
186095
187643
  const next = {
@@ -186108,7 +187656,7 @@ async function ensureClaudeMcp(context) {
186108
187656
  detail
186109
187657
  };
186110
187658
  }
186111
- await writeFile3(configPath, `${JSON.stringify(next, null, 2)}
187659
+ await writeFile4(configPath, `${JSON.stringify(next, null, 2)}
186112
187660
  `, "utf8");
186113
187661
  return {
186114
187662
  title: "Claude Code MCP",
@@ -186118,13 +187666,14 @@ async function ensureClaudeMcp(context) {
186118
187666
  };
186119
187667
  }
186120
187668
  function ensureClaudeGlobalMcp(context) {
186121
- const configPath = join3(context.home, ".claude.json");
186122
- if (hasClaudeMcp(configPath)) {
187669
+ const configPath = join6(context.home, ".claude.json");
187670
+ const existingMcp = detectClaudeMcp(configPath, context.cwd, context.home);
187671
+ if (existingMcp.status !== "missing") {
186123
187672
  return {
186124
187673
  title: "Claude Code MCP",
186125
187674
  status: "skipped",
186126
187675
  path: configPath,
186127
- detail: "existing mcpServers.kyoso kept"
187676
+ detail: existingMcp.status === "disabled" ? disabledClaudeMcpDetail(configPath) : "existing mcpServers.kyoso kept"
186128
187677
  };
186129
187678
  }
186130
187679
  const json2 = JSON.stringify(buildClaudeMcpEntry(context.mcpCommand));
@@ -186138,7 +187687,7 @@ function ensureClaudeGlobalMcp(context) {
186138
187687
  detail: commandLine
186139
187688
  };
186140
187689
  }
186141
- const result = spawnSync("claude", args, { encoding: "utf8" });
187690
+ const result = spawnSync2("claude", args, { encoding: "utf8" });
186142
187691
  if (result.status !== 0) {
186143
187692
  throw new Error(result.stderr || result.stdout || "claude mcp add-json failed");
186144
187693
  }
@@ -186149,43 +187698,25 @@ function ensureClaudeGlobalMcp(context) {
186149
187698
  detail: commandLine
186150
187699
  };
186151
187700
  }
186152
- async function ensureSkill(options) {
186153
- const destinationSkill = join3(options.destinationDir, "SKILL.md");
186154
- if (existsSync(destinationSkill)) {
186155
- return {
186156
- title: options.title,
186157
- status: "skipped",
186158
- path: options.destinationDir,
186159
- detail: "existing kyoso-review skill kept"
186160
- };
186161
- }
186162
- const detail = [
186163
- `copy ${options.sourceDir}`,
186164
- `to ${options.destinationDir}`
186165
- ].join(`
186166
- `);
186167
- if (!options.write) {
186168
- return {
186169
- title: options.title,
186170
- status: "dry-run",
186171
- path: options.destinationDir,
186172
- detail
186173
- };
186174
- }
186175
- await mkdir3(dirname4(options.destinationDir), { recursive: true });
186176
- await cp(options.sourceDir, options.destinationDir, {
186177
- recursive: true,
186178
- force: false
187701
+ async function ensureSkill(context, client, title) {
187702
+ const result = await ensureManagedSkill({
187703
+ sourceDir: context.sourceSkillDir,
187704
+ destinationDir: skillDestination(client, context.scope, context.cwd, context.home),
187705
+ trustedRoot: context.scope === "global" ? context.home : context.cwd,
187706
+ write: context.write,
187707
+ force: context.force
186179
187708
  });
186180
187709
  return {
186181
- title: options.title,
186182
- status: "created",
186183
- path: options.destinationDir,
186184
- detail
187710
+ title,
187711
+ ...result
186185
187712
  };
186186
187713
  }
186187
187714
  function renderSetupOverview(context) {
186188
- const detected = detectSetup({ cwd: context.cwd, home: context.home });
187715
+ const detected = detectSetup({
187716
+ cwd: context.cwd,
187717
+ home: context.home,
187718
+ codexHome: context.codexHome
187719
+ });
186189
187720
  return [
186190
187721
  "Kyoso setup",
186191
187722
  "",
@@ -186194,8 +187725,10 @@ function renderSetupOverview(context) {
186194
187725
  ` claude-code: MCP ${statusWord(detected["claude-code"].mcp)}, skill ${statusWord(detected["claude-code"].skill)}`,
186195
187726
  "",
186196
187727
  "Commands",
186197
- " kyoso setup codex [--write] [--runner npx|bunx] [--global]",
186198
- " kyoso setup claude-code [--write] [--runner npx|bunx] [--global]",
187728
+ " kyoso setup codex [--write] [--runner npx|bunx] [--command <command>] [--global] [--force]",
187729
+ " kyoso setup claude-code [--write] [--runner npx|bunx] [--command <command>] [--global] [--force]",
187730
+ " kyoso setup codex --skill-only [--write] [--global] [--force]",
187731
+ " kyoso setup claude-code --skill-only [--write] [--global] [--force]",
186199
187732
  "",
186200
187733
  `Default MCP command: ${context.mcpCommand.command} ${context.mcpCommand.args.join(" ")}`,
186201
187734
  "Dry-run is the default. Add --write to modify files."
@@ -186221,6 +187754,19 @@ function parseClient(client) {
186221
187754
  return client;
186222
187755
  throw new Error(`Invalid setup client "${client}". Expected codex or claude-code.`);
186223
187756
  }
187757
+ function validateSkillOnlyOptions(options, client) {
187758
+ if (!options.skillOnly)
187759
+ return;
187760
+ if (!client) {
187761
+ throw new Error("--skill-only requires setup client codex or claude-code.");
187762
+ }
187763
+ if (options.runner !== undefined) {
187764
+ throw new Error("--skill-only cannot be combined with --runner.");
187765
+ }
187766
+ if (options.command !== undefined) {
187767
+ throw new Error("--skill-only cannot be combined with --command.");
187768
+ }
187769
+ }
186224
187770
  function parseRunner(runner) {
186225
187771
  if (runner === undefined || runner === "npx")
186226
187772
  return "npx";
@@ -186268,21 +187814,21 @@ function splitCommand(value) {
186268
187814
  return parts;
186269
187815
  }
186270
187816
  function resolveBundledSkillDir() {
186271
- const start = dirname4(fileURLToPath(import.meta.url));
187817
+ const start = dirname7(fileURLToPath(import.meta.url));
186272
187818
  let current = start;
186273
187819
  for (let depth = 0;depth < 5; depth += 1) {
186274
- const candidate = join3(current, ".agents", "skills", "kyoso-review");
186275
- if (existsSync(join3(candidate, "SKILL.md")))
187820
+ const candidate = join6(current, ".agents", "skills", "kyoso-review");
187821
+ if (existsSync2(join6(candidate, "SKILL.md")))
186276
187822
  return candidate;
186277
- current = dirname4(current);
187823
+ current = dirname7(current);
186278
187824
  }
186279
187825
  throw new Error("Bundled kyoso-review skill was not found in this package.");
186280
187826
  }
186281
187827
  async function readOptionalFile(path) {
186282
187828
  try {
186283
- return await readFile5(path, "utf8");
187829
+ return await readFile6(path, "utf8");
186284
187830
  } catch (error51) {
186285
- if (isMissingPathError2(error51))
187831
+ if (isMissingPathError4(error51))
186286
187832
  return "";
186287
187833
  throw error51;
186288
187834
  }
@@ -186292,41 +187838,180 @@ async function readJsonObject(path) {
186292
187838
  if (content.trim().length === 0)
186293
187839
  return {};
186294
187840
  const parsed = JSON.parse(content);
186295
- if (!isRecord5(parsed))
187841
+ if (!isRecord7(parsed))
186296
187842
  throw new Error(`${path} must contain a JSON object`);
186297
187843
  return parsed;
186298
187844
  }
186299
- function hasCodexMcp(path) {
186300
- return existsSync(path) && hasCodexMcpContent(readTextSync(path));
186301
- }
186302
187845
  function hasCodexMcpContent(content) {
186303
187846
  return /^\s*\[mcp_servers\.(?:"kyoso"|kyoso)]\s*$/m.test(content);
186304
187847
  }
186305
- function hasClaudeMcp(path) {
186306
- if (!existsSync(path))
186307
- return false;
187848
+ function codexMcpStatusFromContent(content) {
187849
+ try {
187850
+ const parsed = parse5(content);
187851
+ if (!isRecord7(parsed))
187852
+ return "unknown";
187853
+ if (!isRecord7(parsed.mcp_servers))
187854
+ return "missing";
187855
+ if (!("kyoso" in parsed.mcp_servers))
187856
+ return "missing";
187857
+ return mcpEntryStatus(parsed.mcp_servers.kyoso);
187858
+ } catch {
187859
+ return "unknown";
187860
+ }
187861
+ }
187862
+ function disabledCodexMcpDetail(configPath) {
187863
+ return [
187864
+ "existing [mcp_servers.kyoso] is disabled and was kept unchanged.",
187865
+ `To re-enable it, edit ${configPath} and change enabled = false to enabled = true.`
187866
+ ].join(" ");
187867
+ }
187868
+ function disabledClaudeMcpDetail(configPath) {
187869
+ return [
187870
+ "existing mcpServers.kyoso is disabled and was kept unchanged.",
187871
+ `To re-enable it, edit ${configPath} and change "enabled": false to "enabled": true.`
187872
+ ].join(" ");
187873
+ }
187874
+ function detectCodexMcp(path, cwd, home) {
187875
+ if (!existsSync2(path))
187876
+ return { status: "missing", paths: [] };
187877
+ try {
187878
+ const parsed = parse5(readTextSync(path));
187879
+ if (hasUnprobedProjectIntegrationOverride(parsed, cwd, home)) {
187880
+ return { status: "unknown", paths: [path] };
187881
+ }
187882
+ if (!isRecord7(parsed))
187883
+ return { status: "unknown", paths: [path] };
187884
+ if (!("mcp_servers" in parsed))
187885
+ return { status: "missing", paths: [] };
187886
+ if (!isRecord7(parsed.mcp_servers)) {
187887
+ return { status: "unknown", paths: [path] };
187888
+ }
187889
+ if (!("kyoso" in parsed.mcp_servers)) {
187890
+ return { status: "missing", paths: [] };
187891
+ }
187892
+ return { status: mcpEntryStatus(parsed.mcp_servers.kyoso), paths: [path] };
187893
+ } catch {
187894
+ return { status: "unknown", paths: [path] };
187895
+ }
187896
+ }
187897
+ function detectClaudeMcp(path, cwd, home) {
187898
+ if (!existsSync2(path))
187899
+ return { status: "missing", paths: [] };
186308
187900
  try {
186309
187901
  const parsed = JSON.parse(readTextSync(path));
186310
- return jsonHasKyosoMcp(parsed);
187902
+ const statuses = jsonMcpStatuses(parsed, cwd, home);
187903
+ if (statuses.length === 0)
187904
+ return { status: "missing", paths: [] };
187905
+ return { status: mergeMcpStatuses(statuses), paths: [path] };
186311
187906
  } catch {
186312
- return false;
187907
+ return { status: "unknown", paths: [path] };
187908
+ }
187909
+ }
187910
+ function jsonMcpStatuses(value, cwd, home) {
187911
+ if (!isRecord7(value))
187912
+ return ["unknown"];
187913
+ const statuses = directMcpStatuses(value);
187914
+ if (!("projects" in value))
187915
+ return statuses;
187916
+ if (!isRecord7(value.projects))
187917
+ return [...statuses, "unknown"];
187918
+ const currentProject = normalizeProjectPath(cwd, home);
187919
+ for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
187920
+ if (normalizeProjectPath(projectPath, home) !== currentProject)
187921
+ continue;
187922
+ if (!isRecord7(projectConfig)) {
187923
+ statuses.push("unknown");
187924
+ continue;
187925
+ }
187926
+ statuses.push(...directMcpStatuses(projectConfig));
186313
187927
  }
187928
+ return statuses;
186314
187929
  }
186315
- function jsonHasKyosoMcp(value) {
186316
- if (!isRecord5(value))
187930
+ function directMcpStatuses(value) {
187931
+ const statuses = [];
187932
+ if ("mcpServers" in value) {
187933
+ if (!isRecord7(value.mcpServers)) {
187934
+ statuses.push("unknown");
187935
+ } else if ("kyoso" in value.mcpServers) {
187936
+ statuses.push(mcpEntryStatus(value.mcpServers.kyoso));
187937
+ }
187938
+ }
187939
+ return statuses;
187940
+ }
187941
+ function nestedMcpEntryStatus(value, path) {
187942
+ let current = value;
187943
+ for (const key of path) {
187944
+ if (!isRecord7(current))
187945
+ return "unknown";
187946
+ if (!(key in current))
187947
+ return "missing";
187948
+ current = current[key];
187949
+ }
187950
+ return mcpEntryStatus(current);
187951
+ }
187952
+ function hasUnprobedProjectIntegrationOverride(value, cwd, home) {
187953
+ if (!isRecord7(value) || !isRecord7(value.projects))
186317
187954
  return false;
186318
- if (isRecord5(value.mcpServers) && isRecord5(value.mcpServers.kyoso)) {
186319
- return true;
187955
+ const currentProject = normalizeProjectPath(cwd, home);
187956
+ for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
187957
+ if (normalizeProjectPath(projectPath, home) !== currentProject || !isRecord7(projectConfig)) {
187958
+ continue;
187959
+ }
187960
+ if ("mcp_servers" in projectConfig || "plugins" in projectConfig) {
187961
+ return true;
187962
+ }
186320
187963
  }
186321
- return Object.values(value).some((child) => jsonHasKyosoMcp(child));
187964
+ return false;
187965
+ }
187966
+ function normalizeProjectPath(path, home) {
187967
+ if (path.trim().length === 0)
187968
+ return;
187969
+ const expanded = path === "~" ? home : path.startsWith("~/") || path.startsWith("~\\") ? join6(home, path.slice(2)) : path;
187970
+ const resolved = resolve7(expanded);
187971
+ let normalized;
187972
+ try {
187973
+ normalized = realpathSync2(resolved);
187974
+ } catch {
187975
+ normalized = resolved;
187976
+ }
187977
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
187978
+ }
187979
+ function mcpEntryStatus(value) {
187980
+ if (!isRecord7(value))
187981
+ return "unknown";
187982
+ if (!("enabled" in value))
187983
+ return "enabled";
187984
+ if (value.enabled === true)
187985
+ return "enabled";
187986
+ if (value.enabled === false)
187987
+ return "disabled";
187988
+ return "unknown";
187989
+ }
187990
+ function mergeMcpDetections(detections) {
187991
+ return {
187992
+ status: mergeMcpStatuses(detections.map((detection) => detection.status)),
187993
+ paths: detections.flatMap((detection) => detection.paths)
187994
+ };
187995
+ }
187996
+ function mergeMcpStatuses(statuses) {
187997
+ if (statuses.includes("enabled"))
187998
+ return "enabled";
187999
+ if (statuses.includes("unknown"))
188000
+ return "unknown";
188001
+ if (statuses.includes("disabled"))
188002
+ return "disabled";
188003
+ return "missing";
188004
+ }
188005
+ function existingSkillPaths(paths) {
188006
+ return [...new Set(paths)].filter((path) => existsSync2(join6(path, "SKILL.md")));
186322
188007
  }
186323
188008
  function readTextSync(path) {
186324
- return readFileSync(path, "utf8");
188009
+ return readFileSync2(path, "utf8");
186325
188010
  }
186326
188011
  function recordValue(value) {
186327
188012
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
186328
188013
  }
186329
- function isRecord5(value) {
188014
+ function isRecord7(value) {
186330
188015
  return typeof value === "object" && value !== null && !Array.isArray(value);
186331
188016
  }
186332
188017
  function diffForAppend(path, snippet) {
@@ -186357,7 +188042,7 @@ function statusWord(value) {
186357
188042
  return value ? "ok" : "missing";
186358
188043
  }
186359
188044
  function singleAgentAdvice(context, client) {
186360
- if (client === "claude-code" && !commandExists("codex", context.env)) {
188045
+ if (client === "claude-code" && !commandExists2("codex", context.env)) {
186361
188046
  return [
186362
188047
  {
186363
188048
  title: "Single-agent config",
@@ -186373,7 +188058,7 @@ function singleAgentAdvice(context, client) {
186373
188058
  }
186374
188059
  ];
186375
188060
  }
186376
- if (client === "codex" && !commandExists("claude", context.env)) {
188061
+ if (client === "codex" && !commandExists2("claude", context.env)) {
186377
188062
  return [
186378
188063
  {
186379
188064
  title: "Single-agent config",
@@ -186401,11 +188086,11 @@ function shellQuote(value) {
186401
188086
  return value;
186402
188087
  return `'${value.replaceAll("'", "'\\''")}'`;
186403
188088
  }
186404
- function commandExists(command, env) {
186405
- const paths = env.PATH?.split(delimiter) ?? [];
186406
- return paths.some((path) => existsSync(join3(path, command)));
188089
+ function commandExists2(command, env) {
188090
+ const paths = env.PATH?.split(delimiter2) ?? [];
188091
+ return paths.some((path) => existsSync2(join6(path, command)));
186407
188092
  }
186408
- function isMissingPathError2(error51) {
188093
+ function isMissingPathError4(error51) {
186409
188094
  return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
186410
188095
  }
186411
188096
 
@@ -186414,11 +188099,11 @@ async function runDoctor(options) {
186414
188099
  const env = options.env ?? process.env;
186415
188100
  const loaded = await loadConfig(options);
186416
188101
  const globalConfigPath = resolveGlobalTomlConfigPath(env);
186417
- const projectTomlPath = resolve4(options.cwd, "kyoso.toml");
186418
- const projectTsPath = resolve4(options.cwd, "kyoso.config.ts");
188102
+ const projectTomlPath = resolve8(options.cwd, "kyoso.toml");
188103
+ const projectTsPath = resolve8(options.cwd, "kyoso.config.ts");
186419
188104
  const lines = ["Kyoso doctor", "", "Runtime"];
186420
- lines.push(` Bun: ${commandExists2("bun", env) ? "ok" : "warning not found"}`);
186421
- lines.push(` Node/npm: ${commandExists2("npm", env) ? "ok" : "warning npm not found"}`);
188105
+ lines.push(` Bun: ${commandExists3("bun", env) ? "ok" : "warning not found"}`);
188106
+ lines.push(` Node/npm: ${commandExists3("npm", env) ? "ok" : "warning npm not found"}`);
186422
188107
  lines.push("", "Config");
186423
188108
  lines.push(` global config.toml: ${formatLayer(loaded, "global_toml", globalConfigPath)}`);
186424
188109
  lines.push(` kyoso.toml: ${formatLayer(loaded, "project_toml", projectTomlPath)}`);
@@ -186428,36 +188113,37 @@ async function runDoctor(options) {
186428
188113
  lines.push(` config hash: ${loaded.configHash}`);
186429
188114
  for (const warning of loaded.warnings)
186430
188115
  lines.push(` warning: ${warning}`);
186431
- const setup = detectSetup({ cwd: options.cwd, home: env.HOME });
188116
+ const setup = detectSetup({ cwd: options.cwd, env });
188117
+ const cli = detectCli({ cwd: options.cwd, env });
188118
+ const codexIntegration = determineCodexIntegration({
188119
+ cwd: options.cwd,
188120
+ env,
188121
+ setup: setup.codex,
188122
+ cli,
188123
+ pluginInspector: options.pluginInspector ?? inspectCodexPlugin,
188124
+ mcpListInspector: options.mcpListInspector ?? inspectCodexMcpList
188125
+ });
188126
+ const claudeIntegration = determineClientIntegration("claude-code", setup["claude-code"], cli);
186432
188127
  lines.push("", "MCP", " stdio server: ok");
186433
- lines.push(` Codex registration: ${setup.codex.mcp ? "ok" : "missing"}`);
186434
- lines.push(` Claude Code registration: ${setup["claude-code"].mcp ? "ok" : "missing"}`);
186435
- if (!setup.codex.mcp) {
186436
- lines.push(" next: run `npx @kyo-so/cli setup codex --write`");
186437
- }
186438
- if (!setup["claude-code"].mcp) {
186439
- lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
186440
- }
188128
+ lines.push(` Codex registration: ${formatManualMcpStatus(setup.codex.manualMcpStatus)}`);
188129
+ lines.push(` Claude Code registration: ${formatManualMcpStatus(setup["claude-code"].manualMcpStatus)}`);
186441
188130
  lines.push("", "Skills");
186442
188131
  lines.push(` Codex kyoso-review: ${setup.codex.skill ? "ok" : "missing"}`);
186443
188132
  lines.push(` Claude Code kyoso-review: ${setup["claude-code"].skill ? "ok" : "missing"}`);
186444
- if (!setup.codex.skill) {
186445
- lines.push(" next: run `npx @kyo-so/cli setup codex --write`");
186446
- }
186447
- if (!setup["claude-code"].skill) {
186448
- lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
186449
- }
188133
+ lines.push("", "Integration");
188134
+ appendIntegration(lines, codexIntegration);
188135
+ appendIntegration(lines, claudeIntegration);
186450
188136
  lines.push("", "ACP agents");
186451
188137
  const agentCommandExists = {
186452
- codex: commandExists2(loaded.config.agents.codex.command, env),
186453
- claude: commandExists2(loaded.config.agents.claude.command, env)
188138
+ codex: commandExists3(loaded.config.agents.codex.command, env),
188139
+ claude: commandExists3(loaded.config.agents.claude.command, env)
186454
188140
  };
186455
188141
  for (const agent of ["codex", "claude"]) {
186456
188142
  const config2 = loaded.config.agents[agent];
186457
188143
  const exists3 = agentCommandExists[agent];
186458
188144
  lines.push(` ${agent === "codex" ? "Codex" : "Claude"}: ${exists3 ? "ok" : "warning command not found"}`);
186459
188145
  lines.push(` command: ${[config2.command, ...config2.args].join(" ")}`);
186460
- if (!exists3 && config2.command === "npx" && commandExists2("bunx", env)) {
188146
+ if (!exists3 && config2.command === "npx" && commandExists3("bunx", env)) {
186461
188147
  lines.push(' hint: set agents.<name>.command = "bunx" in config.toml');
186462
188148
  }
186463
188149
  if (agent === "claude") {
@@ -186492,10 +188178,209 @@ async function runDoctor(options) {
186492
188178
  lines.push(` network default: ${loaded.config.network.defaultMode}`);
186493
188179
  lines.push("", "Audit");
186494
188180
  lines.push(` directory: ${loaded.config.audit.directory}`);
188181
+ const auditStateRoot = await inspectAuditStateRootCapability({
188182
+ cwd: options.cwd,
188183
+ env
188184
+ });
188185
+ lines.push(` state root: ${auditStateRoot.available ? "available" : "unavailable"}`);
186495
188186
  lines.push(` raw agent output: ${loaded.config.audit.includeRawAgentOutput ? "enabled" : "disabled"}`);
186496
188187
  return lines.join(`
186497
188188
  `);
186498
188189
  }
188190
+ function determineCodexIntegration(options) {
188191
+ const fallback = determineClientIntegration("codex", options.setup, options.cli);
188192
+ const plugin = options.pluginInspector({
188193
+ cwd: options.cwd,
188194
+ env: options.env
188195
+ });
188196
+ if (plugin.status === "unsupported") {
188197
+ return withPluginDetails(withIntegrationWarnings(fallback, [
188198
+ `Plugin detection unsupported: ${formatCodexInspectionFailure(plugin.failure)}`
188199
+ ], undefined, "not applicable"), "unsupported", "not applicable");
188200
+ }
188201
+ if (plugin.plugin.state === "not_installed") {
188202
+ return withPluginDetails(fallback, "not installed", "not applicable");
188203
+ }
188204
+ if (plugin.plugin.state === "disabled") {
188205
+ return withPluginDetails(withIntegrationWarnings(fallback, ["Plugin disabled."], undefined, undefined), "installed, disabled", "disabled with Plugin");
188206
+ }
188207
+ const pluginWarnings = pluginSkillWarnings(options.setup);
188208
+ const override = detectCodexPluginMcpOverride({
188209
+ cwd: options.cwd,
188210
+ env: options.env
188211
+ });
188212
+ if (options.setup.manualMcpStatus === "unknown") {
188213
+ return withPluginDetails(withIntegrationWarnings(fallback, [
188214
+ ...pluginWarnings,
188215
+ "Plugin MCP origin is unknown because the manual MCP configuration could not be classified."
188216
+ ], "unknown", "unknown"), "installed, enabled", "unknown");
188217
+ }
188218
+ if (options.setup.manualMcpStatus === "enabled" && override.status !== "disabled") {
188219
+ return withPluginDetails(withIntegrationWarnings({
188220
+ ...fallback,
188221
+ mode: "manual-mcp",
188222
+ advice: integrationAdvice("manual-mcp", "codex")
188223
+ }, [
188224
+ ...pluginWarnings,
188225
+ "Plugin and manual Codex MCP registrations coexist; neither registration was changed.",
188226
+ "Plugin MCP origin is unknown while a manual MCP registration is enabled."
188227
+ ], undefined, "unknown"), "installed, enabled", "unknown");
188228
+ }
188229
+ if (override.status === "unknown") {
188230
+ return withPluginDetails(withIntegrationWarnings(fallback, [
188231
+ ...pluginWarnings,
188232
+ "Plugin MCP override could not be safely classified from the Codex configuration."
188233
+ ], "unknown", "unknown"), "installed, enabled", "unknown");
188234
+ }
188235
+ if (override.status === "disabled" && options.setup.manualMcpStatus === "enabled") {
188236
+ return withPluginDetails(withIntegrationWarnings({
188237
+ ...fallback,
188238
+ mode: "manual-mcp",
188239
+ advice: integrationAdvice("manual-mcp", "codex")
188240
+ }, [
188241
+ ...pluginWarnings,
188242
+ "Plugin and manual Codex MCP registrations coexist; neither registration was changed.",
188243
+ "Plugin MCP is disabled by configuration override while the manual MCP remains enabled."
188244
+ ], undefined, "disabled by configuration override"), "installed, enabled", "disabled by configuration override");
188245
+ }
188246
+ if (override.status === "disabled" && options.setup.manualMcpStatus === "disabled") {
188247
+ return withPluginDetails(withIntegrationWarnings({
188248
+ ...fallback,
188249
+ mode: "plugin-skill",
188250
+ advice: integrationAdvice("plugin-skill", "codex")
188251
+ }, pluginWarnings, undefined, "disabled by configuration override"), "installed, enabled", "disabled by configuration override");
188252
+ }
188253
+ if (options.setup.manualMcpStatus === "disabled") {
188254
+ return withPluginDetails(withIntegrationWarnings({
188255
+ ...fallback,
188256
+ mode: "plugin-mcp",
188257
+ advice: integrationAdvice("plugin-mcp", "codex")
188258
+ }, pluginWarnings, undefined, "enabled (manual registration is disabled)"), "installed, enabled", "enabled (manual registration is disabled)");
188259
+ }
188260
+ const effectiveMcp = options.mcpListInspector({
188261
+ cwd: options.cwd,
188262
+ env: options.env
188263
+ });
188264
+ if (effectiveMcp.status === "unsupported") {
188265
+ return withPluginDetails(withIntegrationWarnings(fallback, [
188266
+ ...pluginWarnings,
188267
+ `Plugin MCP effective-state check unsupported: ${formatCodexInspectionFailure(effectiveMcp.failure)}`
188268
+ ], "unknown", "unknown"), "installed, enabled", "unknown");
188269
+ }
188270
+ const expectedEffectiveState = override.status === "disabled" ? "disabled" : "enabled";
188271
+ if (effectiveMcp.kyoso === "enabled" && expectedEffectiveState === "enabled") {
188272
+ return withPluginDetails(withIntegrationWarnings({
188273
+ ...fallback,
188274
+ mode: "plugin-mcp",
188275
+ advice: integrationAdvice("plugin-mcp", "codex")
188276
+ }, pluginWarnings, undefined, "enabled"), "installed, enabled", "enabled");
188277
+ }
188278
+ if (effectiveMcp.kyoso === "disabled" && expectedEffectiveState === "disabled") {
188279
+ return withPluginDetails(withIntegrationWarnings({
188280
+ ...fallback,
188281
+ mode: "plugin-skill",
188282
+ advice: integrationAdvice("plugin-skill", "codex")
188283
+ }, pluginWarnings, undefined, "disabled"), "installed, enabled", "disabled");
188284
+ }
188285
+ if (effectiveMcp.kyoso === "enabled" || effectiveMcp.kyoso === "disabled") {
188286
+ return withPluginDetails(withIntegrationWarnings(fallback, [
188287
+ ...pluginWarnings,
188288
+ "Plugin MCP effective state does not match the recorded configuration precedence; Plugin MCP mode was not inferred."
188289
+ ], "unknown", "unknown"), "installed, enabled", "unknown");
188290
+ }
188291
+ return withPluginDetails(withIntegrationWarnings(fallback, [
188292
+ ...pluginWarnings,
188293
+ "Plugin MCP effective state is unknown; Plugin MCP mode was not inferred."
188294
+ ], "unknown", "unknown"), "installed, enabled", "unknown");
188295
+ }
188296
+ function determineClientIntegration(client, setup, cli) {
188297
+ const integration = determineNonPluginIntegration({
188298
+ manualMcpStatus: setup.manualMcpStatus,
188299
+ hasSkill: setup.skill,
188300
+ cli
188301
+ });
188302
+ return {
188303
+ client: client === "codex" ? "Codex" : "Claude Code",
188304
+ commandClient: client,
188305
+ mode: integration.mode,
188306
+ setup,
188307
+ cli,
188308
+ warnings: integration.warnings,
188309
+ advice: integrationAdvice(integration.mode, client)
188310
+ };
188311
+ }
188312
+ function withPluginDetails(integration, plugin, pluginMcp) {
188313
+ return { ...integration, plugin, pluginMcp };
188314
+ }
188315
+ function withIntegrationWarnings(integration, warnings, mode, pluginMcp) {
188316
+ const nextMode = mode ?? integration.mode;
188317
+ return {
188318
+ ...integration,
188319
+ mode: nextMode,
188320
+ warnings: [...integration.warnings, ...warnings],
188321
+ advice: integrationAdvice(nextMode, integration.commandClient),
188322
+ ...pluginMcp ? { pluginMcp } : {}
188323
+ };
188324
+ }
188325
+ function pluginSkillWarnings(setup) {
188326
+ if (!setup.skill)
188327
+ return [];
188328
+ return [
188329
+ `Plugin Skill and manual Skill copy coexist; priority is not inferred. Manual Skill path(s): ${setup.skillPaths.join(", ")}`
188330
+ ];
188331
+ }
188332
+ function appendIntegration(lines, integration) {
188333
+ lines.push(` ${integration.client} integration: ${integration.mode}`);
188334
+ lines.push(` manual MCP: ${formatManualMcpStatus(integration.setup.manualMcpStatus)}`);
188335
+ if (integration.setup.mcpPaths.length > 0) {
188336
+ lines.push(` manual MCP path(s): ${integration.setup.mcpPaths.join(", ")}`);
188337
+ }
188338
+ lines.push(` Skill: ${integration.setup.skill ? "ok" : "missing"}`);
188339
+ if (integration.setup.skillPaths.length > 0) {
188340
+ lines.push(` manual Skill path(s): ${integration.setup.skillPaths.join(", ")}`);
188341
+ }
188342
+ lines.push(` CLI: ${formatCliAvailability(integration.cli.kyoso)}`);
188343
+ lines.push(` npx: ${integration.cli.npx ? "available" : "missing"}`);
188344
+ lines.push(` bunx: ${integration.cli.bunx ? "available" : "missing"}`);
188345
+ if (integration.plugin)
188346
+ lines.push(` Plugin: ${integration.plugin}`);
188347
+ if (integration.pluginMcp) {
188348
+ lines.push(` Plugin MCP: ${integration.pluginMcp}`);
188349
+ }
188350
+ for (const warning of integration.warnings) {
188351
+ lines.push(` warning: ${warning}`);
188352
+ }
188353
+ lines.push(` ${integration.advice}`);
188354
+ }
188355
+ function formatManualMcpStatus(status) {
188356
+ if (status === "enabled")
188357
+ return "ok";
188358
+ return status;
188359
+ }
188360
+ function integrationAdvice(mode, client) {
188361
+ if (mode === "plugin-mcp" || mode === "manual-mcp") {
188362
+ return "status: ready";
188363
+ }
188364
+ if (mode === "plugin-skill") {
188365
+ return "status: bundled Plugin MCP is disabled; re-enable it or remove the Plugin and use CLI plus Skill-only.";
188366
+ }
188367
+ if (mode === "cli-skill") {
188368
+ return "status: ready; MCP is optional for CLI plus Skill mode.";
188369
+ }
188370
+ if (mode === "skill-on-demand") {
188371
+ return "status: runnable on demand; package-runner fallback may need network access.";
188372
+ }
188373
+ if (mode === "mcp-only") {
188374
+ return `next: run \`npx @kyo-so/cli setup ${client} --write --skill-only\``;
188375
+ }
188376
+ if (mode === "cli-only") {
188377
+ return `next: run \`kyoso setup ${client} --write --skill-only\``;
188378
+ }
188379
+ if (mode === "missing") {
188380
+ return "next: choose a Marketplace Plugin, CLI plus Skill, or traditional MCP setup.";
188381
+ }
188382
+ return "status: inspect the warnings before changing integration state.";
188383
+ }
186499
188384
  function formatLayer(loaded, layer, defaultPath) {
186500
188385
  const source = loaded.sources.find((candidate) => candidate.layer === layer);
186501
188386
  return source ? `found ${source.path}` : `not found ${defaultPath}`;
@@ -186529,11 +188414,11 @@ function formatClaudeDualAuthWarning(preferApiKey) {
186529
188414
  function hasEnv2(env, key) {
186530
188415
  return typeof env[key] === "string" && env[key].trim().length > 0;
186531
188416
  }
186532
- function commandExists2(command, env) {
186533
- const paths = env.PATH?.split(delimiter2) ?? [];
188417
+ function commandExists3(command, env) {
188418
+ const paths = env.PATH?.split(delimiter3) ?? [];
186534
188419
  return paths.some((path) => {
186535
188420
  try {
186536
- accessSync(`${path}/${command}`);
188421
+ accessSync2(`${path}/${command}`);
186537
188422
  return true;
186538
188423
  } catch {
186539
188424
  return false;
@@ -186542,13 +188427,13 @@ function commandExists2(command, env) {
186542
188427
  }
186543
188428
 
186544
188429
  // src/cli/init.ts
186545
- import { access as access3, readFile as readFile6, writeFile as writeFile4 } from "node:fs/promises";
186546
- import { join as join4 } from "node:path";
188430
+ import { access as access3, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
188431
+ import { join as join7 } from "node:path";
186547
188432
  async function runInit(options) {
186548
- const configPath = join4(options.cwd, "kyoso.toml");
186549
- const tsConfigPath = join4(options.cwd, "kyoso.config.ts");
186550
- const skillPath = join4(options.cwd, ".agents/skills/kyoso-review/SKILL.md");
186551
- const gitignorePath = join4(options.cwd, ".gitignore");
188433
+ const configPath = join7(options.cwd, "kyoso.toml");
188434
+ const tsConfigPath = join7(options.cwd, "kyoso.config.ts");
188435
+ const skillPath = join7(options.cwd, ".agents/skills/kyoso-review/SKILL.md");
188436
+ const gitignorePath = join7(options.cwd, ".gitignore");
186552
188437
  const configResult = await writeFileWithOverwritePrompt(configPath, CONFIG_TEMPLATE, options.force);
186553
188438
  const skillResult = await writeFileWithOverwritePrompt(skillPath, SKILL_TEMPLATE, options.force);
186554
188439
  const gitignoreResult = await ensureGitignoreEntry(gitignorePath, ".kyoso/");
@@ -186566,11 +188451,11 @@ async function runInit(options) {
186566
188451
  async function ensureGitignoreEntry(path, entry) {
186567
188452
  let content = "";
186568
188453
  try {
186569
- content = await readFile6(path, "utf8");
188454
+ content = await readFile7(path, "utf8");
186570
188455
  } catch (error51) {
186571
- if (!isMissingPathError3(error51))
188456
+ if (!isMissingPathError5(error51))
186572
188457
  throw error51;
186573
- await writeFile4(path, `${entry}
188458
+ await writeFile5(path, `${entry}
186574
188459
  `, "utf8");
186575
188460
  return "created";
186576
188461
  }
@@ -186580,11 +188465,11 @@ async function ensureGitignoreEntry(path, entry) {
186580
188465
  const separator = content.length > 0 && !content.endsWith(`
186581
188466
  `) ? `
186582
188467
  ` : "";
186583
- await writeFile4(path, `${content}${separator}${entry}
188468
+ await writeFile5(path, `${content}${separator}${entry}
186584
188469
  `, "utf8");
186585
188470
  return "updated";
186586
188471
  }
186587
- function isMissingPathError3(error51) {
188472
+ function isMissingPathError5(error51) {
186588
188473
  return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
186589
188474
  }
186590
188475
  async function exists3(path) {
@@ -187067,11 +188952,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187067
188952
  const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`;
187068
188953
  return `${varKind} ${this.name}${rhs};` + _n;
187069
188954
  }
187070
- optimizeNames(names, constants) {
188955
+ optimizeNames(names, constants2) {
187071
188956
  if (!names[this.name.str])
187072
188957
  return;
187073
188958
  if (this.rhs)
187074
- this.rhs = optimizeExpr(this.rhs, names, constants);
188959
+ this.rhs = optimizeExpr(this.rhs, names, constants2);
187075
188960
  return this;
187076
188961
  }
187077
188962
  get names() {
@@ -187088,10 +188973,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187088
188973
  render({ _n }) {
187089
188974
  return `${this.lhs} = ${this.rhs};` + _n;
187090
188975
  }
187091
- optimizeNames(names, constants) {
188976
+ optimizeNames(names, constants2) {
187092
188977
  if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
187093
188978
  return;
187094
- this.rhs = optimizeExpr(this.rhs, names, constants);
188979
+ this.rhs = optimizeExpr(this.rhs, names, constants2);
187095
188980
  return this;
187096
188981
  }
187097
188982
  get names() {
@@ -187150,8 +189035,8 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187150
189035
  optimizeNodes() {
187151
189036
  return `${this.code}` ? this : undefined;
187152
189037
  }
187153
- optimizeNames(names, constants) {
187154
- this.code = optimizeExpr(this.code, names, constants);
189038
+ optimizeNames(names, constants2) {
189039
+ this.code = optimizeExpr(this.code, names, constants2);
187155
189040
  return this;
187156
189041
  }
187157
189042
  get names() {
@@ -187180,12 +189065,12 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187180
189065
  }
187181
189066
  return nodes.length > 0 ? this : undefined;
187182
189067
  }
187183
- optimizeNames(names, constants) {
189068
+ optimizeNames(names, constants2) {
187184
189069
  const { nodes } = this;
187185
189070
  let i = nodes.length;
187186
189071
  while (i--) {
187187
189072
  const n = nodes[i];
187188
- if (n.optimizeNames(names, constants))
189073
+ if (n.optimizeNames(names, constants2))
187189
189074
  continue;
187190
189075
  subtractNames(names, n.names);
187191
189076
  nodes.splice(i, 1);
@@ -187238,12 +189123,12 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187238
189123
  return;
187239
189124
  return this;
187240
189125
  }
187241
- optimizeNames(names, constants) {
189126
+ optimizeNames(names, constants2) {
187242
189127
  var _a3;
187243
- this.else = (_a3 = this.else) === null || _a3 === undefined ? undefined : _a3.optimizeNames(names, constants);
187244
- if (!(super.optimizeNames(names, constants) || this.else))
189128
+ this.else = (_a3 = this.else) === null || _a3 === undefined ? undefined : _a3.optimizeNames(names, constants2);
189129
+ if (!(super.optimizeNames(names, constants2) || this.else))
187245
189130
  return;
187246
- this.condition = optimizeExpr(this.condition, names, constants);
189131
+ this.condition = optimizeExpr(this.condition, names, constants2);
187247
189132
  return this;
187248
189133
  }
187249
189134
  get names() {
@@ -187266,10 +189151,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187266
189151
  render(opts) {
187267
189152
  return `for(${this.iteration})` + super.render(opts);
187268
189153
  }
187269
- optimizeNames(names, constants) {
187270
- if (!super.optimizeNames(names, constants))
189154
+ optimizeNames(names, constants2) {
189155
+ if (!super.optimizeNames(names, constants2))
187271
189156
  return;
187272
- this.iteration = optimizeExpr(this.iteration, names, constants);
189157
+ this.iteration = optimizeExpr(this.iteration, names, constants2);
187273
189158
  return this;
187274
189159
  }
187275
189160
  get names() {
@@ -187304,10 +189189,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187304
189189
  render(opts) {
187305
189190
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
187306
189191
  }
187307
- optimizeNames(names, constants) {
187308
- if (!super.optimizeNames(names, constants))
189192
+ optimizeNames(names, constants2) {
189193
+ if (!super.optimizeNames(names, constants2))
187309
189194
  return;
187310
- this.iterable = optimizeExpr(this.iterable, names, constants);
189195
+ this.iterable = optimizeExpr(this.iterable, names, constants2);
187311
189196
  return this;
187312
189197
  }
187313
189198
  get names() {
@@ -187348,11 +189233,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187348
189233
  (_b = this.finally) === null || _b === undefined || _b.optimizeNodes();
187349
189234
  return this;
187350
189235
  }
187351
- optimizeNames(names, constants) {
189236
+ optimizeNames(names, constants2) {
187352
189237
  var _a3, _b;
187353
- super.optimizeNames(names, constants);
187354
- (_a3 = this.catch) === null || _a3 === undefined || _a3.optimizeNames(names, constants);
187355
- (_b = this.finally) === null || _b === undefined || _b.optimizeNames(names, constants);
189238
+ super.optimizeNames(names, constants2);
189239
+ (_a3 = this.catch) === null || _a3 === undefined || _a3.optimizeNames(names, constants2);
189240
+ (_b = this.finally) === null || _b === undefined || _b.optimizeNames(names, constants2);
187356
189241
  return this;
187357
189242
  }
187358
189243
  get names() {
@@ -187621,7 +189506,7 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187621
189506
  function addExprNames(names, from) {
187622
189507
  return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
187623
189508
  }
187624
- function optimizeExpr(expr, names, constants) {
189509
+ function optimizeExpr(expr, names, constants2) {
187625
189510
  if (expr instanceof code_1.Name)
187626
189511
  return replaceName(expr);
187627
189512
  if (!canOptimize(expr))
@@ -187636,14 +189521,14 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
187636
189521
  return items;
187637
189522
  }, []));
187638
189523
  function replaceName(n) {
187639
- const c = constants[n.str];
189524
+ const c = constants2[n.str];
187640
189525
  if (c === undefined || names[n.str] !== 1)
187641
189526
  return n;
187642
189527
  delete names[n.str];
187643
189528
  return c;
187644
189529
  }
187645
189530
  function canOptimize(e) {
187646
- return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== undefined);
189531
+ return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== undefined);
187647
189532
  }
187648
189533
  }
187649
189534
  function subtractNames(names, from) {
@@ -188780,8 +190665,8 @@ var require_resolve = /* @__PURE__ */ __commonJSMin((exports) => {
188780
190665
  }
188781
190666
  return count;
188782
190667
  }
188783
- function getFullPath(resolver, id = "", normalize) {
188784
- if (normalize !== false)
190668
+ function getFullPath(resolver, id = "", normalize2) {
190669
+ if (normalize2 !== false)
188785
190670
  id = normalizeId(id);
188786
190671
  return _getFullPath(resolver, resolver.parse(id));
188787
190672
  }
@@ -189496,7 +191381,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
189496
191381
  const schOrFunc = root.refs[ref];
189497
191382
  if (schOrFunc)
189498
191383
  return schOrFunc;
189499
- let _sch = resolve5.call(this, root, ref);
191384
+ let _sch = resolve9.call(this, root, ref);
189500
191385
  if (_sch === undefined) {
189501
191386
  const schema = (_a3 = root.localRefs) === null || _a3 === undefined ? undefined : _a3[ref];
189502
191387
  const { schemaId } = this.opts;
@@ -189527,7 +191412,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
189527
191412
  function sameSchemaEnv(s1, s2) {
189528
191413
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
189529
191414
  }
189530
- function resolve5(root, ref) {
191415
+ function resolve9(root, ref) {
189531
191416
  let sch;
189532
191417
  while (typeof (sch = this.refs[ref]) == "string")
189533
191418
  ref = sch;
@@ -190026,60 +191911,60 @@ var require_schemes = /* @__PURE__ */ __commonJSMin((exports, module) => {
190026
191911
  var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
190027
191912
  const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
190028
191913
  const { SCHEMES, getSchemeHandler } = require_schemes();
190029
- function normalize(uri, options) {
191914
+ function normalize2(uri, options) {
190030
191915
  if (typeof uri === "string")
190031
191916
  uri = serialize(parse6(uri, options), options);
190032
191917
  else if (typeof uri === "object")
190033
191918
  uri = parse6(serialize(uri, options), options);
190034
191919
  return uri;
190035
191920
  }
190036
- function resolve5(baseURI, relativeURI, options) {
191921
+ function resolve9(baseURI, relativeURI, options) {
190037
191922
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
190038
191923
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
190039
191924
  schemelessOptions.skipEscape = true;
190040
191925
  return serialize(resolved, schemelessOptions);
190041
191926
  }
190042
- function resolveComponent(base, relative, options, skipNormalization) {
191927
+ function resolveComponent(base, relative3, options, skipNormalization) {
190043
191928
  const target = {};
190044
191929
  if (!skipNormalization) {
190045
191930
  base = parse6(serialize(base, options), options);
190046
- relative = parse6(serialize(relative, options), options);
191931
+ relative3 = parse6(serialize(relative3, options), options);
190047
191932
  }
190048
191933
  options = options || {};
190049
- if (!options.tolerant && relative.scheme) {
190050
- target.scheme = relative.scheme;
190051
- target.userinfo = relative.userinfo;
190052
- target.host = relative.host;
190053
- target.port = relative.port;
190054
- target.path = removeDotSegments(relative.path || "");
190055
- target.query = relative.query;
191934
+ if (!options.tolerant && relative3.scheme) {
191935
+ target.scheme = relative3.scheme;
191936
+ target.userinfo = relative3.userinfo;
191937
+ target.host = relative3.host;
191938
+ target.port = relative3.port;
191939
+ target.path = removeDotSegments(relative3.path || "");
191940
+ target.query = relative3.query;
190056
191941
  } else {
190057
- if (relative.userinfo !== undefined || relative.host !== undefined || relative.port !== undefined) {
190058
- target.userinfo = relative.userinfo;
190059
- target.host = relative.host;
190060
- target.port = relative.port;
190061
- target.path = removeDotSegments(relative.path || "");
190062
- target.query = relative.query;
191942
+ if (relative3.userinfo !== undefined || relative3.host !== undefined || relative3.port !== undefined) {
191943
+ target.userinfo = relative3.userinfo;
191944
+ target.host = relative3.host;
191945
+ target.port = relative3.port;
191946
+ target.path = removeDotSegments(relative3.path || "");
191947
+ target.query = relative3.query;
190063
191948
  } else {
190064
- if (!relative.path) {
191949
+ if (!relative3.path) {
190065
191950
  target.path = base.path;
190066
- if (relative.query !== undefined)
190067
- target.query = relative.query;
191951
+ if (relative3.query !== undefined)
191952
+ target.query = relative3.query;
190068
191953
  else
190069
191954
  target.query = base.query;
190070
191955
  } else {
190071
- if (relative.path[0] === "/")
190072
- target.path = removeDotSegments(relative.path);
191956
+ if (relative3.path[0] === "/")
191957
+ target.path = removeDotSegments(relative3.path);
190073
191958
  else {
190074
191959
  if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path)
190075
- target.path = "/" + relative.path;
191960
+ target.path = "/" + relative3.path;
190076
191961
  else if (!base.path)
190077
- target.path = relative.path;
191962
+ target.path = relative3.path;
190078
191963
  else
190079
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
191964
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
190080
191965
  target.path = removeDotSegments(target.path);
190081
191966
  }
190082
- target.query = relative.query;
191967
+ target.query = relative3.query;
190083
191968
  }
190084
191969
  target.userinfo = base.userinfo;
190085
191970
  target.host = base.host;
@@ -190087,7 +191972,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
190087
191972
  }
190088
191973
  target.scheme = base.scheme;
190089
191974
  }
190090
- target.fragment = relative.fragment;
191975
+ target.fragment = relative3.fragment;
190091
191976
  return target;
190092
191977
  }
190093
191978
  function equal(uriA, uriB, options) {
@@ -190243,8 +192128,8 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
190243
192128
  }
190244
192129
  const fastUri = {
190245
192130
  SCHEMES,
190246
- normalize,
190247
- resolve: resolve5,
192131
+ normalize: normalize2,
192132
+ resolve: resolve9,
190248
192133
  resolveComponent,
190249
192134
  equal,
190250
192135
  serialize,
@@ -197386,14 +199271,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
197386
199271
  return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
197387
199272
  }
197388
199273
  function sleep(ms, signal) {
197389
- return new Promise((resolve5, reject) => {
199274
+ return new Promise((resolve9, reject) => {
197390
199275
  if (signal?.aborted) {
197391
199276
  reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
197392
199277
  return;
197393
199278
  }
197394
199279
  const timer = setTimeout(() => {
197395
199280
  signal?.removeEventListener("abort", onAbort);
197396
- resolve5();
199281
+ resolve9();
197397
199282
  }, ms);
197398
199283
  const onAbort = () => {
197399
199284
  clearTimeout(timer);
@@ -198155,7 +200040,7 @@ var Protocol = class {
198155
200040
  const flowStartedAt = Date.now();
198156
200041
  let onAbort;
198157
200042
  let cleanupMessageId;
198158
- return new Promise((resolve5, reject) => {
200043
+ return new Promise((resolve9, reject) => {
198159
200044
  const earlyReject = (error51) => {
198160
200045
  reject(error51);
198161
200046
  };
@@ -198231,7 +200116,7 @@ var Protocol = class {
198231
200116
  return reject(decoded.error);
198232
200117
  if (decoded.kind === "input_required") {
198233
200118
  if (options?.allowInputRequired === true)
198234
- return resolve5(manualInputRequiredValue(decoded));
200119
+ return resolve9(manualInputRequiredValue(decoded));
198235
200120
  const flow = {
198236
200121
  codec: codec2,
198237
200122
  request,
@@ -198243,12 +200128,12 @@ var Protocol = class {
198243
200128
  params
198244
200129
  }, resultSchema, legOptions)
198245
200130
  };
198246
- return resolve5(this._resolveNonCompleteResult(decoded, flow));
200131
+ return resolve9(this._resolveNonCompleteResult(decoded, flow));
198247
200132
  }
198248
200133
  const result = decoded.result;
198249
200134
  validateStandardSchema(resultSchema, result).then((parseResult) => {
198250
200135
  if (parseResult.success)
198251
- resolve5(parseResult.data);
200136
+ resolve9(parseResult.data);
198252
200137
  else
198253
200138
  reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
198254
200139
  }, reject);
@@ -199792,7 +201677,7 @@ var StdioServerTransport = class {
199792
201677
  send(message) {
199793
201678
  if (this._closed)
199794
201679
  return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
199795
- return new Promise((resolve5, reject) => {
201680
+ return new Promise((resolve9, reject) => {
199796
201681
  const json2 = serializeMessage(message);
199797
201682
  let settled = false;
199798
201683
  const onError = (error51) => {
@@ -199809,7 +201694,7 @@ var StdioServerTransport = class {
199809
201694
  settled = true;
199810
201695
  this._stdout.off("error", onError);
199811
201696
  this._stdout.off("drain", onDrain);
199812
- resolve5();
201697
+ resolve9();
199813
201698
  };
199814
201699
  this._stdout.once("error", onError);
199815
201700
  if (this._stdout.write(json2)) {
@@ -199817,7 +201702,7 @@ var StdioServerTransport = class {
199817
201702
  return;
199818
201703
  settled = true;
199819
201704
  this._stdout.off("error", onError);
199820
- resolve5();
201705
+ resolve9();
199821
201706
  } else if (!settled)
199822
201707
  this._stdout.once("drain", onDrain);
199823
201708
  });
@@ -199825,7 +201710,7 @@ var StdioServerTransport = class {
199825
201710
  };
199826
201711
 
199827
201712
  // src/core/runReview.ts
199828
- import { resolve as resolve6 } from "node:path";
201713
+ import { resolve as resolve10 } from "node:path";
199829
201714
 
199830
201715
  // src/config/configOverrides.ts
199831
201716
  var NUMBER_VALUE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
@@ -199888,7 +201773,7 @@ function parseConfigOverrideValue(value, currentValue) {
199888
201773
  function readPath2(target, path) {
199889
201774
  let current = target;
199890
201775
  for (const key of path) {
199891
- if (!isRecord6(current))
201776
+ if (!isRecord8(current))
199892
201777
  return;
199893
201778
  current = current[key];
199894
201779
  }
@@ -199898,7 +201783,7 @@ function writePath2(target, path, value) {
199898
201783
  let current = target;
199899
201784
  for (const key of path.slice(0, -1)) {
199900
201785
  const child = current[key];
199901
- if (!isRecord6(child)) {
201786
+ if (!isRecord8(child)) {
199902
201787
  throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
199903
201788
  }
199904
201789
  current = child;
@@ -199907,14 +201792,14 @@ function writePath2(target, path, value) {
199907
201792
  if (leaf)
199908
201793
  current[leaf] = value;
199909
201794
  }
199910
- function isRecord6(value) {
201795
+ function isRecord8(value) {
199911
201796
  return typeof value === "object" && value !== null && !Array.isArray(value);
199912
201797
  }
199913
201798
 
199914
201799
  // src/acp/AcpAgentProcess.ts
199915
201800
  import { spawn } from "node:child_process";
199916
- import { readFile as readFile7, realpath } from "node:fs/promises";
199917
- import { isAbsolute, relative, resolve as resolve5 } from "node:path";
201801
+ import { readFile as readFile8, realpath as realpath3 } from "node:fs/promises";
201802
+ import { isAbsolute as isAbsolute4, relative as relative3, resolve as resolve9 } from "node:path";
199918
201803
  import { Readable, Writable } from "node:stream";
199919
201804
 
199920
201805
  // node_modules/@agentclientprotocol/sdk/dist/schema/index.js
@@ -199986,6 +201871,48 @@ function requiredDefaultOnError(schema, fallback) {
199986
201871
  return exports_external.NEVER;
199987
201872
  });
199988
201873
  }
201874
+ function stringTag(value, key) {
201875
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
201876
+ return;
201877
+ }
201878
+ const tag = value[key];
201879
+ return typeof tag === "string" ? tag : undefined;
201880
+ }
201881
+ function excludeKnownTags(schema, key, knownTags) {
201882
+ return schema.superRefine((value, context) => {
201883
+ const tag = stringTag(value, key);
201884
+ if (tag !== undefined && knownTags.includes(tag)) {
201885
+ context.addIssue({
201886
+ code: "custom",
201887
+ path: [key],
201888
+ message: `${key} ${JSON.stringify(tag)} is reserved by a known variant, ` + `but the value does not match that variant's schema`
201889
+ });
201890
+ }
201891
+ });
201892
+ }
201893
+ function preserveCustomPayload(schema, key, knownTags) {
201894
+ return exports_external.unknown().transform((value, context) => {
201895
+ const result = schema.safeParse(value);
201896
+ if (!result.success) {
201897
+ for (const issue2 of result.error.issues) {
201898
+ context.addIssue({ ...issue2, input: value });
201899
+ }
201900
+ return exports_external.NEVER;
201901
+ }
201902
+ const output2 = result.data;
201903
+ const tag = stringTag(value, key);
201904
+ if (tag !== undefined && !knownTags.includes(tag)) {
201905
+ const raw = value;
201906
+ for (const [property, rawValue] of Object.entries(raw)) {
201907
+ if (property === "__proto__")
201908
+ continue;
201909
+ if (!Object.hasOwn(output2, property))
201910
+ output2[property] = rawValue;
201911
+ }
201912
+ }
201913
+ return output2;
201914
+ });
201915
+ }
199989
201916
  function vecSkipError(itemSchema) {
199990
201917
  return exports_external.array(itemSchema.catch(skippedItem)).transform((items) => items.filter((item) => item !== skippedItem));
199991
201918
  }
@@ -200409,15 +202336,15 @@ var zTitledMultiSelectItems = object({
200409
202336
  return;
200410
202337
  })
200411
202338
  });
200412
- var zMultiSelectItems = union([
202339
+ var zMultiSelectItems = preserveCustomPayload(union([
200413
202340
  zStringMultiSelectItems.and(object({
200414
202341
  type: literal("string")
200415
202342
  })),
200416
- object({
202343
+ excludeKnownTags(object({
200417
202344
  type: string2()
200418
- }),
202345
+ }), "type", ["string"]),
200419
202346
  zTitledMultiSelectItems
200420
- ]);
202347
+ ]), "type", ["string"]);
200421
202348
  var zMultiSelectPropertySchema = object({
200422
202349
  title: defaultOnError(string2().nullish(), () => {
200423
202350
  return;
@@ -200435,7 +202362,7 @@ var zMultiSelectPropertySchema = object({
200435
202362
  return;
200436
202363
  })
200437
202364
  });
200438
- var zElicitationPropertySchema = union([
202365
+ var zElicitationPropertySchema = preserveCustomPayload(union([
200439
202366
  zStringPropertySchema.and(object({
200440
202367
  type: literal("string")
200441
202368
  })),
@@ -200451,10 +202378,10 @@ var zElicitationPropertySchema = union([
200451
202378
  zMultiSelectPropertySchema.and(object({
200452
202379
  type: literal("array")
200453
202380
  })),
200454
- object({
202381
+ excludeKnownTags(object({
200455
202382
  type: string2()
200456
- })
200457
- ]);
202383
+ }), "type", ["array", "boolean", "integer", "number", "string"])
202384
+ ]), "type", ["array", "boolean", "integer", "number", "string"]);
200458
202385
  var zElicitationSchema = object({
200459
202386
  type: defaultOnError(zElicitationSchemaType.optional().default("object"), () => "object"),
200460
202387
  title: defaultOnError(string2().nullish(), () => {
@@ -200477,22 +202404,22 @@ var zElicitationUrlMode = intersection(union([zElicitationSessionScope, zElicita
200477
202404
  elicitationId: zElicitationId,
200478
202405
  url: url()
200479
202406
  }));
200480
- var zCreateElicitationRequest = intersection(union([
202407
+ var zCreateElicitationRequest = preserveCustomPayload(intersection(union([
200481
202408
  zElicitationFormMode.and(object({
200482
202409
  mode: literal("form")
200483
202410
  })),
200484
202411
  zElicitationUrlMode.and(object({
200485
202412
  mode: literal("url")
200486
202413
  })),
200487
- intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
202414
+ excludeKnownTags(intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
200488
202415
  mode: string2()
200489
- }))
202416
+ })), "mode", ["form", "url"])
200490
202417
  ]), object({
200491
202418
  message: string2(),
200492
202419
  _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
200493
202420
  return;
200494
202421
  })
200495
- }));
202422
+ })), "mode", ["form", "url"]);
200496
202423
  var zMcpServerAcpId = string2();
200497
202424
  var zConnectMcpRequest = object({
200498
202425
  serverId: zMcpServerAcpId,
@@ -202092,7 +204019,7 @@ var zElicitationContentValue = union([
202092
204019
  var zElicitationAcceptAction = object({
202093
204020
  content: record(string2(), zElicitationContentValue).nullish()
202094
204021
  });
202095
- var zCreateElicitationResponse = intersection(union([
204022
+ var zCreateElicitationResponse = preserveCustomPayload(intersection(union([
202096
204023
  zElicitationAcceptAction.and(object({
202097
204024
  action: literal("accept")
202098
204025
  })),
@@ -202102,14 +204029,14 @@ var zCreateElicitationResponse = intersection(union([
202102
204029
  object({
202103
204030
  action: literal("cancel")
202104
204031
  }),
202105
- object({
204032
+ excludeKnownTags(object({
202106
204033
  action: string2()
202107
- })
204034
+ }), "action", ["accept", "cancel", "decline"])
202108
204035
  ]), object({
202109
204036
  _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
202110
204037
  return;
202111
204038
  })
202112
- }));
204039
+ })), "action", ["accept", "cancel", "decline"]);
202113
204040
  var zConnectMcpResponse = object({
202114
204041
  connectionId: zMcpConnectionId,
202115
204042
  _meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
@@ -202245,16 +204172,34 @@ var zCancelRequestNotification = object({
202245
204172
  return;
202246
204173
  })
202247
204174
  });
204175
+
204176
+ // node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js
204177
+ var zGuardCreateElicitationRequestForm = zElicitationFormMode.and(object({ mode: literal("form") })).and(object({ message: string2() }));
204178
+ var zGuardCreateElicitationRequestUrl = zElicitationUrlMode.and(object({ mode: literal("url") })).and(object({ message: string2() }));
204179
+ var zGuardCreateElicitationRequestCustom = union([zElicitationSessionScope, zElicitationRequestScope]).and(object({ message: string2() }));
204180
+ var zGuardElicitationPropertySchemaString = zStringPropertySchema.and(object({ type: literal("string") }));
204181
+ var zGuardElicitationPropertySchemaNumber = zNumberPropertySchema.and(object({ type: literal("number") }));
204182
+ var zGuardElicitationPropertySchemaInteger = zIntegerPropertySchema.and(object({ type: literal("integer") }));
204183
+ var zGuardElicitationPropertySchemaBoolean = zBooleanPropertySchema.and(object({ type: literal("boolean") }));
204184
+ var zGuardElicitationPropertySchemaArray = zMultiSelectPropertySchema.and(object({ type: literal("array") }));
204185
+ var zGuardMultiSelectItemsString = zStringMultiSelectItems.and(object({ type: literal("string") }));
204186
+ var zGuardCreateElicitationResponseAccept = zElicitationAcceptAction.and(object({ action: literal("accept") }));
204187
+ var zGuardCreateElicitationResponseDecline = object({
204188
+ action: literal("decline")
204189
+ });
204190
+ var zGuardCreateElicitationResponseCancel = object({
204191
+ action: literal("cancel")
204192
+ });
202248
204193
  // node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
202249
204194
  var CANCEL_REQUEST_METHOD = "$/cancel_request";
202250
- function isRecord7(value) {
204195
+ function isRecord9(value) {
202251
204196
  return typeof value === "object" && value !== null;
202252
204197
  }
202253
204198
  function isJsonRpcId(value) {
202254
204199
  return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
202255
204200
  }
202256
204201
  function cancelRequestId(params) {
202257
- if (!isRecord7(params) || !isJsonRpcId(params["requestId"])) {
204202
+ if (!isRecord9(params) || !isJsonRpcId(params["requestId"])) {
202258
204203
  return;
202259
204204
  }
202260
204205
  return params["requestId"];
@@ -202478,12 +204423,12 @@ class Connection {
202478
204423
  }
202479
204424
  const id = this.nextRequestId++;
202480
204425
  let cancel = () => {};
202481
- const responsePromise = new Promise((resolve5, reject) => {
204426
+ const responsePromise = new Promise((resolve9, reject) => {
202482
204427
  const pendingResponse = {
202483
204428
  resolve: (response) => {
202484
204429
  try {
202485
204430
  const value = mapResponse ? mapResponse(response) : response;
202486
- resolve5(value);
204431
+ resolve9(value);
202487
204432
  } catch (error51) {
202488
204433
  reject(error51);
202489
204434
  }
@@ -202548,8 +204493,8 @@ class Connection {
202548
204493
  initialize(stream, handlers) {
202549
204494
  this.stream = stream;
202550
204495
  this.staticHandlers = handlers;
202551
- this.closedPromise = new Promise((resolve5) => {
202552
- this.abortController.signal.addEventListener("abort", () => resolve5());
204496
+ this.closedPromise = new Promise((resolve9) => {
204497
+ this.abortController.signal.addEventListener("abort", () => resolve9());
202553
204498
  });
202554
204499
  this.receive();
202555
204500
  }
@@ -202601,7 +204546,7 @@ class Connection {
202601
204546
  if (this.abortController.signal.aborted) {
202602
204547
  return;
202603
204548
  }
202604
- if (!isRecord7(message)) {
204549
+ if (!isRecord9(message)) {
202605
204550
  console.error("Invalid message", { message });
202606
204551
  return;
202607
204552
  }
@@ -202694,7 +204639,7 @@ class Connection {
202694
204639
  pendingResponse.cleanup?.();
202695
204640
  if ("result" in response) {
202696
204641
  pendingResponse.resolve(response.result);
202697
- } else if ("error" in response && isRecord7(response.error)) {
204642
+ } else if ("error" in response && isRecord9(response.error)) {
202698
204643
  const { code, message, data } = response.error;
202699
204644
  pendingResponse.reject(new RequestError(code, message, data));
202700
204645
  } else {
@@ -202904,7 +204849,7 @@ function ndJsonStream(output2, input2) {
202904
204849
  if (trimmedLine) {
202905
204850
  try {
202906
204851
  const message = JSON.parse(trimmedLine);
202907
- if (isRecord7(message)) {
204852
+ if (isRecord9(message)) {
202908
204853
  controller.enqueue(message);
202909
204854
  } else {
202910
204855
  console.warn("Skipping JSON line that is not an object:", trimmedLine);
@@ -203262,8 +205207,8 @@ class AsyncQueue {
203262
205207
  if (this.failed) {
203263
205208
  return Promise.reject(this.failure);
203264
205209
  }
203265
- return new Promise((resolve5, reject) => {
203266
- this.waiters.push({ resolve: resolve5, reject });
205210
+ return new Promise((resolve9, reject) => {
205211
+ this.waiters.push({ resolve: resolve9, reject });
203267
205212
  });
203268
205213
  }
203269
205214
  }
@@ -203965,7 +205910,7 @@ function isSeverity(value) {
203965
205910
  return typeof value === "string" && severities.includes(value);
203966
205911
  }
203967
205912
  function normalizeCisaSecureByDesign(value) {
203968
- if (!isRecord8(value))
205913
+ if (!isRecord10(value))
203969
205914
  return;
203970
205915
  const normalized = {};
203971
205916
  const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
@@ -204006,7 +205951,7 @@ function normalizeFindingFiles(value) {
204006
205951
  if (!Array.isArray(value))
204007
205952
  return;
204008
205953
  const files = value.flatMap((item) => {
204009
- if (!isRecord8(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
205954
+ if (!isRecord10(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
204010
205955
  return [];
204011
205956
  }
204012
205957
  const file2 = {
@@ -204025,7 +205970,7 @@ function normalizeFindingFiles(value) {
204025
205970
  function normalizeLineNumber(value) {
204026
205971
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
204027
205972
  }
204028
- function isRecord8(value) {
205973
+ function isRecord10(value) {
204029
205974
  return typeof value === "object" && value !== null && !Array.isArray(value);
204030
205975
  }
204031
205976
 
@@ -204228,14 +206173,14 @@ function resolveEffortConfigOption(agent, effort) {
204228
206173
  return;
204229
206174
  }
204230
206175
  async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
204231
- const workspaceRoot = await realpath(workspaceDir);
206176
+ const workspaceRoot = await realpath3(workspaceDir);
204232
206177
  const candidates = resolveReadablePaths(workspaceRoot, requestedPath);
204233
206178
  let content;
204234
206179
  for (const absolute of candidates) {
204235
206180
  const readablePath = await resolveReadableFile(workspaceRoot, absolute);
204236
206181
  if (!readablePath)
204237
206182
  continue;
204238
- content = await readFile7(readablePath, "utf8").catch(() => {
206183
+ content = await readFile8(readablePath, "utf8").catch(() => {
204239
206184
  return;
204240
206185
  });
204241
206186
  if (content !== undefined)
@@ -204251,8 +206196,8 @@ async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
204251
206196
  `);
204252
206197
  }
204253
206198
  async function resolveReadableFile(workspaceRoot, absolute) {
204254
- const realPath = await realpath(absolute).catch((error51) => {
204255
- if (isMissingPathError4(error51))
206199
+ const realPath = await realpath3(absolute).catch((error51) => {
206200
+ if (isMissingPathError6(error51))
204256
206201
  return;
204257
206202
  throw error51;
204258
206203
  });
@@ -204262,18 +206207,18 @@ async function resolveReadableFile(workspaceRoot, absolute) {
204262
206207
  return realPath;
204263
206208
  }
204264
206209
  function resolveReadablePaths(workspaceRoot, requestedPath) {
204265
- const primary = resolve5(workspaceRoot, requestedPath);
206210
+ const primary = resolve9(workspaceRoot, requestedPath);
204266
206211
  assertWithinWorkspace(workspaceRoot, primary);
204267
- const relativePath = relative(workspaceRoot, primary).replaceAll("\\", "/");
206212
+ const relativePath = relative3(workspaceRoot, primary).replaceAll("\\", "/");
204268
206213
  if (relativePath.startsWith("context/") || relativePath.startsWith("repo/")) {
204269
206214
  return [primary];
204270
206215
  }
204271
- const repoPath = resolve5(workspaceRoot, "repo", relativePath);
206216
+ const repoPath = resolve9(workspaceRoot, "repo", relativePath);
204272
206217
  assertWithinWorkspace(workspaceRoot, repoPath);
204273
- return isAbsolute(requestedPath) ? [primary, repoPath] : [repoPath, primary];
206218
+ return isAbsolute4(requestedPath) ? [primary, repoPath] : [repoPath, primary];
204274
206219
  }
204275
206220
  function assertWithinWorkspace(workspaceRoot, absolute) {
204276
- const relativePath = relative(workspaceRoot, absolute);
206221
+ const relativePath = relative3(workspaceRoot, absolute);
204277
206222
  if (relativePath.startsWith("..") || relativePath === "" || relativePath.startsWith("/")) {
204278
206223
  throw RequestError.invalidRequest({
204279
206224
  policy: "Kyoso only allows reads from the temporary snapshot."
@@ -204290,7 +206235,7 @@ function terminateChild(child) {
204290
206235
  }, 2000);
204291
206236
  killTimer.unref();
204292
206237
  }
204293
- function isMissingPathError4(error51) {
206238
+ function isMissingPathError6(error51) {
204294
206239
  return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
204295
206240
  }
204296
206241
  function buildAgentFailure(rawDetail, fallbackMessage) {
@@ -204986,68 +206931,78 @@ function normalizeTitle(value) {
204986
206931
  return value.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
204987
206932
  }
204988
206933
 
204989
- // src/audit/trace.ts
204990
- import { mkdir as mkdir4, appendFile } from "node:fs/promises";
204991
- import { dirname as dirname5, isAbsolute as isAbsolute2, join as join5 } from "node:path";
204992
-
204993
- // src/context/pathPolicy.ts
204994
- import { normalize, sep } from "node:path";
204995
-
204996
- // src/core/errors.ts
204997
- class KyosoRequestError extends Error {
204998
- code;
204999
- constructor(message, code) {
205000
- super(message);
205001
- this.code = code;
205002
- this.name = "KyosoRequestError";
206934
+ // src/audit/safeTraceFile.ts
206935
+ import { constants as constants2 } from "node:fs";
206936
+ import { lstat as lstat3, open, realpath as realpath4, stat } from "node:fs/promises";
206937
+ import { join as join8 } from "node:path";
206938
+ var AUDIT_WARNING_UNSUPPORTED_OPEN_CAPABILITY = "AUDIT_DISABLED_UNSUPPORTED_CAPABILITY: Audit trace writing requires unavailable filesystem capabilities.";
206939
+ async function openVerifiedTraceFile(options) {
206940
+ const flags = secureOpenFlags(options.openConstants);
206941
+ if (flags === undefined)
206942
+ throw new Error("secure open capability unavailable");
206943
+ if (!isSafeTraceId(options.traceId))
206944
+ throw new Error("unsafe trace id");
206945
+ const traceDirectory = await ensureTrustedDirectory({
206946
+ root: options.kyosoRoot,
206947
+ segments: [
206948
+ "workspaces",
206949
+ options.workspaceHash,
206950
+ ...options.logicalDirectory.split("/"),
206951
+ options.date
206952
+ ],
206953
+ uid: options.uid,
206954
+ workspaceRoot: options.workspaceRoot
206955
+ });
206956
+ const tracePath = join8(traceDirectory, `${options.traceId}.jsonl`);
206957
+ if (await optionalLstat3(tracePath)) {
206958
+ throw new Error("trace path already exists");
205003
206959
  }
205004
- }
205005
-
205006
- // src/context/pathPolicy.ts
205007
- function normalizeRelativePath(path) {
205008
- const normalized = normalize(path).replaceAll("\\", "/");
205009
- if (normalized === "." || normalized.startsWith("..") || normalized.includes(`${sep}..${sep}`) || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized)) {
205010
- throw new KyosoRequestError(`Path escapes workspace root: ${path}`, "INVALID_PATH");
206960
+ await options.beforeOpen?.(tracePath);
206961
+ const handle = await open(tracePath, flags, 384);
206962
+ try {
206963
+ const [handleStat, pathStat, realTracePath] = await Promise.all([
206964
+ handle.stat({ bigint: true }),
206965
+ stat(tracePath, { bigint: true }),
206966
+ realpath4(tracePath)
206967
+ ]);
206968
+ if (!handleStat.isFile() || !pathStat.isFile() || handleStat.dev !== pathStat.dev || handleStat.ino !== pathStat.ino || !isPathWithin(realTracePath, options.kyosoRoot)) {
206969
+ throw new Error("trace file identity could not be verified");
206970
+ }
206971
+ return { handle, tracePath };
206972
+ } catch (error51) {
206973
+ try {
206974
+ await handle.close();
206975
+ } catch {}
206976
+ throw error51;
205011
206977
  }
205012
- return normalized;
205013
206978
  }
205014
- function isDeniedPath(path, denyPatterns) {
205015
- return matchesPathPattern(path, denyPatterns, "deny");
206979
+ function secureOpenFlags(provided) {
206980
+ const openConstants = provided ?? constants2;
206981
+ const required2 = [
206982
+ openConstants.O_CREAT,
206983
+ openConstants.O_EXCL,
206984
+ openConstants.O_APPEND,
206985
+ openConstants.O_WRONLY,
206986
+ openConstants.O_NOFOLLOW,
206987
+ openConstants.O_NONBLOCK
206988
+ ];
206989
+ if (required2.some((flag) => typeof flag !== "number" || flag <= 0)) {
206990
+ return;
206991
+ }
206992
+ return required2.reduce((combined, flag) => combined | flag, 0);
205016
206993
  }
205017
- function isAllowedPath(path, allowPatterns) {
205018
- if (allowPatterns.length === 0)
205019
- return true;
205020
- return matchesPathPattern(path, allowPatterns, "allow");
206994
+ function isSafeTraceId(traceId) {
206995
+ return traceId.length > 0 && traceId !== "." && traceId !== ".." && !traceId.includes("/") && !traceId.includes("\\");
205021
206996
  }
205022
- function matchesPathPattern(path, patterns, mode) {
205023
- const normalized = normalizeRelativePath(path);
205024
- const segments = normalized.split("/");
205025
- return patterns.some((pattern) => {
205026
- const normalizedPattern = normalizeRelativePath(pattern);
205027
- if (normalizedPattern.endsWith(".*") && !normalizedPattern.includes("/")) {
205028
- const base = normalizedPattern.slice(0, -2);
205029
- if (mode === "allow") {
205030
- const firstSegment = segments[0] ?? "";
205031
- return firstSegment === base || firstSegment.startsWith(`${base}.`);
205032
- }
205033
- return segments.some((segment) => segment === base || segment.startsWith(`${base}.`));
205034
- }
205035
- if (normalizedPattern.includes("*")) {
205036
- const escaped = normalizedPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
205037
- const regex = mode === "allow" ? new RegExp(`^${escaped}($|/)`) : new RegExp(`(^|/)${escaped}($|/)`);
205038
- return regex.test(normalized);
205039
- }
205040
- if (!normalizedPattern.includes("/")) {
205041
- if (mode === "allow") {
205042
- return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
205043
- }
205044
- return segments.includes(normalizedPattern);
205045
- }
205046
- if (mode === "allow") {
205047
- return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
206997
+ async function optionalLstat3(path) {
206998
+ try {
206999
+ return await lstat3(path);
207000
+ } catch (error51) {
207001
+ if (typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT") {
207002
+ return;
205048
207003
  }
205049
- return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`) || normalized.endsWith(`/${normalizedPattern}`) || normalized.includes(`/${normalizedPattern}/`);
205050
- });
207004
+ throw error51;
207005
+ }
205051
207006
  }
205052
207007
 
205053
207008
  // src/audit/sanitize.ts
@@ -205070,46 +207025,149 @@ function sanitizeForAudit(value, options = {}) {
205070
207025
  }
205071
207026
 
205072
207027
  // src/audit/trace.ts
207028
+ var AUDIT_WARNING_WRITE_FAILED = "AUDIT_WRITE_FAILED: Audit trace writing failed; no further audit events will be written.";
207029
+ var AUDIT_WARNING_FINALIZE_FAILED = "AUDIT_FINALIZE_FAILED: Audit trace close failed.";
207030
+ var AUDIT_WARNING_WRITE_AFTER_FINALIZE = "AUDIT_WRITE_AFTER_FINALIZE: Audit trace is already finalized.";
205073
207031
  function createTraceWriter(options) {
205074
207032
  const warnings = [];
205075
- if (!options.enabled) {
205076
- return {
205077
- warnings,
205078
- async write() {
205079
- return;
205080
- }
205081
- };
205082
- }
207033
+ const warningSet = new Set;
205083
207034
  const date5 = new Date().toISOString().slice(0, 10);
205084
- const directory = validateAuditDirectory(options.directory, warnings);
205085
- const tracePath = join5(options.cwd, directory, date5, `${options.traceId}.jsonl`);
207035
+ let tracePath;
207036
+ let handle;
207037
+ let disabled = !options.enabled;
207038
+ let finalizing = false;
207039
+ let finalized = false;
207040
+ let queue = Promise.resolve();
207041
+ let finalizePromise;
207042
+ const addWarning = (warning) => {
207043
+ if (warningSet.has(warning))
207044
+ return;
207045
+ warningSet.add(warning);
207046
+ warnings.push(warning);
207047
+ };
207048
+ const closeHandle = async () => {
207049
+ const current = handle;
207050
+ handle = undefined;
207051
+ if (!current)
207052
+ return;
207053
+ try {
207054
+ await (options.closeHandle ?? defaultCloseHandle)(current);
207055
+ } catch {
207056
+ addWarning(AUDIT_WARNING_FINALIZE_FAILED);
207057
+ }
207058
+ };
207059
+ const disableAfterWriteFailure = async () => {
207060
+ disabled = true;
207061
+ addWarning(AUDIT_WARNING_WRITE_FAILED);
207062
+ await closeHandle();
207063
+ };
207064
+ const openIfNeeded = async () => {
207065
+ if (handle || disabled)
207066
+ return;
207067
+ if (secureOpenFlags(options.openConstants) === undefined) {
207068
+ disabled = true;
207069
+ addWarning(AUDIT_WARNING_UNSUPPORTED_OPEN_CAPABILITY);
207070
+ return;
207071
+ }
207072
+ const stateRoot = await resolveAuditStateRoot({
207073
+ cwd: options.cwd,
207074
+ directory: options.directory,
207075
+ env: options.env,
207076
+ platform: options.platform,
207077
+ getuid: options.getuid
207078
+ });
207079
+ for (const warning of stateRoot.warnings)
207080
+ addWarning(warning);
207081
+ if (!isResolvedAuditStateRoot(stateRoot)) {
207082
+ disabled = true;
207083
+ return;
207084
+ }
207085
+ try {
207086
+ const opened = await openVerifiedTraceFile({
207087
+ kyosoRoot: stateRoot.kyosoRoot,
207088
+ workspaceHash: stateRoot.workspaceHash,
207089
+ logicalDirectory: stateRoot.logicalDirectory,
207090
+ date: date5,
207091
+ traceId: options.traceId,
207092
+ uid: stateRoot.uid,
207093
+ workspaceRoot: stateRoot.workspaceRoot,
207094
+ openConstants: options.openConstants,
207095
+ beforeOpen: options.beforeOpen
207096
+ });
207097
+ handle = opened.handle;
207098
+ tracePath = opened.tracePath;
207099
+ } catch {
207100
+ disabled = true;
207101
+ addWarning(AUDIT_WARNING_WRITE_FAILED);
207102
+ }
207103
+ };
207104
+ const writeOne = async (event) => {
207105
+ if (disabled)
207106
+ return;
207107
+ try {
207108
+ await openIfNeeded();
207109
+ if (disabled || !handle)
207110
+ return;
207111
+ const line = Buffer.from(`${JSON.stringify(sanitizeForAudit(event, {
207112
+ includeRawAgentOutput: options.includeRawAgentOutput
207113
+ }))}
207114
+ `, "utf8");
207115
+ await writeFully(handle, line, options.writeChunk);
207116
+ } catch {
207117
+ await disableAfterWriteFailure();
207118
+ }
207119
+ };
205086
207120
  return {
205087
- tracePath,
207121
+ get tracePath() {
207122
+ return tracePath;
207123
+ },
205088
207124
  warnings,
205089
- async write(event) {
205090
- try {
205091
- await mkdir4(dirname5(tracePath), { recursive: true });
205092
- await appendFile(tracePath, `${JSON.stringify(sanitizeForAudit(event, {
205093
- includeRawAgentOutput: options.includeRawAgentOutput
205094
- }))}
205095
- `, "utf8");
205096
- } catch (error51) {
205097
- warnings.push(`Audit write failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
205098
- }
207125
+ write(event) {
207126
+ if (finalizing || finalized) {
207127
+ addWarning(AUDIT_WARNING_WRITE_AFTER_FINALIZE);
207128
+ return Promise.resolve();
207129
+ }
207130
+ const task = queue.then(() => writeOne(event));
207131
+ queue = task.catch(async () => {
207132
+ await disableAfterWriteFailure();
207133
+ });
207134
+ return task.catch(() => {
207135
+ return;
207136
+ });
207137
+ },
207138
+ finalize() {
207139
+ if (finalizePromise)
207140
+ return finalizePromise;
207141
+ finalizing = true;
207142
+ finalizePromise = queue.then(async () => {
207143
+ await closeHandle();
207144
+ finalized = true;
207145
+ }).catch(() => {
207146
+ disabled = true;
207147
+ addWarning(AUDIT_WARNING_FINALIZE_FAILED);
207148
+ finalized = true;
207149
+ });
207150
+ return finalizePromise;
205099
207151
  }
205100
207152
  };
205101
207153
  }
205102
- function validateAuditDirectory(directory, warnings) {
205103
- try {
205104
- if (isAbsolute2(directory) || directory.split(/[\\/]+/).includes("..")) {
205105
- throw new Error("unsafe path");
207154
+ async function writeFully(handle, buffer, writeChunk) {
207155
+ let offset = 0;
207156
+ while (offset < buffer.byteLength) {
207157
+ const bytesWritten = await (writeChunk ?? defaultWriteChunk)(handle, buffer, offset);
207158
+ if (!Number.isInteger(bytesWritten) || bytesWritten <= 0) {
207159
+ throw new Error("partial audit write could not advance");
205106
207160
  }
205107
- return normalizeRelativePath(directory);
205108
- } catch {
205109
- warnings.push(`Unsafe audit directory ignored: ${directory}`);
205110
- return ".kyoso/traces";
207161
+ offset += bytesWritten;
205111
207162
  }
205112
207163
  }
207164
+ async function defaultWriteChunk(handle, buffer, offset) {
207165
+ const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset, null);
207166
+ return bytesWritten;
207167
+ }
207168
+ async function defaultCloseHandle(handle) {
207169
+ await handle.close();
207170
+ }
205113
207171
 
205114
207172
  // src/context/truncate.ts
205115
207173
  function truncateUtf8(input2, maxBytes) {
@@ -205526,40 +207584,40 @@ function decide(input2) {
205526
207584
  }
205527
207585
 
205528
207586
  // src/workspace/createSnapshot.ts
205529
- import { chmod, mkdir as mkdir5, mkdtemp, writeFile as writeFile5 } from "node:fs/promises";
205530
- import { dirname as dirname6, join as join6 } from "node:path";
205531
- import { tmpdir } from "node:os";
207587
+ import { chmod, mkdir as mkdir6, mkdtemp as mkdtemp2, writeFile as writeFile6 } from "node:fs/promises";
207588
+ import { dirname as dirname8, join as join9 } from "node:path";
207589
+ import { tmpdir as tmpdir2 } from "node:os";
205532
207590
  async function createSnapshot(traceId, tool, request, options = {}) {
205533
- const root = await mkdtemp(join6(tmpdir(), `kyoso-${traceId}-`));
205534
- const repoDir = join6(root, "repo");
205535
- const contextDir = join6(root, "context");
205536
- await mkdir5(repoDir, { recursive: true });
205537
- await mkdir5(contextDir, { recursive: true });
207591
+ const root = await mkdtemp2(join9(tmpdir2(), `kyoso-${traceId}-`));
207592
+ const repoDir = join9(root, "repo");
207593
+ const contextDir = join9(root, "context");
207594
+ await mkdir6(repoDir, { recursive: true });
207595
+ await mkdir6(contextDir, { recursive: true });
205538
207596
  let fileCount = 0;
205539
207597
  for (const file2 of request.selectedFiles ?? []) {
205540
- const relative2 = normalizeRelativePath(file2.path);
205541
- if (isDeniedPath(relative2, options.denyPatterns ?? []))
207598
+ const relative4 = normalizeRelativePath(file2.path);
207599
+ if (isDeniedPath(relative4, options.denyPatterns ?? []))
205542
207600
  continue;
205543
- if (!isAllowedPath(relative2, options.allowPatterns ?? []))
207601
+ if (!isAllowedPath(relative4, options.allowPatterns ?? []))
205544
207602
  continue;
205545
- const dest = join6(repoDir, relative2);
205546
- await mkdir5(dirname6(dest), { recursive: true });
205547
- await writeFile5(dest, file2.content, "utf8");
207603
+ const dest = join9(repoDir, relative4);
207604
+ await mkdir6(dirname8(dest), { recursive: true });
207605
+ await writeFile6(dest, file2.content, "utf8");
205548
207606
  await chmod(dest, 292).catch(() => {
205549
207607
  return;
205550
207608
  });
205551
207609
  fileCount += 1;
205552
207610
  }
205553
- await writeFile5(join6(contextDir, "request.json"), JSON.stringify(stripContents(request), null, 2), "utf8");
205554
- await writeFile5(join6(contextDir, "selected_files_manifest.json"), JSON.stringify(buildSelectedFilesManifest(request), null, 2), "utf8");
205555
- await writeFile5(join6(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex", options.agentRoles?.codex ?? "implementation_reviewer"), "utf8");
205556
- await writeFile5(join6(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude", options.agentRoles?.claude ?? "architecture_security_reviewer"), "utf8");
207611
+ await writeFile6(join9(contextDir, "request.json"), JSON.stringify(stripContents(request), null, 2), "utf8");
207612
+ await writeFile6(join9(contextDir, "selected_files_manifest.json"), JSON.stringify(buildSelectedFilesManifest(request), null, 2), "utf8");
207613
+ await writeFile6(join9(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex", options.agentRoles?.codex ?? "implementation_reviewer"), "utf8");
207614
+ await writeFile6(join9(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude", options.agentRoles?.claude ?? "architecture_security_reviewer"), "utf8");
205557
207615
  if (request.repoSummary)
205558
- await writeFile5(join6(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
207616
+ await writeFile6(join9(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
205559
207617
  if (request.currentPlan)
205560
- await writeFile5(join6(contextDir, "current_plan.md"), request.currentPlan, "utf8");
207618
+ await writeFile6(join9(contextDir, "current_plan.md"), request.currentPlan, "utf8");
205561
207619
  if (request.diff?.unifiedDiff)
205562
- await writeFile5(join6(contextDir, "diff.patch"), request.diff.unifiedDiff, "utf8");
207620
+ await writeFile6(join9(contextDir, "diff.patch"), request.diff.unifiedDiff, "utf8");
205563
207621
  return { root, repoDir, contextDir, fileCount };
205564
207622
  }
205565
207623
  function stripContents(request) {
@@ -205581,17 +207639,17 @@ function buildSelectedFilesManifest(request) {
205581
207639
  }
205582
207640
 
205583
207641
  // src/workspace/cleanup.ts
205584
- import { rm } from "node:fs/promises";
207642
+ import { rm as rm2 } from "node:fs/promises";
205585
207643
  async function cleanupSnapshot(path) {
205586
207644
  if (process.env.KYOSO_KEEP_TEMP === "1")
205587
207645
  return;
205588
- await rm(path, { recursive: true, force: true });
207646
+ await rm2(path, { recursive: true, force: true });
205589
207647
  }
205590
207648
 
205591
207649
  // src/utils/ids.ts
205592
- import { randomUUID } from "node:crypto";
207650
+ import { randomUUID as randomUUID2 } from "node:crypto";
205593
207651
  function newTraceId() {
205594
- return `tr_${randomUUID()}`;
207652
+ return `tr_${randomUUID2()}`;
205595
207653
  }
205596
207654
 
205597
207655
  // src/core/verification.ts
@@ -205649,7 +207707,7 @@ function parseVerificationVerdicts(rawText) {
205649
207707
  if (!Array.isArray(parsed.verdicts))
205650
207708
  return;
205651
207709
  return parsed.verdicts.flatMap((item) => {
205652
- if (!isRecord9(item))
207710
+ if (!isRecord11(item))
205653
207711
  return [];
205654
207712
  if (typeof item.findingId !== "string")
205655
207713
  return [];
@@ -205727,7 +207785,7 @@ function verificationNote(reasoning) {
205727
207785
  function isVerdict(value) {
205728
207786
  return value === "confirmed" || value === "refuted" || value === "uncertain";
205729
207787
  }
205730
- function isRecord9(value) {
207788
+ function isRecord11(value) {
205731
207789
  return typeof value === "object" && value !== null && !Array.isArray(value);
205732
207790
  }
205733
207791
 
@@ -205736,43 +207794,50 @@ async function runReview(tool, request, options = {}) {
205736
207794
  const cwd = options.cwd ?? process.cwd();
205737
207795
  const traceId = newTraceId();
205738
207796
  const startedAt = new Date().toISOString();
207797
+ const auditEnv = { ...process.env, ...options.env };
207798
+ const traceWriterFactory = options.traceWriterFactory ?? createTraceWriter;
205739
207799
  let snapshot;
205740
207800
  try {
205741
207801
  assertNotChildAgent(options.env ?? process.env);
205742
207802
  } catch (error51) {
205743
207803
  if (error51 instanceof KyosoRequestError) {
205744
207804
  const config2 = kyosoConfigSchema.parse(defaultConfig);
205745
- const trace2 = createTraceWriter({
207805
+ const trace2 = traceWriterFactory({
205746
207806
  enabled: config2.audit.enabled,
205747
207807
  directory: config2.audit.directory,
205748
207808
  traceId,
205749
- cwd
205750
- });
205751
- await trace2.write({
205752
- type: "request_received",
205753
- traceId,
205754
- tool,
205755
- timestamp: new Date().toISOString()
205756
- });
205757
- return await buildPolicyBlockResult({
205758
- tool,
205759
- trace: trace2,
205760
- traceId,
205761
- startedAt,
205762
- networkMode: config2.network.defaultMode,
205763
- warning: error51.message,
205764
- finding: {
205765
- id: "KYOSO-1",
205766
- severity: "critical",
205767
- category: "other",
205768
- title: "Recursive Kyoso invocation blocked",
205769
- evidence: error51.message,
205770
- recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
205771
- sourceAgents: ["kyoso_policy"],
205772
- confidence: "high"
205773
- },
205774
- redactionsApplied: 0
207809
+ cwd,
207810
+ env: auditEnv
205775
207811
  });
207812
+ try {
207813
+ await trace2.write({
207814
+ type: "request_received",
207815
+ traceId,
207816
+ tool,
207817
+ timestamp: new Date().toISOString()
207818
+ });
207819
+ return await buildPolicyBlockResult({
207820
+ tool,
207821
+ trace: trace2,
207822
+ traceId,
207823
+ startedAt,
207824
+ networkMode: config2.network.defaultMode,
207825
+ warning: error51.message,
207826
+ finding: {
207827
+ id: "KYOSO-1",
207828
+ severity: "critical",
207829
+ category: "other",
207830
+ title: "Recursive Kyoso invocation blocked",
207831
+ evidence: error51.message,
207832
+ recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
207833
+ sourceAgents: ["kyoso_policy"],
207834
+ confidence: "high"
207835
+ },
207836
+ redactionsApplied: 0
207837
+ });
207838
+ } finally {
207839
+ await trace2.finalize();
207840
+ }
205776
207841
  }
205777
207842
  throw error51;
205778
207843
  }
@@ -205797,12 +207862,13 @@ async function runReview(tool, request, options = {}) {
205797
207862
  ...baseLoaded,
205798
207863
  config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
205799
207864
  } : baseLoaded;
205800
- const trace = createTraceWriter({
207865
+ const trace = traceWriterFactory({
205801
207866
  enabled: loaded.config.audit.enabled,
205802
207867
  directory: loaded.config.audit.directory,
205803
207868
  traceId,
205804
207869
  cwd,
205805
- includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput
207870
+ includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
207871
+ env: auditEnv
205806
207872
  });
205807
207873
  const warnings = [...loaded.warnings, ...trace.warnings];
205808
207874
  try {
@@ -205884,11 +207950,11 @@ async function runReview(tool, request, options = {}) {
205884
207950
  manager,
205885
207951
  trace
205886
207952
  });
205887
- warnings.push(...agentResults.flatMap((result2) => (result2.warnings ?? []).map((warning) => `Agent ${result2.agent} ${warning}`)));
207953
+ warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
205888
207954
  const normalizedAgentResults = agentResults.map(normalizeAgentRunResult);
205889
- const agentsUsed = normalizedAgentResults.map((result2) => result2.agent);
207955
+ const agentsUsed = normalizedAgentResults.map((result) => result.agent);
205890
207956
  const reviewMode = agentsUsed.length === 1 ? "single_agent" : "multi_agent";
205891
- const completed = normalizedAgentResults.filter((result2) => result2.status === "completed");
207957
+ const completed = normalizedAgentResults.filter((result) => result.status === "completed");
205892
207958
  const degraded = completed.length !== agentResults.length;
205893
207959
  let aggregate = aggregateAgentResults(normalizedAgentResults, {
205894
207960
  reviewMode
@@ -205915,7 +207981,7 @@ async function runReview(tool, request, options = {}) {
205915
207981
  severity: "critical",
205916
207982
  category: "other",
205917
207983
  title: "All backend agents failed",
205918
- evidence: normalizedAgentResults.map((result2) => `${result2.agent}: ${result2.error?.code ?? result2.status}`).join("; "),
207984
+ evidence: normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
205919
207985
  recommendation: "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
205920
207986
  sourceAgents: ["kyoso_policy"],
205921
207987
  confidence: "high"
@@ -205965,7 +208031,7 @@ async function runReview(tool, request, options = {}) {
205965
208031
  residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
205966
208032
  "No residual risks were reported by completed agents; verify security assumptions before release."
205967
208033
  ] : aggregate.residualRisks,
205968
- agentOpinions: normalizedAgentResults.map((result2) => agentOpinionSummary(result2, request.options?.includeAgentRawOutputs === true)),
208034
+ agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
205969
208035
  audit: {
205970
208036
  traceId,
205971
208037
  startedAt,
@@ -205997,15 +208063,10 @@ async function runReview(tool, request, options = {}) {
205997
208063
  judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
205998
208064
  }));
205999
208065
  const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
206000
- const result = {
208066
+ const resultAfterJudge = {
206001
208067
  ...resultWithoutMarkdown,
206002
208068
  disagreements,
206003
- ...crossModelAnalysis ? { crossModelAnalysis } : {},
206004
- summaryMarkdown: renderMarkdownResult(tool, {
206005
- ...resultWithoutMarkdown,
206006
- disagreements,
206007
- ...crossModelAnalysis ? { crossModelAnalysis } : {}
206008
- }, { summaryText: judge.output.summaryText })
208069
+ ...crossModelAnalysis ? { crossModelAnalysis } : {}
206009
208070
  };
206010
208071
  const judgeEvent = {
206011
208072
  type: "judge_completed",
@@ -206017,7 +208078,7 @@ async function runReview(tool, request, options = {}) {
206017
208078
  if (judge.error)
206018
208079
  judgeEvent.error = judge.error;
206019
208080
  await trace.write(judgeEvent);
206020
- result.audit.completedAt = new Date().toISOString();
208081
+ resultAfterJudge.audit.completedAt = new Date().toISOString();
206021
208082
  await trace.write({
206022
208083
  type: "decision_completed",
206023
208084
  traceId,
@@ -206029,8 +208090,14 @@ async function runReview(tool, request, options = {}) {
206029
208090
  traceId,
206030
208091
  timestamp: new Date().toISOString()
206031
208092
  });
206032
- return result;
208093
+ return await finalizeReviewResult({
208094
+ tool,
208095
+ trace,
208096
+ result: resultAfterJudge,
208097
+ summaryText: judge.output.summaryText
208098
+ });
206033
208099
  } finally {
208100
+ await trace.finalize();
206034
208101
  if (snapshot)
206035
208102
  await cleanupSnapshot(snapshot.root);
206036
208103
  }
@@ -206279,10 +208346,6 @@ async function buildSecretBlockResult(input2) {
206279
208346
  warnings: input2.warnings
206280
208347
  }
206281
208348
  };
206282
- const result = {
206283
- ...resultWithoutMarkdown,
206284
- summaryMarkdown: renderMarkdownResult(input2.tool, resultWithoutMarkdown)
206285
- };
206286
208349
  await input2.trace.write({
206287
208350
  type: "decision_completed",
206288
208351
  traceId: input2.traceId,
@@ -206294,7 +208357,11 @@ async function buildSecretBlockResult(input2) {
206294
208357
  traceId: input2.traceId,
206295
208358
  timestamp: new Date().toISOString()
206296
208359
  });
206297
- return result;
208360
+ return await finalizeReviewResult({
208361
+ tool: input2.tool,
208362
+ trace: input2.trace,
208363
+ result: resultWithoutMarkdown
208364
+ });
206298
208365
  }
206299
208366
  function buildSecretFinding(secretScan, options) {
206300
208367
  return {
@@ -206344,10 +208411,6 @@ async function buildPolicyBlockResult(input2) {
206344
208411
  warnings: [input2.warning]
206345
208412
  }
206346
208413
  };
206347
- const result = {
206348
- ...resultWithoutMarkdown,
206349
- summaryMarkdown: renderMarkdownResult(input2.tool, resultWithoutMarkdown)
206350
- };
206351
208414
  await input2.trace.write({
206352
208415
  type: "decision_completed",
206353
208416
  traceId: input2.traceId,
@@ -206359,7 +208422,30 @@ async function buildPolicyBlockResult(input2) {
206359
208422
  traceId: input2.traceId,
206360
208423
  timestamp: new Date().toISOString()
206361
208424
  });
206362
- return result;
208425
+ return await finalizeReviewResult({
208426
+ tool: input2.tool,
208427
+ trace: input2.trace,
208428
+ result: resultWithoutMarkdown
208429
+ });
208430
+ }
208431
+ async function finalizeReviewResult(input2) {
208432
+ await input2.trace.finalize();
208433
+ const result = {
208434
+ ...input2.result,
208435
+ audit: {
208436
+ ...input2.result.audit,
208437
+ warnings: Array.from(new Set([
208438
+ ...input2.result.audit.warnings ?? [],
208439
+ ...input2.trace.warnings
208440
+ ]))
208441
+ }
208442
+ };
208443
+ return {
208444
+ ...result,
208445
+ summaryMarkdown: renderMarkdownResult(input2.tool, result, {
208446
+ summaryText: input2.summaryText
208447
+ })
208448
+ };
206363
208449
  }
206364
208450
  function mergeDenyPatterns(configDeny, requestDeny) {
206365
208451
  return Array.from(new Set([...configDeny, ...requestDeny ?? []]));
@@ -206367,7 +208453,7 @@ function mergeDenyPatterns(configDeny, requestDeny) {
206367
208453
  function assertTrustedWorkspaceRoot(requestRoot, configRoot, cwd) {
206368
208454
  if (!requestRoot)
206369
208455
  return;
206370
- if (resolve6(cwd, requestRoot) !== resolve6(cwd, configRoot)) {
208456
+ if (resolve10(cwd, requestRoot) !== resolve10(cwd, configRoot)) {
206371
208457
  throw new KyosoRequestError("workspace.root is not trusted by config", "UNTRUSTED_WORKSPACE_ROOT");
206372
208458
  }
206373
208459
  }
@@ -206485,7 +208571,9 @@ async function main() {
206485
208571
  write: booleanFlag(parsed.flags, "write"),
206486
208572
  global: booleanFlag(parsed.flags, "global"),
206487
208573
  runner: stringFlag(parsed.flags, "runner"),
206488
- command: stringFlag(parsed.flags, "command")
208574
+ command: stringFlag(parsed.flags, "command"),
208575
+ skillOnly: booleanFlag(parsed.flags, "skill-only"),
208576
+ force: booleanFlag(parsed.flags, "force")
206489
208577
  }));
206490
208578
  return;
206491
208579
  }
@@ -206542,7 +208630,7 @@ async function buildDiff(tool, flags) {
206542
208630
  return;
206543
208631
  const base = stringFlag(flags, "base") ?? "main";
206544
208632
  const head = stringFlag(flags, "head") ?? "HEAD";
206545
- const result = spawnSync2("git", ["diff", base, head], { encoding: "utf8" });
208633
+ const result = spawnSync3("git", ["diff", base, head], { encoding: "utf8" });
206546
208634
  if (result.status !== 0) {
206547
208635
  throw new Error(result.stderr || `git diff failed for ${base}..${head}`);
206548
208636
  }
@@ -206588,7 +208676,8 @@ var HELP = `Kyoso
206588
208676
 
206589
208677
  Usage:
206590
208678
  kyoso mcp [--config kyoso.toml|kyoso.config.ts] [--ignore-config] [--trust-config] [--allow-unknown-config] [--network model_only|unrestricted]
206591
- kyoso setup [codex|claude-code] [--write] [--runner npx|bunx] [--command <command>] [--global]
208679
+ kyoso setup [codex|claude-code] [--write] [--runner npx|bunx] [--command <command>] [--global] [--force]
208680
+ kyoso setup codex|claude-code --skill-only [--write] [--global] [--force]
206592
208681
  kyoso plan --goal <text> [--plan <path-or-text>] [--file <path>] [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]
206593
208682
  kyoso security --goal <text> [--diff <path>] [--file <path>] [--set <key>=<value>]... [--json] [--allow-secret-redaction] [--trust-config] [--allow-unknown-config]
206594
208683
  kyoso diff --base main --head HEAD [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]