@kyo-so/cli 0.7.1 → 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/.agents/skills/kyoso-review/SKILL.md +16 -4
- package/.agents/skills/kyoso-review/agents/openai.yaml +0 -7
- package/CHANGELOG.md +57 -0
- package/README.ja.md +74 -8
- package/README.md +74 -8
- package/README.zh-CN.md +74 -8
- package/dist/audit/safeTraceFile.d.ts +20 -0
- package/dist/audit/stateRoot.d.ts +38 -0
- package/dist/audit/trace.d.ts +15 -3
- package/dist/bin/kyoso.js +2702 -503
- package/dist/cli/codexPluginDetector.d.ts +70 -0
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/integration.d.ts +30 -0
- package/dist/cli/knownSkillDigests.d.ts +11 -0
- package/dist/cli/pluginRuntimeContract.d.ts +232 -0
- package/dist/cli/setup.d.ts +24 -4
- package/dist/cli/skillInstall.d.ts +16 -0
- package/dist/config/configOverrides.d.ts +2 -0
- package/dist/config/projectScope.d.ts +2 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/runReview.d.ts +3 -0
- package/dist/index.js +755 -140
- package/dist/utils/pathContainment.d.ts +1 -0
- package/package.json +9 -2
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(
|
|
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(
|
|
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,
|
|
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
|
|
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,
|
|
19727
|
+
function ensureDirectoriesExist(directoryPath, createDirectory2, directoryExists) {
|
|
19728
19728
|
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
|
|
19729
19729
|
const parentDirectory = getDirectoryPath(directoryPath);
|
|
19730
|
-
ensureDirectoriesExist(parentDirectory,
|
|
19731
|
-
|
|
19730
|
+
ensureDirectoriesExist(parentDirectory, createDirectory2, directoryExists);
|
|
19731
|
+
createDirectory2(directoryPath);
|
|
19732
19732
|
}
|
|
19733
19733
|
}
|
|
19734
|
-
function writeFileEnsuringDirectories(path, data, writeByteOrderMark, writeFile22,
|
|
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)),
|
|
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,
|
|
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(
|
|
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
|
|
41678
|
-
if (
|
|
41677
|
+
const sep3 = directory.lastIndexOf(directorySeparator, i - 1);
|
|
41678
|
+
if (sep3 === -1) {
|
|
41679
41679
|
return;
|
|
41680
41680
|
}
|
|
41681
|
-
return directory.substr(0, Math.max(
|
|
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
|
|
98918
|
+
const resolve3 = factory2.createUniqueName("resolve");
|
|
98919
98919
|
const reject = factory2.createUniqueName("reject");
|
|
98920
98920
|
const parameters = [
|
|
98921
|
-
factory2.createParameterDeclaration(undefined, undefined,
|
|
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()]),
|
|
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 &&
|
|
107030
|
+
createDirectory: host.createDirectory && createDirectory2,
|
|
107031
107031
|
writeFile: host.writeFile && writeFile22,
|
|
107032
107032
|
addOrDeleteFileOrDirectory,
|
|
107033
107033
|
addOrDeleteFile,
|
|
107034
107034
|
clearCache,
|
|
107035
|
-
realpath: host.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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
108615
|
-
if (
|
|
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(
|
|
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
|
|
160214
|
-
if (
|
|
160215
|
-
this.realpath = project.toPath(
|
|
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
|
|
161302
|
-
if (
|
|
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
|
|
163490
|
-
if (
|
|
163491
|
-
this.realpathToScriptInfos.remove(
|
|
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
|
|
164150
|
-
if (
|
|
164151
|
-
forEach(this.realpathToScriptInfos.get(
|
|
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
|
|
164190
|
-
if (((_a3 = result.affectedModuleSpecifierCacheProjects) == null ? undefined : _a3.size) && (
|
|
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((
|
|
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
|
|
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
|
|
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
|
|
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
|
|
@@ -169739,6 +170089,28 @@ var defaultConfig = {
|
|
|
169739
170089
|
|
|
169740
170090
|
// src/config/projectScope.ts
|
|
169741
170091
|
var PROJECT_GLOBAL_ONLY_MESSAGE = "Move global-only settings to the user global config:";
|
|
170092
|
+
var kyosoConfigOverridePaths = [
|
|
170093
|
+
"agents.codex.enabled",
|
|
170094
|
+
"agents.codex.model",
|
|
170095
|
+
"agents.codex.effort",
|
|
170096
|
+
"agents.codex.role",
|
|
170097
|
+
"agents.codex.timeoutMs",
|
|
170098
|
+
"agents.claude.enabled",
|
|
170099
|
+
"agents.claude.model",
|
|
170100
|
+
"agents.claude.effort",
|
|
170101
|
+
"agents.claude.role",
|
|
170102
|
+
"agents.claude.timeoutMs",
|
|
170103
|
+
"verification.enabled",
|
|
170104
|
+
"verification.maxFindings",
|
|
170105
|
+
"verification.timeoutMs",
|
|
170106
|
+
"judge.mode",
|
|
170107
|
+
"judge.provider",
|
|
170108
|
+
"judge.timeoutMs"
|
|
170109
|
+
];
|
|
170110
|
+
var CONFIG_OVERRIDE_PATHS = new Set(kyosoConfigOverridePaths);
|
|
170111
|
+
function isAllowedConfigOverridePath(path) {
|
|
170112
|
+
return CONFIG_OVERRIDE_PATHS.has(path.join("."));
|
|
170113
|
+
}
|
|
169742
170114
|
function mergeProjectTomlConfig(baseConfig, projectConfig, options) {
|
|
169743
170115
|
const violations = collectProjectScopeViolations(projectConfig);
|
|
169744
170116
|
if (violations.length > 0) {
|
|
@@ -169771,13 +170143,9 @@ function collectProjectScopeViolations(config) {
|
|
|
169771
170143
|
}
|
|
169772
170144
|
function isAllowedProjectPath(path) {
|
|
169773
170145
|
const [top, second, third, fourth] = path;
|
|
169774
|
-
if (
|
|
169775
|
-
return true;
|
|
169776
|
-
}
|
|
169777
|
-
if (top === "agents" && path.length === 3 && ["codex", "claude"].includes(second ?? "") && ["enabled", "model", "effort", "role", "timeoutMs"].includes(third ?? "")) {
|
|
170146
|
+
if (isAllowedConfigOverridePath(path))
|
|
169778
170147
|
return true;
|
|
169779
|
-
|
|
169780
|
-
if (top === "verification" && path.length === 2 && ["enabled", "maxFindings", "timeoutMs"].includes(second ?? "")) {
|
|
170148
|
+
if (top === "tools" && path.length === 2 && ["planReview", "securityReview", "diffReview"].includes(second ?? "")) {
|
|
169781
170149
|
return true;
|
|
169782
170150
|
}
|
|
169783
170151
|
if (top === "workspace" && path.length === 2 && ["maxContextBytes", "maxDiffBytes", "deny"].includes(second ?? "")) {
|
|
@@ -169789,9 +170157,6 @@ function isAllowedProjectPath(path) {
|
|
|
169789
170157
|
if (top === "secrets" && path.length === 2 && ["blockOnDetectedSecret", "allowOverride"].includes(second ?? "")) {
|
|
169790
170158
|
return true;
|
|
169791
170159
|
}
|
|
169792
|
-
if (top === "judge" && path.length === 2 && ["mode", "provider", "timeoutMs"].includes(second ?? "")) {
|
|
169793
|
-
return true;
|
|
169794
|
-
}
|
|
169795
170160
|
if (top === "securityReview" && second === "cisaSecureByDesign" && path.length >= 3) {
|
|
169796
170161
|
if (third === "dimensions") {
|
|
169797
170162
|
return path.length === 4 && [
|
|
@@ -184345,12 +184710,6 @@ var kyosoConfigSecuritySensitivePrefixes = [
|
|
|
184345
184710
|
// src/security/redact.ts
|
|
184346
184711
|
var REDACTION = "[KYOSO_REDACTED]";
|
|
184347
184712
|
|
|
184348
|
-
// src/core/constants.ts
|
|
184349
|
-
var DEFAULT_AGENT_TIMEOUT_MS = 120000;
|
|
184350
|
-
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
184351
|
-
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
184352
|
-
var KYOSO_VERSION = "0.7.1";
|
|
184353
|
-
|
|
184354
184713
|
// src/security/sanitizeText.ts
|
|
184355
184714
|
var SENSITIVE_TEXT_PATTERNS = [
|
|
184356
184715
|
/\bsk-(?:proj-)?[A-Za-z0-9_-]{8,}\b/g,
|
|
@@ -184818,7 +185177,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
184818
185177
|
}
|
|
184819
185178
|
return ptr;
|
|
184820
185179
|
}
|
|
184821
|
-
function skipUntil(str, ptr,
|
|
185180
|
+
function skipUntil(str, ptr, sep3, end, banNewLines = false) {
|
|
184822
185181
|
if (!end) {
|
|
184823
185182
|
ptr = indexOfNewline(str, ptr);
|
|
184824
185183
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -184827,7 +185186,7 @@ function skipUntil(str, ptr, sep, end, banNewLines = false) {
|
|
|
184827
185186
|
let c = str[i];
|
|
184828
185187
|
if (c === "#") {
|
|
184829
185188
|
i = indexOfNewline(str, i);
|
|
184830
|
-
} else if (c ===
|
|
185189
|
+
} else if (c === sep3) {
|
|
184831
185190
|
return i + 1;
|
|
184832
185191
|
} else if (c === end || banNewLines && (c === `
|
|
184833
185192
|
` || c === "\r" && str[i + 1] === `
|
|
@@ -185325,7 +185684,7 @@ function formatTomlError(configPath, error51) {
|
|
|
185325
185684
|
// src/config/tsConfigLoader.ts
|
|
185326
185685
|
var import_typescript = __toESM(require_typescript(), 1);
|
|
185327
185686
|
import { createRequire as createRequire2 } from "node:module";
|
|
185328
|
-
import { dirname as
|
|
185687
|
+
import { dirname as dirname3, extname as extname2 } from "node:path";
|
|
185329
185688
|
import { pathToFileURL } from "node:url";
|
|
185330
185689
|
import vm from "node:vm";
|
|
185331
185690
|
|
|
@@ -185359,20 +185718,20 @@ function loadTypeScriptConfig(filePath, source) {
|
|
|
185359
185718
|
${transpiled}
|
|
185360
185719
|
})`, { filename: filePath });
|
|
185361
185720
|
const runner = script.runInThisContext();
|
|
185362
|
-
runner(module.exports, require2, module, filePath,
|
|
185721
|
+
runner(module.exports, require2, module, filePath, dirname3(filePath), defineConfig);
|
|
185363
185722
|
return module.exports;
|
|
185364
185723
|
}
|
|
185365
185724
|
|
|
185366
185725
|
// src/config/trustedConfig.ts
|
|
185367
|
-
import { createHash } from "node:crypto";
|
|
185368
|
-
import { mkdir as
|
|
185726
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
185727
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
185369
185728
|
import { homedir } from "node:os";
|
|
185370
|
-
import { dirname as
|
|
185729
|
+
import { dirname as dirname4, join as join2, resolve as resolve3 } from "node:path";
|
|
185371
185730
|
function hashConfigSource(source) {
|
|
185372
|
-
return
|
|
185731
|
+
return createHash2("sha256").update(source).digest("hex");
|
|
185373
185732
|
}
|
|
185374
185733
|
function defaultTrustedConfigStorePath(env = process.env) {
|
|
185375
|
-
return env.KYOSO_TRUST_STORE_PATH ?
|
|
185734
|
+
return env.KYOSO_TRUST_STORE_PATH ? resolve3(env.KYOSO_TRUST_STORE_PATH) : join2(env.HOME ? resolve3(env.HOME) : homedir(), ".kyoso", "trusted-configs.json");
|
|
185376
185735
|
}
|
|
185377
185736
|
async function isTrustedConfig(storePath, configPath, configHash) {
|
|
185378
185737
|
const store = await readTrustedConfigStore(storePath);
|
|
@@ -185381,7 +185740,7 @@ async function isTrustedConfig(storePath, configPath, configHash) {
|
|
|
185381
185740
|
async function trustConfig(storePath, configPath, configHash) {
|
|
185382
185741
|
const store = await readTrustedConfigStore(storePath);
|
|
185383
185742
|
store[configPath] = configHash;
|
|
185384
|
-
await
|
|
185743
|
+
await mkdir3(dirname4(storePath), { recursive: true });
|
|
185385
185744
|
await writeFile2(storePath, `${JSON.stringify(store, null, 2)}
|
|
185386
185745
|
`, {
|
|
185387
185746
|
encoding: "utf8",
|
|
@@ -185393,7 +185752,7 @@ async function readTrustedConfigStore(storePath) {
|
|
|
185393
185752
|
try {
|
|
185394
185753
|
parsed = JSON.parse(await readFile3(storePath, "utf8"));
|
|
185395
185754
|
} catch (error51) {
|
|
185396
|
-
if (
|
|
185755
|
+
if (isMissingPathError2(error51))
|
|
185397
185756
|
return {};
|
|
185398
185757
|
if (error51 instanceof SyntaxError)
|
|
185399
185758
|
return {};
|
|
@@ -185411,7 +185770,7 @@ async function readTrustedConfigStore(storePath) {
|
|
|
185411
185770
|
function isRecord2(value) {
|
|
185412
185771
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
185413
185772
|
}
|
|
185414
|
-
function
|
|
185773
|
+
function isMissingPathError2(error51) {
|
|
185415
185774
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
185416
185775
|
}
|
|
185417
185776
|
|
|
@@ -185442,7 +185801,7 @@ async function loadConfig(options = {}) {
|
|
|
185442
185801
|
sources.push({ path: globalConfigPath, layer: "global_toml" });
|
|
185443
185802
|
}
|
|
185444
185803
|
if (options.configPath) {
|
|
185445
|
-
const explicitConfigPath =
|
|
185804
|
+
const explicitConfigPath = resolve4(cwd, options.configPath);
|
|
185446
185805
|
if (!await exists2(explicitConfigPath)) {
|
|
185447
185806
|
throw new Error(`Config file not found: ${explicitConfigPath} (from --config)`);
|
|
185448
185807
|
}
|
|
@@ -185459,8 +185818,8 @@ async function loadConfig(options = {}) {
|
|
|
185459
185818
|
sources.push(loaded.source);
|
|
185460
185819
|
warnings.push(...loaded.warnings);
|
|
185461
185820
|
} else {
|
|
185462
|
-
const projectTomlPath =
|
|
185463
|
-
const projectTsPath =
|
|
185821
|
+
const projectTomlPath = resolve4(cwd, "kyoso.toml");
|
|
185822
|
+
const projectTsPath = resolve4(cwd, "kyoso.config.ts");
|
|
185464
185823
|
const hasProjectToml = await exists2(projectTomlPath);
|
|
185465
185824
|
const hasProjectTs = await exists2(projectTsPath);
|
|
185466
185825
|
if (hasProjectToml) {
|
|
@@ -185496,8 +185855,8 @@ async function loadConfig(options = {}) {
|
|
|
185496
185855
|
};
|
|
185497
185856
|
}
|
|
185498
185857
|
function resolveGlobalTomlConfigPath(env = process.env) {
|
|
185499
|
-
const configHome = env.XDG_CONFIG_HOME ?
|
|
185500
|
-
return
|
|
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");
|
|
185501
185860
|
}
|
|
185502
185861
|
function collectGlobalConfigWarnings(configPath, config2) {
|
|
185503
185862
|
const unknownSettings = flattenLeaves(config2).map((leaf) => leaf.path).filter((path) => !isKnownGlobalConfigPath(path)).map((path) => ({
|
|
@@ -185932,23 +186291,1174 @@ function hasEnv(env, key) {
|
|
|
185932
186291
|
return typeof env[key] === "string" && env[key].trim().length > 0;
|
|
185933
186292
|
}
|
|
185934
186293
|
|
|
185935
|
-
// src/cli/
|
|
186294
|
+
// src/cli/codexPluginDetector.ts
|
|
185936
186295
|
import { spawnSync } from "node:child_process";
|
|
185937
|
-
|
|
185938
|
-
|
|
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";
|
|
185939
186882
|
import { homedir as homedir3 } from "node:os";
|
|
185940
|
-
import { delimiter, dirname as
|
|
186883
|
+
import { delimiter as delimiter2, dirname as dirname7, join as join6, resolve as resolve7 } from "node:path";
|
|
185941
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
|
|
185942
187446
|
async function runSetup(options) {
|
|
185943
187447
|
const client = parseClient(options.client);
|
|
187448
|
+
validateSkillOnlyOptions(options, client);
|
|
185944
187449
|
const runner = parseRunner(options.runner);
|
|
185945
187450
|
const command = options.command ? parseCommandSpec(options.command) : commandForRunner(runner);
|
|
187451
|
+
const env = options.env ?? process.env;
|
|
187452
|
+
const home = resolveUserHome(env);
|
|
185946
187453
|
const context = {
|
|
185947
187454
|
cwd: options.cwd,
|
|
185948
|
-
home
|
|
185949
|
-
|
|
187455
|
+
home,
|
|
187456
|
+
codexHome: dirname7(resolveCodexConfigPath(env, options.cwd)),
|
|
187457
|
+
env,
|
|
185950
187458
|
write: options.write,
|
|
185951
187459
|
scope: options.global ? "global" : "project",
|
|
187460
|
+
skillOnly: options.skillOnly ?? false,
|
|
187461
|
+
force: options.force ?? false,
|
|
185952
187462
|
mcpCommand: command,
|
|
185953
187463
|
sourceSkillDir: resolveBundledSkillDir()
|
|
185954
187464
|
};
|
|
@@ -185991,50 +187501,103 @@ function buildClaudeMcpEntry(command) {
|
|
|
185991
187501
|
function skillDestination(client, scope, cwd, home) {
|
|
185992
187502
|
if (client === "codex") {
|
|
185993
187503
|
const root2 = scope === "global" ? home : cwd;
|
|
185994
|
-
return
|
|
187504
|
+
return join6(root2, ".agents", "skills", "kyoso-review");
|
|
185995
187505
|
}
|
|
185996
187506
|
const root = scope === "global" ? home : cwd;
|
|
185997
|
-
return
|
|
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
|
+
}
|
|
185998
187538
|
}
|
|
185999
187539
|
function detectSetup(options) {
|
|
186000
|
-
const
|
|
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
|
+
]);
|
|
186001
187556
|
return {
|
|
186002
187557
|
codex: {
|
|
186003
|
-
mcp:
|
|
186004
|
-
skill:
|
|
187558
|
+
mcp: codexMcp.status === "enabled",
|
|
187559
|
+
skill: codexSkillPaths.length > 0,
|
|
187560
|
+
manualMcpStatus: codexMcp.status,
|
|
187561
|
+
mcpPaths: codexMcp.paths,
|
|
187562
|
+
skillPaths: codexSkillPaths
|
|
186005
187563
|
},
|
|
186006
187564
|
"claude-code": {
|
|
186007
|
-
mcp:
|
|
186008
|
-
skill:
|
|
187565
|
+
mcp: claudeMcp.status === "enabled",
|
|
187566
|
+
skill: claudeSkillPaths.length > 0,
|
|
187567
|
+
manualMcpStatus: claudeMcp.status,
|
|
187568
|
+
mcpPaths: claudeMcp.paths,
|
|
187569
|
+
skillPaths: claudeSkillPaths
|
|
186009
187570
|
}
|
|
186010
187571
|
};
|
|
186011
187572
|
}
|
|
186012
187573
|
async function setupCodex(context) {
|
|
187574
|
+
if (context.skillOnly) {
|
|
187575
|
+
return [
|
|
187576
|
+
await ensureSkill(context, "codex", "Codex skill"),
|
|
187577
|
+
...singleAgentAdvice(context, "codex")
|
|
187578
|
+
];
|
|
187579
|
+
}
|
|
186013
187580
|
return [
|
|
186014
187581
|
await ensureCodexMcp(context),
|
|
186015
|
-
await ensureSkill(
|
|
186016
|
-
title: "Codex skill",
|
|
186017
|
-
sourceDir: context.sourceSkillDir,
|
|
186018
|
-
destinationDir: skillDestination("codex", context.scope, context.cwd, context.home),
|
|
186019
|
-
write: context.write
|
|
186020
|
-
}),
|
|
187582
|
+
await ensureSkill(context, "codex", "Codex skill"),
|
|
186021
187583
|
...singleAgentAdvice(context, "codex")
|
|
186022
187584
|
];
|
|
186023
187585
|
}
|
|
186024
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
|
+
}
|
|
186025
187593
|
return [
|
|
186026
187594
|
await ensureClaudeMcp(context),
|
|
186027
|
-
await ensureSkill(
|
|
186028
|
-
title: "Claude Code skill",
|
|
186029
|
-
sourceDir: context.sourceSkillDir,
|
|
186030
|
-
destinationDir: skillDestination("claude-code", context.scope, context.cwd, context.home),
|
|
186031
|
-
write: context.write
|
|
186032
|
-
}),
|
|
187595
|
+
await ensureSkill(context, "claude-code", "Claude Code skill"),
|
|
186033
187596
|
...singleAgentAdvice(context, "claude-code")
|
|
186034
187597
|
];
|
|
186035
187598
|
}
|
|
186036
187599
|
async function ensureCodexMcp(context) {
|
|
186037
|
-
const configPath =
|
|
187600
|
+
const configPath = join6(context.codexHome, "config.toml");
|
|
186038
187601
|
const snippet = buildCodexMcpToml(context.mcpCommand);
|
|
186039
187602
|
const current = await readOptionalFile(configPath);
|
|
186040
187603
|
if (hasCodexMcpContent(current)) {
|
|
@@ -186042,7 +187605,7 @@ async function ensureCodexMcp(context) {
|
|
|
186042
187605
|
title: "Codex MCP",
|
|
186043
187606
|
status: "skipped",
|
|
186044
187607
|
path: configPath,
|
|
186045
|
-
detail: "existing [mcp_servers.kyoso] kept"
|
|
187608
|
+
detail: codexMcpStatusFromContent(current) === "disabled" ? disabledCodexMcpDetail(configPath) : "existing [mcp_servers.kyoso] kept"
|
|
186046
187609
|
};
|
|
186047
187610
|
}
|
|
186048
187611
|
const detail = diffForAppend(configPath, snippet);
|
|
@@ -186053,8 +187616,8 @@ async function ensureCodexMcp(context) {
|
|
|
186053
187616
|
`) ? `
|
|
186054
187617
|
|
|
186055
187618
|
` : "";
|
|
186056
|
-
await
|
|
186057
|
-
await
|
|
187619
|
+
await mkdir5(dirname7(configPath), { recursive: true });
|
|
187620
|
+
await writeFile4(configPath, `${current}${separator}${snippet}`, "utf8");
|
|
186058
187621
|
return {
|
|
186059
187622
|
title: "Codex MCP",
|
|
186060
187623
|
status: current.length > 0 ? "updated" : "created",
|
|
@@ -186066,15 +187629,15 @@ async function ensureClaudeMcp(context) {
|
|
|
186066
187629
|
if (context.scope === "global") {
|
|
186067
187630
|
return ensureClaudeGlobalMcp(context);
|
|
186068
187631
|
}
|
|
186069
|
-
const configPath =
|
|
187632
|
+
const configPath = join6(context.cwd, ".mcp.json");
|
|
186070
187633
|
const current = await readJsonObject(configPath);
|
|
186071
187634
|
const mcpServers = recordValue(current.mcpServers);
|
|
186072
|
-
if (
|
|
187635
|
+
if (isRecord7(mcpServers.kyoso)) {
|
|
186073
187636
|
return {
|
|
186074
187637
|
title: "Claude Code MCP",
|
|
186075
187638
|
status: "skipped",
|
|
186076
187639
|
path: configPath,
|
|
186077
|
-
detail: "existing mcpServers.kyoso kept"
|
|
187640
|
+
detail: mcpEntryStatus(mcpServers.kyoso) === "disabled" ? disabledClaudeMcpDetail(configPath) : "existing mcpServers.kyoso kept"
|
|
186078
187641
|
};
|
|
186079
187642
|
}
|
|
186080
187643
|
const next = {
|
|
@@ -186093,7 +187656,7 @@ async function ensureClaudeMcp(context) {
|
|
|
186093
187656
|
detail
|
|
186094
187657
|
};
|
|
186095
187658
|
}
|
|
186096
|
-
await
|
|
187659
|
+
await writeFile4(configPath, `${JSON.stringify(next, null, 2)}
|
|
186097
187660
|
`, "utf8");
|
|
186098
187661
|
return {
|
|
186099
187662
|
title: "Claude Code MCP",
|
|
@@ -186103,13 +187666,14 @@ async function ensureClaudeMcp(context) {
|
|
|
186103
187666
|
};
|
|
186104
187667
|
}
|
|
186105
187668
|
function ensureClaudeGlobalMcp(context) {
|
|
186106
|
-
const configPath =
|
|
186107
|
-
|
|
187669
|
+
const configPath = join6(context.home, ".claude.json");
|
|
187670
|
+
const existingMcp = detectClaudeMcp(configPath, context.cwd, context.home);
|
|
187671
|
+
if (existingMcp.status !== "missing") {
|
|
186108
187672
|
return {
|
|
186109
187673
|
title: "Claude Code MCP",
|
|
186110
187674
|
status: "skipped",
|
|
186111
187675
|
path: configPath,
|
|
186112
|
-
detail: "existing mcpServers.kyoso kept"
|
|
187676
|
+
detail: existingMcp.status === "disabled" ? disabledClaudeMcpDetail(configPath) : "existing mcpServers.kyoso kept"
|
|
186113
187677
|
};
|
|
186114
187678
|
}
|
|
186115
187679
|
const json2 = JSON.stringify(buildClaudeMcpEntry(context.mcpCommand));
|
|
@@ -186123,7 +187687,7 @@ function ensureClaudeGlobalMcp(context) {
|
|
|
186123
187687
|
detail: commandLine
|
|
186124
187688
|
};
|
|
186125
187689
|
}
|
|
186126
|
-
const result =
|
|
187690
|
+
const result = spawnSync2("claude", args, { encoding: "utf8" });
|
|
186127
187691
|
if (result.status !== 0) {
|
|
186128
187692
|
throw new Error(result.stderr || result.stdout || "claude mcp add-json failed");
|
|
186129
187693
|
}
|
|
@@ -186134,43 +187698,25 @@ function ensureClaudeGlobalMcp(context) {
|
|
|
186134
187698
|
detail: commandLine
|
|
186135
187699
|
};
|
|
186136
187700
|
}
|
|
186137
|
-
async function ensureSkill(
|
|
186138
|
-
const
|
|
186139
|
-
|
|
186140
|
-
|
|
186141
|
-
|
|
186142
|
-
|
|
186143
|
-
|
|
186144
|
-
detail: "existing kyoso-review skill kept"
|
|
186145
|
-
};
|
|
186146
|
-
}
|
|
186147
|
-
const detail = [
|
|
186148
|
-
`copy ${options.sourceDir}`,
|
|
186149
|
-
`to ${options.destinationDir}`
|
|
186150
|
-
].join(`
|
|
186151
|
-
`);
|
|
186152
|
-
if (!options.write) {
|
|
186153
|
-
return {
|
|
186154
|
-
title: options.title,
|
|
186155
|
-
status: "dry-run",
|
|
186156
|
-
path: options.destinationDir,
|
|
186157
|
-
detail
|
|
186158
|
-
};
|
|
186159
|
-
}
|
|
186160
|
-
await mkdir3(dirname4(options.destinationDir), { recursive: true });
|
|
186161
|
-
await cp(options.sourceDir, options.destinationDir, {
|
|
186162
|
-
recursive: true,
|
|
186163
|
-
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
|
|
186164
187708
|
});
|
|
186165
187709
|
return {
|
|
186166
|
-
title
|
|
186167
|
-
|
|
186168
|
-
path: options.destinationDir,
|
|
186169
|
-
detail
|
|
187710
|
+
title,
|
|
187711
|
+
...result
|
|
186170
187712
|
};
|
|
186171
187713
|
}
|
|
186172
187714
|
function renderSetupOverview(context) {
|
|
186173
|
-
const detected = detectSetup({
|
|
187715
|
+
const detected = detectSetup({
|
|
187716
|
+
cwd: context.cwd,
|
|
187717
|
+
home: context.home,
|
|
187718
|
+
codexHome: context.codexHome
|
|
187719
|
+
});
|
|
186174
187720
|
return [
|
|
186175
187721
|
"Kyoso setup",
|
|
186176
187722
|
"",
|
|
@@ -186179,8 +187725,10 @@ function renderSetupOverview(context) {
|
|
|
186179
187725
|
` claude-code: MCP ${statusWord(detected["claude-code"].mcp)}, skill ${statusWord(detected["claude-code"].skill)}`,
|
|
186180
187726
|
"",
|
|
186181
187727
|
"Commands",
|
|
186182
|
-
" kyoso setup codex [--write] [--runner npx|bunx] [--global]",
|
|
186183
|
-
" 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]",
|
|
186184
187732
|
"",
|
|
186185
187733
|
`Default MCP command: ${context.mcpCommand.command} ${context.mcpCommand.args.join(" ")}`,
|
|
186186
187734
|
"Dry-run is the default. Add --write to modify files."
|
|
@@ -186206,6 +187754,19 @@ function parseClient(client) {
|
|
|
186206
187754
|
return client;
|
|
186207
187755
|
throw new Error(`Invalid setup client "${client}". Expected codex or claude-code.`);
|
|
186208
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
|
+
}
|
|
186209
187770
|
function parseRunner(runner) {
|
|
186210
187771
|
if (runner === undefined || runner === "npx")
|
|
186211
187772
|
return "npx";
|
|
@@ -186253,21 +187814,21 @@ function splitCommand(value) {
|
|
|
186253
187814
|
return parts;
|
|
186254
187815
|
}
|
|
186255
187816
|
function resolveBundledSkillDir() {
|
|
186256
|
-
const start =
|
|
187817
|
+
const start = dirname7(fileURLToPath(import.meta.url));
|
|
186257
187818
|
let current = start;
|
|
186258
187819
|
for (let depth = 0;depth < 5; depth += 1) {
|
|
186259
|
-
const candidate =
|
|
186260
|
-
if (
|
|
187820
|
+
const candidate = join6(current, ".agents", "skills", "kyoso-review");
|
|
187821
|
+
if (existsSync2(join6(candidate, "SKILL.md")))
|
|
186261
187822
|
return candidate;
|
|
186262
|
-
current =
|
|
187823
|
+
current = dirname7(current);
|
|
186263
187824
|
}
|
|
186264
187825
|
throw new Error("Bundled kyoso-review skill was not found in this package.");
|
|
186265
187826
|
}
|
|
186266
187827
|
async function readOptionalFile(path) {
|
|
186267
187828
|
try {
|
|
186268
|
-
return await
|
|
187829
|
+
return await readFile6(path, "utf8");
|
|
186269
187830
|
} catch (error51) {
|
|
186270
|
-
if (
|
|
187831
|
+
if (isMissingPathError4(error51))
|
|
186271
187832
|
return "";
|
|
186272
187833
|
throw error51;
|
|
186273
187834
|
}
|
|
@@ -186277,41 +187838,180 @@ async function readJsonObject(path) {
|
|
|
186277
187838
|
if (content.trim().length === 0)
|
|
186278
187839
|
return {};
|
|
186279
187840
|
const parsed = JSON.parse(content);
|
|
186280
|
-
if (!
|
|
187841
|
+
if (!isRecord7(parsed))
|
|
186281
187842
|
throw new Error(`${path} must contain a JSON object`);
|
|
186282
187843
|
return parsed;
|
|
186283
187844
|
}
|
|
186284
|
-
function hasCodexMcp(path) {
|
|
186285
|
-
return existsSync(path) && hasCodexMcpContent(readTextSync(path));
|
|
186286
|
-
}
|
|
186287
187845
|
function hasCodexMcpContent(content) {
|
|
186288
187846
|
return /^\s*\[mcp_servers\.(?:"kyoso"|kyoso)]\s*$/m.test(content);
|
|
186289
187847
|
}
|
|
186290
|
-
function
|
|
186291
|
-
|
|
186292
|
-
|
|
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: [] };
|
|
186293
187900
|
try {
|
|
186294
187901
|
const parsed = JSON.parse(readTextSync(path));
|
|
186295
|
-
|
|
187902
|
+
const statuses = jsonMcpStatuses(parsed, cwd, home);
|
|
187903
|
+
if (statuses.length === 0)
|
|
187904
|
+
return { status: "missing", paths: [] };
|
|
187905
|
+
return { status: mergeMcpStatuses(statuses), paths: [path] };
|
|
186296
187906
|
} catch {
|
|
186297
|
-
return
|
|
187907
|
+
return { status: "unknown", paths: [path] };
|
|
186298
187908
|
}
|
|
186299
187909
|
}
|
|
186300
|
-
function
|
|
186301
|
-
if (!
|
|
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));
|
|
187927
|
+
}
|
|
187928
|
+
return statuses;
|
|
187929
|
+
}
|
|
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))
|
|
186302
187954
|
return false;
|
|
186303
|
-
|
|
186304
|
-
|
|
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
|
+
}
|
|
186305
187963
|
}
|
|
186306
|
-
return
|
|
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")));
|
|
186307
188007
|
}
|
|
186308
188008
|
function readTextSync(path) {
|
|
186309
|
-
return
|
|
188009
|
+
return readFileSync2(path, "utf8");
|
|
186310
188010
|
}
|
|
186311
188011
|
function recordValue(value) {
|
|
186312
188012
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
186313
188013
|
}
|
|
186314
|
-
function
|
|
188014
|
+
function isRecord7(value) {
|
|
186315
188015
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186316
188016
|
}
|
|
186317
188017
|
function diffForAppend(path, snippet) {
|
|
@@ -186342,7 +188042,7 @@ function statusWord(value) {
|
|
|
186342
188042
|
return value ? "ok" : "missing";
|
|
186343
188043
|
}
|
|
186344
188044
|
function singleAgentAdvice(context, client) {
|
|
186345
|
-
if (client === "claude-code" && !
|
|
188045
|
+
if (client === "claude-code" && !commandExists2("codex", context.env)) {
|
|
186346
188046
|
return [
|
|
186347
188047
|
{
|
|
186348
188048
|
title: "Single-agent config",
|
|
@@ -186358,7 +188058,7 @@ function singleAgentAdvice(context, client) {
|
|
|
186358
188058
|
}
|
|
186359
188059
|
];
|
|
186360
188060
|
}
|
|
186361
|
-
if (client === "codex" && !
|
|
188061
|
+
if (client === "codex" && !commandExists2("claude", context.env)) {
|
|
186362
188062
|
return [
|
|
186363
188063
|
{
|
|
186364
188064
|
title: "Single-agent config",
|
|
@@ -186386,11 +188086,11 @@ function shellQuote(value) {
|
|
|
186386
188086
|
return value;
|
|
186387
188087
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
186388
188088
|
}
|
|
186389
|
-
function
|
|
186390
|
-
const paths = env.PATH?.split(
|
|
186391
|
-
return paths.some((path) =>
|
|
188089
|
+
function commandExists2(command, env) {
|
|
188090
|
+
const paths = env.PATH?.split(delimiter2) ?? [];
|
|
188091
|
+
return paths.some((path) => existsSync2(join6(path, command)));
|
|
186392
188092
|
}
|
|
186393
|
-
function
|
|
188093
|
+
function isMissingPathError4(error51) {
|
|
186394
188094
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
186395
188095
|
}
|
|
186396
188096
|
|
|
@@ -186399,11 +188099,11 @@ async function runDoctor(options) {
|
|
|
186399
188099
|
const env = options.env ?? process.env;
|
|
186400
188100
|
const loaded = await loadConfig(options);
|
|
186401
188101
|
const globalConfigPath = resolveGlobalTomlConfigPath(env);
|
|
186402
|
-
const projectTomlPath =
|
|
186403
|
-
const projectTsPath =
|
|
188102
|
+
const projectTomlPath = resolve8(options.cwd, "kyoso.toml");
|
|
188103
|
+
const projectTsPath = resolve8(options.cwd, "kyoso.config.ts");
|
|
186404
188104
|
const lines = ["Kyoso doctor", "", "Runtime"];
|
|
186405
|
-
lines.push(` Bun: ${
|
|
186406
|
-
lines.push(` Node/npm: ${
|
|
188105
|
+
lines.push(` Bun: ${commandExists3("bun", env) ? "ok" : "warning not found"}`);
|
|
188106
|
+
lines.push(` Node/npm: ${commandExists3("npm", env) ? "ok" : "warning npm not found"}`);
|
|
186407
188107
|
lines.push("", "Config");
|
|
186408
188108
|
lines.push(` global config.toml: ${formatLayer(loaded, "global_toml", globalConfigPath)}`);
|
|
186409
188109
|
lines.push(` kyoso.toml: ${formatLayer(loaded, "project_toml", projectTomlPath)}`);
|
|
@@ -186413,36 +188113,37 @@ async function runDoctor(options) {
|
|
|
186413
188113
|
lines.push(` config hash: ${loaded.configHash}`);
|
|
186414
188114
|
for (const warning of loaded.warnings)
|
|
186415
188115
|
lines.push(` warning: ${warning}`);
|
|
186416
|
-
const setup = detectSetup({ cwd: options.cwd,
|
|
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);
|
|
186417
188127
|
lines.push("", "MCP", " stdio server: ok");
|
|
186418
|
-
lines.push(` Codex registration: ${setup.codex.
|
|
186419
|
-
lines.push(` Claude Code registration: ${setup["claude-code"].
|
|
186420
|
-
if (!setup.codex.mcp) {
|
|
186421
|
-
lines.push(" next: run `npx @kyo-so/cli setup codex --write`");
|
|
186422
|
-
}
|
|
186423
|
-
if (!setup["claude-code"].mcp) {
|
|
186424
|
-
lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
|
|
186425
|
-
}
|
|
188128
|
+
lines.push(` Codex registration: ${formatManualMcpStatus(setup.codex.manualMcpStatus)}`);
|
|
188129
|
+
lines.push(` Claude Code registration: ${formatManualMcpStatus(setup["claude-code"].manualMcpStatus)}`);
|
|
186426
188130
|
lines.push("", "Skills");
|
|
186427
188131
|
lines.push(` Codex kyoso-review: ${setup.codex.skill ? "ok" : "missing"}`);
|
|
186428
188132
|
lines.push(` Claude Code kyoso-review: ${setup["claude-code"].skill ? "ok" : "missing"}`);
|
|
186429
|
-
|
|
186430
|
-
|
|
186431
|
-
|
|
186432
|
-
if (!setup["claude-code"].skill) {
|
|
186433
|
-
lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
|
|
186434
|
-
}
|
|
188133
|
+
lines.push("", "Integration");
|
|
188134
|
+
appendIntegration(lines, codexIntegration);
|
|
188135
|
+
appendIntegration(lines, claudeIntegration);
|
|
186435
188136
|
lines.push("", "ACP agents");
|
|
186436
188137
|
const agentCommandExists = {
|
|
186437
|
-
codex:
|
|
186438
|
-
claude:
|
|
188138
|
+
codex: commandExists3(loaded.config.agents.codex.command, env),
|
|
188139
|
+
claude: commandExists3(loaded.config.agents.claude.command, env)
|
|
186439
188140
|
};
|
|
186440
188141
|
for (const agent of ["codex", "claude"]) {
|
|
186441
188142
|
const config2 = loaded.config.agents[agent];
|
|
186442
188143
|
const exists3 = agentCommandExists[agent];
|
|
186443
188144
|
lines.push(` ${agent === "codex" ? "Codex" : "Claude"}: ${exists3 ? "ok" : "warning command not found"}`);
|
|
186444
188145
|
lines.push(` command: ${[config2.command, ...config2.args].join(" ")}`);
|
|
186445
|
-
if (!exists3 && config2.command === "npx" &&
|
|
188146
|
+
if (!exists3 && config2.command === "npx" && commandExists3("bunx", env)) {
|
|
186446
188147
|
lines.push(' hint: set agents.<name>.command = "bunx" in config.toml');
|
|
186447
188148
|
}
|
|
186448
188149
|
if (agent === "claude") {
|
|
@@ -186477,10 +188178,209 @@ async function runDoctor(options) {
|
|
|
186477
188178
|
lines.push(` network default: ${loaded.config.network.defaultMode}`);
|
|
186478
188179
|
lines.push("", "Audit");
|
|
186479
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"}`);
|
|
186480
188186
|
lines.push(` raw agent output: ${loaded.config.audit.includeRawAgentOutput ? "enabled" : "disabled"}`);
|
|
186481
188187
|
return lines.join(`
|
|
186482
188188
|
`);
|
|
186483
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
|
+
}
|
|
186484
188384
|
function formatLayer(loaded, layer, defaultPath) {
|
|
186485
188385
|
const source = loaded.sources.find((candidate) => candidate.layer === layer);
|
|
186486
188386
|
return source ? `found ${source.path}` : `not found ${defaultPath}`;
|
|
@@ -186514,11 +188414,11 @@ function formatClaudeDualAuthWarning(preferApiKey) {
|
|
|
186514
188414
|
function hasEnv2(env, key) {
|
|
186515
188415
|
return typeof env[key] === "string" && env[key].trim().length > 0;
|
|
186516
188416
|
}
|
|
186517
|
-
function
|
|
186518
|
-
const paths = env.PATH?.split(
|
|
188417
|
+
function commandExists3(command, env) {
|
|
188418
|
+
const paths = env.PATH?.split(delimiter3) ?? [];
|
|
186519
188419
|
return paths.some((path) => {
|
|
186520
188420
|
try {
|
|
186521
|
-
|
|
188421
|
+
accessSync2(`${path}/${command}`);
|
|
186522
188422
|
return true;
|
|
186523
188423
|
} catch {
|
|
186524
188424
|
return false;
|
|
@@ -186527,13 +188427,13 @@ function commandExists2(command, env) {
|
|
|
186527
188427
|
}
|
|
186528
188428
|
|
|
186529
188429
|
// src/cli/init.ts
|
|
186530
|
-
import { access as access3, readFile as
|
|
186531
|
-
import { join as
|
|
188430
|
+
import { access as access3, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
|
|
188431
|
+
import { join as join7 } from "node:path";
|
|
186532
188432
|
async function runInit(options) {
|
|
186533
|
-
const configPath =
|
|
186534
|
-
const tsConfigPath =
|
|
186535
|
-
const skillPath =
|
|
186536
|
-
const gitignorePath =
|
|
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");
|
|
186537
188437
|
const configResult = await writeFileWithOverwritePrompt(configPath, CONFIG_TEMPLATE, options.force);
|
|
186538
188438
|
const skillResult = await writeFileWithOverwritePrompt(skillPath, SKILL_TEMPLATE, options.force);
|
|
186539
188439
|
const gitignoreResult = await ensureGitignoreEntry(gitignorePath, ".kyoso/");
|
|
@@ -186551,11 +188451,11 @@ async function runInit(options) {
|
|
|
186551
188451
|
async function ensureGitignoreEntry(path, entry) {
|
|
186552
188452
|
let content = "";
|
|
186553
188453
|
try {
|
|
186554
|
-
content = await
|
|
188454
|
+
content = await readFile7(path, "utf8");
|
|
186555
188455
|
} catch (error51) {
|
|
186556
|
-
if (!
|
|
188456
|
+
if (!isMissingPathError5(error51))
|
|
186557
188457
|
throw error51;
|
|
186558
|
-
await
|
|
188458
|
+
await writeFile5(path, `${entry}
|
|
186559
188459
|
`, "utf8");
|
|
186560
188460
|
return "created";
|
|
186561
188461
|
}
|
|
@@ -186565,11 +188465,11 @@ async function ensureGitignoreEntry(path, entry) {
|
|
|
186565
188465
|
const separator = content.length > 0 && !content.endsWith(`
|
|
186566
188466
|
`) ? `
|
|
186567
188467
|
` : "";
|
|
186568
|
-
await
|
|
188468
|
+
await writeFile5(path, `${content}${separator}${entry}
|
|
186569
188469
|
`, "utf8");
|
|
186570
188470
|
return "updated";
|
|
186571
188471
|
}
|
|
186572
|
-
function
|
|
188472
|
+
function isMissingPathError5(error51) {
|
|
186573
188473
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
186574
188474
|
}
|
|
186575
188475
|
async function exists3(path) {
|
|
@@ -187052,11 +188952,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187052
188952
|
const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`;
|
|
187053
188953
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
187054
188954
|
}
|
|
187055
|
-
optimizeNames(names,
|
|
188955
|
+
optimizeNames(names, constants2) {
|
|
187056
188956
|
if (!names[this.name.str])
|
|
187057
188957
|
return;
|
|
187058
188958
|
if (this.rhs)
|
|
187059
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
188959
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
187060
188960
|
return this;
|
|
187061
188961
|
}
|
|
187062
188962
|
get names() {
|
|
@@ -187073,10 +188973,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187073
188973
|
render({ _n }) {
|
|
187074
188974
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
187075
188975
|
}
|
|
187076
|
-
optimizeNames(names,
|
|
188976
|
+
optimizeNames(names, constants2) {
|
|
187077
188977
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
187078
188978
|
return;
|
|
187079
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
188979
|
+
this.rhs = optimizeExpr(this.rhs, names, constants2);
|
|
187080
188980
|
return this;
|
|
187081
188981
|
}
|
|
187082
188982
|
get names() {
|
|
@@ -187135,8 +189035,8 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187135
189035
|
optimizeNodes() {
|
|
187136
189036
|
return `${this.code}` ? this : undefined;
|
|
187137
189037
|
}
|
|
187138
|
-
optimizeNames(names,
|
|
187139
|
-
this.code = optimizeExpr(this.code, names,
|
|
189038
|
+
optimizeNames(names, constants2) {
|
|
189039
|
+
this.code = optimizeExpr(this.code, names, constants2);
|
|
187140
189040
|
return this;
|
|
187141
189041
|
}
|
|
187142
189042
|
get names() {
|
|
@@ -187165,12 +189065,12 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187165
189065
|
}
|
|
187166
189066
|
return nodes.length > 0 ? this : undefined;
|
|
187167
189067
|
}
|
|
187168
|
-
optimizeNames(names,
|
|
189068
|
+
optimizeNames(names, constants2) {
|
|
187169
189069
|
const { nodes } = this;
|
|
187170
189070
|
let i = nodes.length;
|
|
187171
189071
|
while (i--) {
|
|
187172
189072
|
const n = nodes[i];
|
|
187173
|
-
if (n.optimizeNames(names,
|
|
189073
|
+
if (n.optimizeNames(names, constants2))
|
|
187174
189074
|
continue;
|
|
187175
189075
|
subtractNames(names, n.names);
|
|
187176
189076
|
nodes.splice(i, 1);
|
|
@@ -187223,12 +189123,12 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187223
189123
|
return;
|
|
187224
189124
|
return this;
|
|
187225
189125
|
}
|
|
187226
|
-
optimizeNames(names,
|
|
189126
|
+
optimizeNames(names, constants2) {
|
|
187227
189127
|
var _a3;
|
|
187228
|
-
this.else = (_a3 = this.else) === null || _a3 === undefined ? undefined : _a3.optimizeNames(names,
|
|
187229
|
-
if (!(super.optimizeNames(names,
|
|
189128
|
+
this.else = (_a3 = this.else) === null || _a3 === undefined ? undefined : _a3.optimizeNames(names, constants2);
|
|
189129
|
+
if (!(super.optimizeNames(names, constants2) || this.else))
|
|
187230
189130
|
return;
|
|
187231
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
189131
|
+
this.condition = optimizeExpr(this.condition, names, constants2);
|
|
187232
189132
|
return this;
|
|
187233
189133
|
}
|
|
187234
189134
|
get names() {
|
|
@@ -187251,10 +189151,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187251
189151
|
render(opts) {
|
|
187252
189152
|
return `for(${this.iteration})` + super.render(opts);
|
|
187253
189153
|
}
|
|
187254
|
-
optimizeNames(names,
|
|
187255
|
-
if (!super.optimizeNames(names,
|
|
189154
|
+
optimizeNames(names, constants2) {
|
|
189155
|
+
if (!super.optimizeNames(names, constants2))
|
|
187256
189156
|
return;
|
|
187257
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
189157
|
+
this.iteration = optimizeExpr(this.iteration, names, constants2);
|
|
187258
189158
|
return this;
|
|
187259
189159
|
}
|
|
187260
189160
|
get names() {
|
|
@@ -187289,10 +189189,10 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187289
189189
|
render(opts) {
|
|
187290
189190
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
187291
189191
|
}
|
|
187292
|
-
optimizeNames(names,
|
|
187293
|
-
if (!super.optimizeNames(names,
|
|
189192
|
+
optimizeNames(names, constants2) {
|
|
189193
|
+
if (!super.optimizeNames(names, constants2))
|
|
187294
189194
|
return;
|
|
187295
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
189195
|
+
this.iterable = optimizeExpr(this.iterable, names, constants2);
|
|
187296
189196
|
return this;
|
|
187297
189197
|
}
|
|
187298
189198
|
get names() {
|
|
@@ -187333,11 +189233,11 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187333
189233
|
(_b = this.finally) === null || _b === undefined || _b.optimizeNodes();
|
|
187334
189234
|
return this;
|
|
187335
189235
|
}
|
|
187336
|
-
optimizeNames(names,
|
|
189236
|
+
optimizeNames(names, constants2) {
|
|
187337
189237
|
var _a3, _b;
|
|
187338
|
-
super.optimizeNames(names,
|
|
187339
|
-
(_a3 = this.catch) === null || _a3 === undefined || _a3.optimizeNames(names,
|
|
187340
|
-
(_b = this.finally) === null || _b === undefined || _b.optimizeNames(names,
|
|
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);
|
|
187341
189241
|
return this;
|
|
187342
189242
|
}
|
|
187343
189243
|
get names() {
|
|
@@ -187606,7 +189506,7 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187606
189506
|
function addExprNames(names, from) {
|
|
187607
189507
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
187608
189508
|
}
|
|
187609
|
-
function optimizeExpr(expr, names,
|
|
189509
|
+
function optimizeExpr(expr, names, constants2) {
|
|
187610
189510
|
if (expr instanceof code_1.Name)
|
|
187611
189511
|
return replaceName(expr);
|
|
187612
189512
|
if (!canOptimize(expr))
|
|
@@ -187621,14 +189521,14 @@ var require_codegen = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
187621
189521
|
return items;
|
|
187622
189522
|
}, []));
|
|
187623
189523
|
function replaceName(n) {
|
|
187624
|
-
const c =
|
|
189524
|
+
const c = constants2[n.str];
|
|
187625
189525
|
if (c === undefined || names[n.str] !== 1)
|
|
187626
189526
|
return n;
|
|
187627
189527
|
delete names[n.str];
|
|
187628
189528
|
return c;
|
|
187629
189529
|
}
|
|
187630
189530
|
function canOptimize(e) {
|
|
187631
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
189531
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== undefined);
|
|
187632
189532
|
}
|
|
187633
189533
|
}
|
|
187634
189534
|
function subtractNames(names, from) {
|
|
@@ -188765,8 +190665,8 @@ var require_resolve = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
188765
190665
|
}
|
|
188766
190666
|
return count;
|
|
188767
190667
|
}
|
|
188768
|
-
function getFullPath(resolver, id = "",
|
|
188769
|
-
if (
|
|
190668
|
+
function getFullPath(resolver, id = "", normalize2) {
|
|
190669
|
+
if (normalize2 !== false)
|
|
188770
190670
|
id = normalizeId(id);
|
|
188771
190671
|
return _getFullPath(resolver, resolver.parse(id));
|
|
188772
190672
|
}
|
|
@@ -189481,7 +191381,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
189481
191381
|
const schOrFunc = root.refs[ref];
|
|
189482
191382
|
if (schOrFunc)
|
|
189483
191383
|
return schOrFunc;
|
|
189484
|
-
let _sch =
|
|
191384
|
+
let _sch = resolve9.call(this, root, ref);
|
|
189485
191385
|
if (_sch === undefined) {
|
|
189486
191386
|
const schema = (_a3 = root.localRefs) === null || _a3 === undefined ? undefined : _a3[ref];
|
|
189487
191387
|
const { schemaId } = this.opts;
|
|
@@ -189512,7 +191412,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
189512
191412
|
function sameSchemaEnv(s1, s2) {
|
|
189513
191413
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
189514
191414
|
}
|
|
189515
|
-
function
|
|
191415
|
+
function resolve9(root, ref) {
|
|
189516
191416
|
let sch;
|
|
189517
191417
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
189518
191418
|
ref = sch;
|
|
@@ -190011,60 +191911,60 @@ var require_schemes = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190011
191911
|
var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
190012
191912
|
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
|
|
190013
191913
|
const { SCHEMES, getSchemeHandler } = require_schemes();
|
|
190014
|
-
function
|
|
191914
|
+
function normalize2(uri, options) {
|
|
190015
191915
|
if (typeof uri === "string")
|
|
190016
191916
|
uri = serialize(parse6(uri, options), options);
|
|
190017
191917
|
else if (typeof uri === "object")
|
|
190018
191918
|
uri = parse6(serialize(uri, options), options);
|
|
190019
191919
|
return uri;
|
|
190020
191920
|
}
|
|
190021
|
-
function
|
|
191921
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
190022
191922
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
190023
191923
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
190024
191924
|
schemelessOptions.skipEscape = true;
|
|
190025
191925
|
return serialize(resolved, schemelessOptions);
|
|
190026
191926
|
}
|
|
190027
|
-
function resolveComponent(base,
|
|
191927
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
190028
191928
|
const target = {};
|
|
190029
191929
|
if (!skipNormalization) {
|
|
190030
191930
|
base = parse6(serialize(base, options), options);
|
|
190031
|
-
|
|
191931
|
+
relative3 = parse6(serialize(relative3, options), options);
|
|
190032
191932
|
}
|
|
190033
191933
|
options = options || {};
|
|
190034
|
-
if (!options.tolerant &&
|
|
190035
|
-
target.scheme =
|
|
190036
|
-
target.userinfo =
|
|
190037
|
-
target.host =
|
|
190038
|
-
target.port =
|
|
190039
|
-
target.path = removeDotSegments(
|
|
190040
|
-
target.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;
|
|
190041
191941
|
} else {
|
|
190042
|
-
if (
|
|
190043
|
-
target.userinfo =
|
|
190044
|
-
target.host =
|
|
190045
|
-
target.port =
|
|
190046
|
-
target.path = removeDotSegments(
|
|
190047
|
-
target.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;
|
|
190048
191948
|
} else {
|
|
190049
|
-
if (!
|
|
191949
|
+
if (!relative3.path) {
|
|
190050
191950
|
target.path = base.path;
|
|
190051
|
-
if (
|
|
190052
|
-
target.query =
|
|
191951
|
+
if (relative3.query !== undefined)
|
|
191952
|
+
target.query = relative3.query;
|
|
190053
191953
|
else
|
|
190054
191954
|
target.query = base.query;
|
|
190055
191955
|
} else {
|
|
190056
|
-
if (
|
|
190057
|
-
target.path = removeDotSegments(
|
|
191956
|
+
if (relative3.path[0] === "/")
|
|
191957
|
+
target.path = removeDotSegments(relative3.path);
|
|
190058
191958
|
else {
|
|
190059
191959
|
if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path)
|
|
190060
|
-
target.path = "/" +
|
|
191960
|
+
target.path = "/" + relative3.path;
|
|
190061
191961
|
else if (!base.path)
|
|
190062
|
-
target.path =
|
|
191962
|
+
target.path = relative3.path;
|
|
190063
191963
|
else
|
|
190064
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
191964
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
190065
191965
|
target.path = removeDotSegments(target.path);
|
|
190066
191966
|
}
|
|
190067
|
-
target.query =
|
|
191967
|
+
target.query = relative3.query;
|
|
190068
191968
|
}
|
|
190069
191969
|
target.userinfo = base.userinfo;
|
|
190070
191970
|
target.host = base.host;
|
|
@@ -190072,7 +191972,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190072
191972
|
}
|
|
190073
191973
|
target.scheme = base.scheme;
|
|
190074
191974
|
}
|
|
190075
|
-
target.fragment =
|
|
191975
|
+
target.fragment = relative3.fragment;
|
|
190076
191976
|
return target;
|
|
190077
191977
|
}
|
|
190078
191978
|
function equal(uriA, uriB, options) {
|
|
@@ -190228,8 +192128,8 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190228
192128
|
}
|
|
190229
192129
|
const fastUri = {
|
|
190230
192130
|
SCHEMES,
|
|
190231
|
-
normalize,
|
|
190232
|
-
resolve:
|
|
192131
|
+
normalize: normalize2,
|
|
192132
|
+
resolve: resolve9,
|
|
190233
192133
|
resolveComponent,
|
|
190234
192134
|
equal,
|
|
190235
192135
|
serialize,
|
|
@@ -197371,14 +199271,14 @@ function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
|
197371
199271
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
197372
199272
|
}
|
|
197373
199273
|
function sleep(ms, signal) {
|
|
197374
|
-
return new Promise((
|
|
199274
|
+
return new Promise((resolve9, reject) => {
|
|
197375
199275
|
if (signal?.aborted) {
|
|
197376
199276
|
reject(signal.reason instanceof SdkError ? signal.reason : new SdkError(SdkErrorCode.RequestTimeout, String(signal.reason)));
|
|
197377
199277
|
return;
|
|
197378
199278
|
}
|
|
197379
199279
|
const timer = setTimeout(() => {
|
|
197380
199280
|
signal?.removeEventListener("abort", onAbort);
|
|
197381
|
-
|
|
199281
|
+
resolve9();
|
|
197382
199282
|
}, ms);
|
|
197383
199283
|
const onAbort = () => {
|
|
197384
199284
|
clearTimeout(timer);
|
|
@@ -198140,7 +200040,7 @@ var Protocol = class {
|
|
|
198140
200040
|
const flowStartedAt = Date.now();
|
|
198141
200041
|
let onAbort;
|
|
198142
200042
|
let cleanupMessageId;
|
|
198143
|
-
return new Promise((
|
|
200043
|
+
return new Promise((resolve9, reject) => {
|
|
198144
200044
|
const earlyReject = (error51) => {
|
|
198145
200045
|
reject(error51);
|
|
198146
200046
|
};
|
|
@@ -198216,7 +200116,7 @@ var Protocol = class {
|
|
|
198216
200116
|
return reject(decoded.error);
|
|
198217
200117
|
if (decoded.kind === "input_required") {
|
|
198218
200118
|
if (options?.allowInputRequired === true)
|
|
198219
|
-
return
|
|
200119
|
+
return resolve9(manualInputRequiredValue(decoded));
|
|
198220
200120
|
const flow = {
|
|
198221
200121
|
codec: codec2,
|
|
198222
200122
|
request,
|
|
@@ -198228,12 +200128,12 @@ var Protocol = class {
|
|
|
198228
200128
|
params
|
|
198229
200129
|
}, resultSchema, legOptions)
|
|
198230
200130
|
};
|
|
198231
|
-
return
|
|
200131
|
+
return resolve9(this._resolveNonCompleteResult(decoded, flow));
|
|
198232
200132
|
}
|
|
198233
200133
|
const result = decoded.result;
|
|
198234
200134
|
validateStandardSchema(resultSchema, result).then((parseResult) => {
|
|
198235
200135
|
if (parseResult.success)
|
|
198236
|
-
|
|
200136
|
+
resolve9(parseResult.data);
|
|
198237
200137
|
else
|
|
198238
200138
|
reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
198239
200139
|
}, reject);
|
|
@@ -199777,7 +201677,7 @@ var StdioServerTransport = class {
|
|
|
199777
201677
|
send(message) {
|
|
199778
201678
|
if (this._closed)
|
|
199779
201679
|
return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
199780
|
-
return new Promise((
|
|
201680
|
+
return new Promise((resolve9, reject) => {
|
|
199781
201681
|
const json2 = serializeMessage(message);
|
|
199782
201682
|
let settled = false;
|
|
199783
201683
|
const onError = (error51) => {
|
|
@@ -199794,7 +201694,7 @@ var StdioServerTransport = class {
|
|
|
199794
201694
|
settled = true;
|
|
199795
201695
|
this._stdout.off("error", onError);
|
|
199796
201696
|
this._stdout.off("drain", onDrain);
|
|
199797
|
-
|
|
201697
|
+
resolve9();
|
|
199798
201698
|
};
|
|
199799
201699
|
this._stdout.once("error", onError);
|
|
199800
201700
|
if (this._stdout.write(json2)) {
|
|
@@ -199802,7 +201702,7 @@ var StdioServerTransport = class {
|
|
|
199802
201702
|
return;
|
|
199803
201703
|
settled = true;
|
|
199804
201704
|
this._stdout.off("error", onError);
|
|
199805
|
-
|
|
201705
|
+
resolve9();
|
|
199806
201706
|
} else if (!settled)
|
|
199807
201707
|
this._stdout.once("drain", onDrain);
|
|
199808
201708
|
});
|
|
@@ -199810,12 +201710,96 @@ var StdioServerTransport = class {
|
|
|
199810
201710
|
};
|
|
199811
201711
|
|
|
199812
201712
|
// src/core/runReview.ts
|
|
199813
|
-
import { resolve as
|
|
201713
|
+
import { resolve as resolve10 } from "node:path";
|
|
201714
|
+
|
|
201715
|
+
// src/config/configOverrides.ts
|
|
201716
|
+
var NUMBER_VALUE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
201717
|
+
function applyConfigOverrides(config2, assignments) {
|
|
201718
|
+
if (assignments.length === 0)
|
|
201719
|
+
return config2;
|
|
201720
|
+
const overrides = assignments.map(parseConfigOverride);
|
|
201721
|
+
const baseConfig = config2;
|
|
201722
|
+
const overridden = structuredClone(config2);
|
|
201723
|
+
for (const override of overrides) {
|
|
201724
|
+
writePath2(overridden, override.path, parseConfigOverrideValue(override.value, readPath2(baseConfig, override.path)));
|
|
201725
|
+
}
|
|
201726
|
+
const parsed = kyosoConfigSchema.safeParse(overridden);
|
|
201727
|
+
if (parsed.success)
|
|
201728
|
+
return parsed.data;
|
|
201729
|
+
const issue2 = parsed.error.issues[0];
|
|
201730
|
+
const issuePath = issue2?.path.map(String).join(".") ?? "config";
|
|
201731
|
+
const assignment = findAssignmentForPath(overrides, issuePath) ?? assignments.at(-1) ?? "";
|
|
201732
|
+
throw new Error(`Invalid --set value ${JSON.stringify(assignment)}: ${issuePath}: ${issue2?.message ?? "config validation failed"}.`);
|
|
201733
|
+
}
|
|
201734
|
+
function findAssignmentForPath(overrides, path) {
|
|
201735
|
+
for (let index = overrides.length - 1;index >= 0; index -= 1) {
|
|
201736
|
+
const override = overrides[index];
|
|
201737
|
+
if (override?.path.join(".") === path)
|
|
201738
|
+
return override.assignment;
|
|
201739
|
+
}
|
|
201740
|
+
return;
|
|
201741
|
+
}
|
|
201742
|
+
function parseConfigOverride(assignment) {
|
|
201743
|
+
const separator = assignment.indexOf("=");
|
|
201744
|
+
if (separator <= 0) {
|
|
201745
|
+
throw new Error(`Invalid --set value ${JSON.stringify(assignment)}. Expected key=value.`);
|
|
201746
|
+
}
|
|
201747
|
+
const key = assignment.slice(0, separator);
|
|
201748
|
+
const path = key.split(".");
|
|
201749
|
+
if (!isAllowedConfigOverridePath(path)) {
|
|
201750
|
+
throw new Error(`Unknown --set key ${JSON.stringify(key)}.`);
|
|
201751
|
+
}
|
|
201752
|
+
return {
|
|
201753
|
+
assignment,
|
|
201754
|
+
path,
|
|
201755
|
+
value: assignment.slice(separator + 1)
|
|
201756
|
+
};
|
|
201757
|
+
}
|
|
201758
|
+
function parseConfigOverrideValue(value, currentValue) {
|
|
201759
|
+
if (typeof currentValue === "boolean") {
|
|
201760
|
+
if (value === "true")
|
|
201761
|
+
return true;
|
|
201762
|
+
if (value === "false")
|
|
201763
|
+
return false;
|
|
201764
|
+
return value;
|
|
201765
|
+
}
|
|
201766
|
+
if (typeof currentValue === "number" && NUMBER_VALUE.test(value)) {
|
|
201767
|
+
const parsed = Number(value);
|
|
201768
|
+
if (Number.isFinite(parsed))
|
|
201769
|
+
return parsed;
|
|
201770
|
+
}
|
|
201771
|
+
return value;
|
|
201772
|
+
}
|
|
201773
|
+
function readPath2(target, path) {
|
|
201774
|
+
let current = target;
|
|
201775
|
+
for (const key of path) {
|
|
201776
|
+
if (!isRecord8(current))
|
|
201777
|
+
return;
|
|
201778
|
+
current = current[key];
|
|
201779
|
+
}
|
|
201780
|
+
return current;
|
|
201781
|
+
}
|
|
201782
|
+
function writePath2(target, path, value) {
|
|
201783
|
+
let current = target;
|
|
201784
|
+
for (const key of path.slice(0, -1)) {
|
|
201785
|
+
const child = current[key];
|
|
201786
|
+
if (!isRecord8(child)) {
|
|
201787
|
+
throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
|
|
201788
|
+
}
|
|
201789
|
+
current = child;
|
|
201790
|
+
}
|
|
201791
|
+
const leaf = path.at(-1);
|
|
201792
|
+
if (leaf)
|
|
201793
|
+
current[leaf] = value;
|
|
201794
|
+
}
|
|
201795
|
+
function isRecord8(value) {
|
|
201796
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
201797
|
+
}
|
|
199814
201798
|
|
|
199815
201799
|
// src/acp/AcpAgentProcess.ts
|
|
199816
201800
|
import { spawn } from "node:child_process";
|
|
199817
|
-
import { readFile as
|
|
199818
|
-
import { isAbsolute, relative, resolve as
|
|
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";
|
|
199819
201803
|
import { Readable, Writable } from "node:stream";
|
|
199820
201804
|
|
|
199821
201805
|
// node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
@@ -199887,6 +201871,48 @@ function requiredDefaultOnError(schema, fallback) {
|
|
|
199887
201871
|
return exports_external.NEVER;
|
|
199888
201872
|
});
|
|
199889
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
|
+
}
|
|
199890
201916
|
function vecSkipError(itemSchema) {
|
|
199891
201917
|
return exports_external.array(itemSchema.catch(skippedItem)).transform((items) => items.filter((item) => item !== skippedItem));
|
|
199892
201918
|
}
|
|
@@ -200310,15 +202336,15 @@ var zTitledMultiSelectItems = object({
|
|
|
200310
202336
|
return;
|
|
200311
202337
|
})
|
|
200312
202338
|
});
|
|
200313
|
-
var zMultiSelectItems = union([
|
|
202339
|
+
var zMultiSelectItems = preserveCustomPayload(union([
|
|
200314
202340
|
zStringMultiSelectItems.and(object({
|
|
200315
202341
|
type: literal("string")
|
|
200316
202342
|
})),
|
|
200317
|
-
object({
|
|
202343
|
+
excludeKnownTags(object({
|
|
200318
202344
|
type: string2()
|
|
200319
|
-
}),
|
|
202345
|
+
}), "type", ["string"]),
|
|
200320
202346
|
zTitledMultiSelectItems
|
|
200321
|
-
]);
|
|
202347
|
+
]), "type", ["string"]);
|
|
200322
202348
|
var zMultiSelectPropertySchema = object({
|
|
200323
202349
|
title: defaultOnError(string2().nullish(), () => {
|
|
200324
202350
|
return;
|
|
@@ -200336,7 +202362,7 @@ var zMultiSelectPropertySchema = object({
|
|
|
200336
202362
|
return;
|
|
200337
202363
|
})
|
|
200338
202364
|
});
|
|
200339
|
-
var zElicitationPropertySchema = union([
|
|
202365
|
+
var zElicitationPropertySchema = preserveCustomPayload(union([
|
|
200340
202366
|
zStringPropertySchema.and(object({
|
|
200341
202367
|
type: literal("string")
|
|
200342
202368
|
})),
|
|
@@ -200352,10 +202378,10 @@ var zElicitationPropertySchema = union([
|
|
|
200352
202378
|
zMultiSelectPropertySchema.and(object({
|
|
200353
202379
|
type: literal("array")
|
|
200354
202380
|
})),
|
|
200355
|
-
object({
|
|
202381
|
+
excludeKnownTags(object({
|
|
200356
202382
|
type: string2()
|
|
200357
|
-
})
|
|
200358
|
-
]);
|
|
202383
|
+
}), "type", ["array", "boolean", "integer", "number", "string"])
|
|
202384
|
+
]), "type", ["array", "boolean", "integer", "number", "string"]);
|
|
200359
202385
|
var zElicitationSchema = object({
|
|
200360
202386
|
type: defaultOnError(zElicitationSchemaType.optional().default("object"), () => "object"),
|
|
200361
202387
|
title: defaultOnError(string2().nullish(), () => {
|
|
@@ -200378,22 +202404,22 @@ var zElicitationUrlMode = intersection(union([zElicitationSessionScope, zElicita
|
|
|
200378
202404
|
elicitationId: zElicitationId,
|
|
200379
202405
|
url: url()
|
|
200380
202406
|
}));
|
|
200381
|
-
var zCreateElicitationRequest = intersection(union([
|
|
202407
|
+
var zCreateElicitationRequest = preserveCustomPayload(intersection(union([
|
|
200382
202408
|
zElicitationFormMode.and(object({
|
|
200383
202409
|
mode: literal("form")
|
|
200384
202410
|
})),
|
|
200385
202411
|
zElicitationUrlMode.and(object({
|
|
200386
202412
|
mode: literal("url")
|
|
200387
202413
|
})),
|
|
200388
|
-
intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
|
|
202414
|
+
excludeKnownTags(intersection(union([zElicitationSessionScope, zElicitationRequestScope]), object({
|
|
200389
202415
|
mode: string2()
|
|
200390
|
-
}))
|
|
202416
|
+
})), "mode", ["form", "url"])
|
|
200391
202417
|
]), object({
|
|
200392
202418
|
message: string2(),
|
|
200393
202419
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
200394
202420
|
return;
|
|
200395
202421
|
})
|
|
200396
|
-
}));
|
|
202422
|
+
})), "mode", ["form", "url"]);
|
|
200397
202423
|
var zMcpServerAcpId = string2();
|
|
200398
202424
|
var zConnectMcpRequest = object({
|
|
200399
202425
|
serverId: zMcpServerAcpId,
|
|
@@ -201993,7 +204019,7 @@ var zElicitationContentValue = union([
|
|
|
201993
204019
|
var zElicitationAcceptAction = object({
|
|
201994
204020
|
content: record(string2(), zElicitationContentValue).nullish()
|
|
201995
204021
|
});
|
|
201996
|
-
var zCreateElicitationResponse = intersection(union([
|
|
204022
|
+
var zCreateElicitationResponse = preserveCustomPayload(intersection(union([
|
|
201997
204023
|
zElicitationAcceptAction.and(object({
|
|
201998
204024
|
action: literal("accept")
|
|
201999
204025
|
})),
|
|
@@ -202003,14 +204029,14 @@ var zCreateElicitationResponse = intersection(union([
|
|
|
202003
204029
|
object({
|
|
202004
204030
|
action: literal("cancel")
|
|
202005
204031
|
}),
|
|
202006
|
-
object({
|
|
204032
|
+
excludeKnownTags(object({
|
|
202007
204033
|
action: string2()
|
|
202008
|
-
})
|
|
204034
|
+
}), "action", ["accept", "cancel", "decline"])
|
|
202009
204035
|
]), object({
|
|
202010
204036
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
202011
204037
|
return;
|
|
202012
204038
|
})
|
|
202013
|
-
}));
|
|
204039
|
+
})), "action", ["accept", "cancel", "decline"]);
|
|
202014
204040
|
var zConnectMcpResponse = object({
|
|
202015
204041
|
connectionId: zMcpConnectionId,
|
|
202016
204042
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
@@ -202146,16 +204172,34 @@ var zCancelRequestNotification = object({
|
|
|
202146
204172
|
return;
|
|
202147
204173
|
})
|
|
202148
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
|
+
});
|
|
202149
204193
|
// node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
202150
204194
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
202151
|
-
function
|
|
204195
|
+
function isRecord9(value) {
|
|
202152
204196
|
return typeof value === "object" && value !== null;
|
|
202153
204197
|
}
|
|
202154
204198
|
function isJsonRpcId(value) {
|
|
202155
204199
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
202156
204200
|
}
|
|
202157
204201
|
function cancelRequestId(params) {
|
|
202158
|
-
if (!
|
|
204202
|
+
if (!isRecord9(params) || !isJsonRpcId(params["requestId"])) {
|
|
202159
204203
|
return;
|
|
202160
204204
|
}
|
|
202161
204205
|
return params["requestId"];
|
|
@@ -202379,12 +204423,12 @@ class Connection {
|
|
|
202379
204423
|
}
|
|
202380
204424
|
const id = this.nextRequestId++;
|
|
202381
204425
|
let cancel = () => {};
|
|
202382
|
-
const responsePromise = new Promise((
|
|
204426
|
+
const responsePromise = new Promise((resolve9, reject) => {
|
|
202383
204427
|
const pendingResponse = {
|
|
202384
204428
|
resolve: (response) => {
|
|
202385
204429
|
try {
|
|
202386
204430
|
const value = mapResponse ? mapResponse(response) : response;
|
|
202387
|
-
|
|
204431
|
+
resolve9(value);
|
|
202388
204432
|
} catch (error51) {
|
|
202389
204433
|
reject(error51);
|
|
202390
204434
|
}
|
|
@@ -202449,8 +204493,8 @@ class Connection {
|
|
|
202449
204493
|
initialize(stream, handlers) {
|
|
202450
204494
|
this.stream = stream;
|
|
202451
204495
|
this.staticHandlers = handlers;
|
|
202452
|
-
this.closedPromise = new Promise((
|
|
202453
|
-
this.abortController.signal.addEventListener("abort", () =>
|
|
204496
|
+
this.closedPromise = new Promise((resolve9) => {
|
|
204497
|
+
this.abortController.signal.addEventListener("abort", () => resolve9());
|
|
202454
204498
|
});
|
|
202455
204499
|
this.receive();
|
|
202456
204500
|
}
|
|
@@ -202502,7 +204546,7 @@ class Connection {
|
|
|
202502
204546
|
if (this.abortController.signal.aborted) {
|
|
202503
204547
|
return;
|
|
202504
204548
|
}
|
|
202505
|
-
if (!
|
|
204549
|
+
if (!isRecord9(message)) {
|
|
202506
204550
|
console.error("Invalid message", { message });
|
|
202507
204551
|
return;
|
|
202508
204552
|
}
|
|
@@ -202595,7 +204639,7 @@ class Connection {
|
|
|
202595
204639
|
pendingResponse.cleanup?.();
|
|
202596
204640
|
if ("result" in response) {
|
|
202597
204641
|
pendingResponse.resolve(response.result);
|
|
202598
|
-
} else if ("error" in response &&
|
|
204642
|
+
} else if ("error" in response && isRecord9(response.error)) {
|
|
202599
204643
|
const { code, message, data } = response.error;
|
|
202600
204644
|
pendingResponse.reject(new RequestError(code, message, data));
|
|
202601
204645
|
} else {
|
|
@@ -202805,7 +204849,7 @@ function ndJsonStream(output2, input2) {
|
|
|
202805
204849
|
if (trimmedLine) {
|
|
202806
204850
|
try {
|
|
202807
204851
|
const message = JSON.parse(trimmedLine);
|
|
202808
|
-
if (
|
|
204852
|
+
if (isRecord9(message)) {
|
|
202809
204853
|
controller.enqueue(message);
|
|
202810
204854
|
} else {
|
|
202811
204855
|
console.warn("Skipping JSON line that is not an object:", trimmedLine);
|
|
@@ -203163,8 +205207,8 @@ class AsyncQueue {
|
|
|
203163
205207
|
if (this.failed) {
|
|
203164
205208
|
return Promise.reject(this.failure);
|
|
203165
205209
|
}
|
|
203166
|
-
return new Promise((
|
|
203167
|
-
this.waiters.push({ resolve:
|
|
205210
|
+
return new Promise((resolve9, reject) => {
|
|
205211
|
+
this.waiters.push({ resolve: resolve9, reject });
|
|
203168
205212
|
});
|
|
203169
205213
|
}
|
|
203170
205214
|
}
|
|
@@ -203866,7 +205910,7 @@ function isSeverity(value) {
|
|
|
203866
205910
|
return typeof value === "string" && severities.includes(value);
|
|
203867
205911
|
}
|
|
203868
205912
|
function normalizeCisaSecureByDesign(value) {
|
|
203869
|
-
if (!
|
|
205913
|
+
if (!isRecord10(value))
|
|
203870
205914
|
return;
|
|
203871
205915
|
const normalized = {};
|
|
203872
205916
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -203907,7 +205951,7 @@ function normalizeFindingFiles(value) {
|
|
|
203907
205951
|
if (!Array.isArray(value))
|
|
203908
205952
|
return;
|
|
203909
205953
|
const files = value.flatMap((item) => {
|
|
203910
|
-
if (!
|
|
205954
|
+
if (!isRecord10(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
203911
205955
|
return [];
|
|
203912
205956
|
}
|
|
203913
205957
|
const file2 = {
|
|
@@ -203926,7 +205970,7 @@ function normalizeFindingFiles(value) {
|
|
|
203926
205970
|
function normalizeLineNumber(value) {
|
|
203927
205971
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
203928
205972
|
}
|
|
203929
|
-
function
|
|
205973
|
+
function isRecord10(value) {
|
|
203930
205974
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
203931
205975
|
}
|
|
203932
205976
|
|
|
@@ -204129,14 +206173,14 @@ function resolveEffortConfigOption(agent, effort) {
|
|
|
204129
206173
|
return;
|
|
204130
206174
|
}
|
|
204131
206175
|
async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
|
|
204132
|
-
const workspaceRoot = await
|
|
206176
|
+
const workspaceRoot = await realpath3(workspaceDir);
|
|
204133
206177
|
const candidates = resolveReadablePaths(workspaceRoot, requestedPath);
|
|
204134
206178
|
let content;
|
|
204135
206179
|
for (const absolute of candidates) {
|
|
204136
206180
|
const readablePath = await resolveReadableFile(workspaceRoot, absolute);
|
|
204137
206181
|
if (!readablePath)
|
|
204138
206182
|
continue;
|
|
204139
|
-
content = await
|
|
206183
|
+
content = await readFile8(readablePath, "utf8").catch(() => {
|
|
204140
206184
|
return;
|
|
204141
206185
|
});
|
|
204142
206186
|
if (content !== undefined)
|
|
@@ -204152,8 +206196,8 @@ async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
|
|
|
204152
206196
|
`);
|
|
204153
206197
|
}
|
|
204154
206198
|
async function resolveReadableFile(workspaceRoot, absolute) {
|
|
204155
|
-
const realPath = await
|
|
204156
|
-
if (
|
|
206199
|
+
const realPath = await realpath3(absolute).catch((error51) => {
|
|
206200
|
+
if (isMissingPathError6(error51))
|
|
204157
206201
|
return;
|
|
204158
206202
|
throw error51;
|
|
204159
206203
|
});
|
|
@@ -204163,18 +206207,18 @@ async function resolveReadableFile(workspaceRoot, absolute) {
|
|
|
204163
206207
|
return realPath;
|
|
204164
206208
|
}
|
|
204165
206209
|
function resolveReadablePaths(workspaceRoot, requestedPath) {
|
|
204166
|
-
const primary =
|
|
206210
|
+
const primary = resolve9(workspaceRoot, requestedPath);
|
|
204167
206211
|
assertWithinWorkspace(workspaceRoot, primary);
|
|
204168
|
-
const relativePath =
|
|
206212
|
+
const relativePath = relative3(workspaceRoot, primary).replaceAll("\\", "/");
|
|
204169
206213
|
if (relativePath.startsWith("context/") || relativePath.startsWith("repo/")) {
|
|
204170
206214
|
return [primary];
|
|
204171
206215
|
}
|
|
204172
|
-
const repoPath =
|
|
206216
|
+
const repoPath = resolve9(workspaceRoot, "repo", relativePath);
|
|
204173
206217
|
assertWithinWorkspace(workspaceRoot, repoPath);
|
|
204174
|
-
return
|
|
206218
|
+
return isAbsolute4(requestedPath) ? [primary, repoPath] : [repoPath, primary];
|
|
204175
206219
|
}
|
|
204176
206220
|
function assertWithinWorkspace(workspaceRoot, absolute) {
|
|
204177
|
-
const relativePath =
|
|
206221
|
+
const relativePath = relative3(workspaceRoot, absolute);
|
|
204178
206222
|
if (relativePath.startsWith("..") || relativePath === "" || relativePath.startsWith("/")) {
|
|
204179
206223
|
throw RequestError.invalidRequest({
|
|
204180
206224
|
policy: "Kyoso only allows reads from the temporary snapshot."
|
|
@@ -204191,7 +206235,7 @@ function terminateChild(child) {
|
|
|
204191
206235
|
}, 2000);
|
|
204192
206236
|
killTimer.unref();
|
|
204193
206237
|
}
|
|
204194
|
-
function
|
|
206238
|
+
function isMissingPathError6(error51) {
|
|
204195
206239
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
204196
206240
|
}
|
|
204197
206241
|
function buildAgentFailure(rawDetail, fallbackMessage) {
|
|
@@ -204887,68 +206931,78 @@ function normalizeTitle(value) {
|
|
|
204887
206931
|
return value.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
204888
206932
|
}
|
|
204889
206933
|
|
|
204890
|
-
// src/audit/
|
|
204891
|
-
import {
|
|
204892
|
-
import {
|
|
204893
|
-
|
|
204894
|
-
|
|
204895
|
-
|
|
204896
|
-
|
|
204897
|
-
|
|
204898
|
-
|
|
204899
|
-
|
|
204900
|
-
|
|
204901
|
-
|
|
204902
|
-
|
|
204903
|
-
|
|
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");
|
|
204904
206959
|
}
|
|
204905
|
-
|
|
204906
|
-
|
|
204907
|
-
|
|
204908
|
-
|
|
204909
|
-
|
|
204910
|
-
|
|
204911
|
-
|
|
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;
|
|
204912
206977
|
}
|
|
204913
|
-
return normalized;
|
|
204914
206978
|
}
|
|
204915
|
-
function
|
|
204916
|
-
|
|
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);
|
|
204917
206993
|
}
|
|
204918
|
-
function
|
|
204919
|
-
|
|
204920
|
-
return true;
|
|
204921
|
-
return matchesPathPattern(path, allowPatterns, "allow");
|
|
206994
|
+
function isSafeTraceId(traceId) {
|
|
206995
|
+
return traceId.length > 0 && traceId !== "." && traceId !== ".." && !traceId.includes("/") && !traceId.includes("\\");
|
|
204922
206996
|
}
|
|
204923
|
-
function
|
|
204924
|
-
|
|
204925
|
-
|
|
204926
|
-
|
|
204927
|
-
|
|
204928
|
-
|
|
204929
|
-
const base = normalizedPattern.slice(0, -2);
|
|
204930
|
-
if (mode === "allow") {
|
|
204931
|
-
const firstSegment = segments[0] ?? "";
|
|
204932
|
-
return firstSegment === base || firstSegment.startsWith(`${base}.`);
|
|
204933
|
-
}
|
|
204934
|
-
return segments.some((segment) => segment === base || segment.startsWith(`${base}.`));
|
|
204935
|
-
}
|
|
204936
|
-
if (normalizedPattern.includes("*")) {
|
|
204937
|
-
const escaped = normalizedPattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
|
|
204938
|
-
const regex = mode === "allow" ? new RegExp(`^${escaped}($|/)`) : new RegExp(`(^|/)${escaped}($|/)`);
|
|
204939
|
-
return regex.test(normalized);
|
|
204940
|
-
}
|
|
204941
|
-
if (!normalizedPattern.includes("/")) {
|
|
204942
|
-
if (mode === "allow") {
|
|
204943
|
-
return normalized === normalizedPattern || normalized.startsWith(`${normalizedPattern}/`);
|
|
204944
|
-
}
|
|
204945
|
-
return segments.includes(normalizedPattern);
|
|
204946
|
-
}
|
|
204947
|
-
if (mode === "allow") {
|
|
204948
|
-
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;
|
|
204949
207003
|
}
|
|
204950
|
-
|
|
204951
|
-
}
|
|
207004
|
+
throw error51;
|
|
207005
|
+
}
|
|
204952
207006
|
}
|
|
204953
207007
|
|
|
204954
207008
|
// src/audit/sanitize.ts
|
|
@@ -204971,46 +207025,149 @@ function sanitizeForAudit(value, options = {}) {
|
|
|
204971
207025
|
}
|
|
204972
207026
|
|
|
204973
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.";
|
|
204974
207031
|
function createTraceWriter(options) {
|
|
204975
207032
|
const warnings = [];
|
|
204976
|
-
|
|
204977
|
-
return {
|
|
204978
|
-
warnings,
|
|
204979
|
-
async write() {
|
|
204980
|
-
return;
|
|
204981
|
-
}
|
|
204982
|
-
};
|
|
204983
|
-
}
|
|
207033
|
+
const warningSet = new Set;
|
|
204984
207034
|
const date5 = new Date().toISOString().slice(0, 10);
|
|
204985
|
-
|
|
204986
|
-
|
|
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
|
+
};
|
|
204987
207120
|
return {
|
|
204988
|
-
tracePath
|
|
207121
|
+
get tracePath() {
|
|
207122
|
+
return tracePath;
|
|
207123
|
+
},
|
|
204989
207124
|
warnings,
|
|
204990
|
-
|
|
204991
|
-
|
|
204992
|
-
|
|
204993
|
-
|
|
204994
|
-
|
|
204995
|
-
|
|
204996
|
-
|
|
204997
|
-
|
|
204998
|
-
|
|
204999
|
-
|
|
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;
|
|
205000
207151
|
}
|
|
205001
207152
|
};
|
|
205002
207153
|
}
|
|
205003
|
-
function
|
|
205004
|
-
|
|
205005
|
-
|
|
205006
|
-
|
|
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");
|
|
205007
207160
|
}
|
|
205008
|
-
|
|
205009
|
-
} catch {
|
|
205010
|
-
warnings.push(`Unsafe audit directory ignored: ${directory}`);
|
|
205011
|
-
return ".kyoso/traces";
|
|
207161
|
+
offset += bytesWritten;
|
|
205012
207162
|
}
|
|
205013
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
|
+
}
|
|
205014
207171
|
|
|
205015
207172
|
// src/context/truncate.ts
|
|
205016
207173
|
function truncateUtf8(input2, maxBytes) {
|
|
@@ -205427,40 +207584,40 @@ function decide(input2) {
|
|
|
205427
207584
|
}
|
|
205428
207585
|
|
|
205429
207586
|
// src/workspace/createSnapshot.ts
|
|
205430
|
-
import { chmod, mkdir as
|
|
205431
|
-
import { dirname as
|
|
205432
|
-
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";
|
|
205433
207590
|
async function createSnapshot(traceId, tool, request, options = {}) {
|
|
205434
|
-
const root = await
|
|
205435
|
-
const repoDir =
|
|
205436
|
-
const contextDir =
|
|
205437
|
-
await
|
|
205438
|
-
await
|
|
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 });
|
|
205439
207596
|
let fileCount = 0;
|
|
205440
207597
|
for (const file2 of request.selectedFiles ?? []) {
|
|
205441
|
-
const
|
|
205442
|
-
if (isDeniedPath(
|
|
207598
|
+
const relative4 = normalizeRelativePath(file2.path);
|
|
207599
|
+
if (isDeniedPath(relative4, options.denyPatterns ?? []))
|
|
205443
207600
|
continue;
|
|
205444
|
-
if (!isAllowedPath(
|
|
207601
|
+
if (!isAllowedPath(relative4, options.allowPatterns ?? []))
|
|
205445
207602
|
continue;
|
|
205446
|
-
const dest =
|
|
205447
|
-
await
|
|
205448
|
-
await
|
|
207603
|
+
const dest = join9(repoDir, relative4);
|
|
207604
|
+
await mkdir6(dirname8(dest), { recursive: true });
|
|
207605
|
+
await writeFile6(dest, file2.content, "utf8");
|
|
205449
207606
|
await chmod(dest, 292).catch(() => {
|
|
205450
207607
|
return;
|
|
205451
207608
|
});
|
|
205452
207609
|
fileCount += 1;
|
|
205453
207610
|
}
|
|
205454
|
-
await
|
|
205455
|
-
await
|
|
205456
|
-
await
|
|
205457
|
-
await
|
|
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");
|
|
205458
207615
|
if (request.repoSummary)
|
|
205459
|
-
await
|
|
207616
|
+
await writeFile6(join9(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
|
|
205460
207617
|
if (request.currentPlan)
|
|
205461
|
-
await
|
|
207618
|
+
await writeFile6(join9(contextDir, "current_plan.md"), request.currentPlan, "utf8");
|
|
205462
207619
|
if (request.diff?.unifiedDiff)
|
|
205463
|
-
await
|
|
207620
|
+
await writeFile6(join9(contextDir, "diff.patch"), request.diff.unifiedDiff, "utf8");
|
|
205464
207621
|
return { root, repoDir, contextDir, fileCount };
|
|
205465
207622
|
}
|
|
205466
207623
|
function stripContents(request) {
|
|
@@ -205482,17 +207639,17 @@ function buildSelectedFilesManifest(request) {
|
|
|
205482
207639
|
}
|
|
205483
207640
|
|
|
205484
207641
|
// src/workspace/cleanup.ts
|
|
205485
|
-
import { rm } from "node:fs/promises";
|
|
207642
|
+
import { rm as rm2 } from "node:fs/promises";
|
|
205486
207643
|
async function cleanupSnapshot(path) {
|
|
205487
207644
|
if (process.env.KYOSO_KEEP_TEMP === "1")
|
|
205488
207645
|
return;
|
|
205489
|
-
await
|
|
207646
|
+
await rm2(path, { recursive: true, force: true });
|
|
205490
207647
|
}
|
|
205491
207648
|
|
|
205492
207649
|
// src/utils/ids.ts
|
|
205493
|
-
import { randomUUID } from "node:crypto";
|
|
207650
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
205494
207651
|
function newTraceId() {
|
|
205495
|
-
return `tr_${
|
|
207652
|
+
return `tr_${randomUUID2()}`;
|
|
205496
207653
|
}
|
|
205497
207654
|
|
|
205498
207655
|
// src/core/verification.ts
|
|
@@ -205550,7 +207707,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
205550
207707
|
if (!Array.isArray(parsed.verdicts))
|
|
205551
207708
|
return;
|
|
205552
207709
|
return parsed.verdicts.flatMap((item) => {
|
|
205553
|
-
if (!
|
|
207710
|
+
if (!isRecord11(item))
|
|
205554
207711
|
return [];
|
|
205555
207712
|
if (typeof item.findingId !== "string")
|
|
205556
207713
|
return [];
|
|
@@ -205628,7 +207785,7 @@ function verificationNote(reasoning) {
|
|
|
205628
207785
|
function isVerdict(value) {
|
|
205629
207786
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
205630
207787
|
}
|
|
205631
|
-
function
|
|
207788
|
+
function isRecord11(value) {
|
|
205632
207789
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
205633
207790
|
}
|
|
205634
207791
|
|
|
@@ -205637,47 +207794,54 @@ async function runReview(tool, request, options = {}) {
|
|
|
205637
207794
|
const cwd = options.cwd ?? process.cwd();
|
|
205638
207795
|
const traceId = newTraceId();
|
|
205639
207796
|
const startedAt = new Date().toISOString();
|
|
207797
|
+
const auditEnv = { ...process.env, ...options.env };
|
|
207798
|
+
const traceWriterFactory = options.traceWriterFactory ?? createTraceWriter;
|
|
205640
207799
|
let snapshot;
|
|
205641
207800
|
try {
|
|
205642
207801
|
assertNotChildAgent(options.env ?? process.env);
|
|
205643
207802
|
} catch (error51) {
|
|
205644
207803
|
if (error51 instanceof KyosoRequestError) {
|
|
205645
207804
|
const config2 = kyosoConfigSchema.parse(defaultConfig);
|
|
205646
|
-
const trace2 =
|
|
207805
|
+
const trace2 = traceWriterFactory({
|
|
205647
207806
|
enabled: config2.audit.enabled,
|
|
205648
207807
|
directory: config2.audit.directory,
|
|
205649
207808
|
traceId,
|
|
205650
|
-
cwd
|
|
205651
|
-
|
|
205652
|
-
await trace2.write({
|
|
205653
|
-
type: "request_received",
|
|
205654
|
-
traceId,
|
|
205655
|
-
tool,
|
|
205656
|
-
timestamp: new Date().toISOString()
|
|
205657
|
-
});
|
|
205658
|
-
return await buildPolicyBlockResult({
|
|
205659
|
-
tool,
|
|
205660
|
-
trace: trace2,
|
|
205661
|
-
traceId,
|
|
205662
|
-
startedAt,
|
|
205663
|
-
networkMode: config2.network.defaultMode,
|
|
205664
|
-
warning: error51.message,
|
|
205665
|
-
finding: {
|
|
205666
|
-
id: "KYOSO-1",
|
|
205667
|
-
severity: "critical",
|
|
205668
|
-
category: "other",
|
|
205669
|
-
title: "Recursive Kyoso invocation blocked",
|
|
205670
|
-
evidence: error51.message,
|
|
205671
|
-
recommendation: "Do not expose Kyoso MCP tools to Kyoso child agents.",
|
|
205672
|
-
sourceAgents: ["kyoso_policy"],
|
|
205673
|
-
confidence: "high"
|
|
205674
|
-
},
|
|
205675
|
-
redactionsApplied: 0
|
|
207809
|
+
cwd,
|
|
207810
|
+
env: auditEnv
|
|
205676
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
|
+
}
|
|
205677
207841
|
}
|
|
205678
207842
|
throw error51;
|
|
205679
207843
|
}
|
|
205680
|
-
const
|
|
207844
|
+
const baseLoaded = options.config !== undefined ? {
|
|
205681
207845
|
config: options.config,
|
|
205682
207846
|
configHash: options.configHash,
|
|
205683
207847
|
configTrustStatus: "trusted",
|
|
@@ -205694,12 +207858,17 @@ async function runReview(tool, request, options = {}) {
|
|
|
205694
207858
|
env: options.env,
|
|
205695
207859
|
trustPrompt: options.trustPrompt
|
|
205696
207860
|
});
|
|
205697
|
-
const
|
|
207861
|
+
const loaded = options.configOverrides && options.configOverrides.length > 0 ? {
|
|
207862
|
+
...baseLoaded,
|
|
207863
|
+
config: applyConfigOverrides(baseLoaded.config, options.configOverrides)
|
|
207864
|
+
} : baseLoaded;
|
|
207865
|
+
const trace = traceWriterFactory({
|
|
205698
207866
|
enabled: loaded.config.audit.enabled,
|
|
205699
207867
|
directory: loaded.config.audit.directory,
|
|
205700
207868
|
traceId,
|
|
205701
207869
|
cwd,
|
|
205702
|
-
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput
|
|
207870
|
+
includeRawAgentOutput: loaded.config.audit.includeRawAgentOutput,
|
|
207871
|
+
env: auditEnv
|
|
205703
207872
|
});
|
|
205704
207873
|
const warnings = [...loaded.warnings, ...trace.warnings];
|
|
205705
207874
|
try {
|
|
@@ -205781,11 +207950,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
205781
207950
|
manager,
|
|
205782
207951
|
trace
|
|
205783
207952
|
});
|
|
205784
|
-
warnings.push(...agentResults.flatMap((
|
|
207953
|
+
warnings.push(...agentResults.flatMap((result) => (result.warnings ?? []).map((warning) => `Agent ${result.agent} ${warning}`)));
|
|
205785
207954
|
const normalizedAgentResults = agentResults.map(normalizeAgentRunResult);
|
|
205786
|
-
const agentsUsed = normalizedAgentResults.map((
|
|
207955
|
+
const agentsUsed = normalizedAgentResults.map((result) => result.agent);
|
|
205787
207956
|
const reviewMode = agentsUsed.length === 1 ? "single_agent" : "multi_agent";
|
|
205788
|
-
const completed = normalizedAgentResults.filter((
|
|
207957
|
+
const completed = normalizedAgentResults.filter((result) => result.status === "completed");
|
|
205789
207958
|
const degraded = completed.length !== agentResults.length;
|
|
205790
207959
|
let aggregate = aggregateAgentResults(normalizedAgentResults, {
|
|
205791
207960
|
reviewMode
|
|
@@ -205812,7 +207981,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
205812
207981
|
severity: "critical",
|
|
205813
207982
|
category: "other",
|
|
205814
207983
|
title: "All backend agents failed",
|
|
205815
|
-
evidence: normalizedAgentResults.map((
|
|
207984
|
+
evidence: normalizedAgentResults.map((result) => `${result.agent}: ${result.error?.code ?? result.status}`).join("; "),
|
|
205816
207985
|
recommendation: "Run kyoso doctor and retry after agent authentication or adapter issues are fixed.",
|
|
205817
207986
|
sourceAgents: ["kyoso_policy"],
|
|
205818
207987
|
confidence: "high"
|
|
@@ -205862,7 +208031,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
205862
208031
|
residualRisks: tool === "security_review" && aggregate.residualRisks.length === 0 ? [
|
|
205863
208032
|
"No residual risks were reported by completed agents; verify security assumptions before release."
|
|
205864
208033
|
] : aggregate.residualRisks,
|
|
205865
|
-
agentOpinions: normalizedAgentResults.map((
|
|
208034
|
+
agentOpinions: normalizedAgentResults.map((result) => agentOpinionSummary(result, request.options?.includeAgentRawOutputs === true)),
|
|
205866
208035
|
audit: {
|
|
205867
208036
|
traceId,
|
|
205868
208037
|
startedAt,
|
|
@@ -205894,15 +208063,10 @@ async function runReview(tool, request, options = {}) {
|
|
|
205894
208063
|
judgeComment: judgeComments.get(disagreement.topic) ?? disagreement.judgeComment
|
|
205895
208064
|
}));
|
|
205896
208065
|
const crossModelAnalysis = buildCrossModelAnalysis(judge, reviewMode);
|
|
205897
|
-
const
|
|
208066
|
+
const resultAfterJudge = {
|
|
205898
208067
|
...resultWithoutMarkdown,
|
|
205899
208068
|
disagreements,
|
|
205900
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
205901
|
-
summaryMarkdown: renderMarkdownResult(tool, {
|
|
205902
|
-
...resultWithoutMarkdown,
|
|
205903
|
-
disagreements,
|
|
205904
|
-
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
205905
|
-
}, { summaryText: judge.output.summaryText })
|
|
208069
|
+
...crossModelAnalysis ? { crossModelAnalysis } : {}
|
|
205906
208070
|
};
|
|
205907
208071
|
const judgeEvent = {
|
|
205908
208072
|
type: "judge_completed",
|
|
@@ -205914,7 +208078,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
205914
208078
|
if (judge.error)
|
|
205915
208079
|
judgeEvent.error = judge.error;
|
|
205916
208080
|
await trace.write(judgeEvent);
|
|
205917
|
-
|
|
208081
|
+
resultAfterJudge.audit.completedAt = new Date().toISOString();
|
|
205918
208082
|
await trace.write({
|
|
205919
208083
|
type: "decision_completed",
|
|
205920
208084
|
traceId,
|
|
@@ -205926,8 +208090,14 @@ async function runReview(tool, request, options = {}) {
|
|
|
205926
208090
|
traceId,
|
|
205927
208091
|
timestamp: new Date().toISOString()
|
|
205928
208092
|
});
|
|
205929
|
-
return
|
|
208093
|
+
return await finalizeReviewResult({
|
|
208094
|
+
tool,
|
|
208095
|
+
trace,
|
|
208096
|
+
result: resultAfterJudge,
|
|
208097
|
+
summaryText: judge.output.summaryText
|
|
208098
|
+
});
|
|
205930
208099
|
} finally {
|
|
208100
|
+
await trace.finalize();
|
|
205931
208101
|
if (snapshot)
|
|
205932
208102
|
await cleanupSnapshot(snapshot.root);
|
|
205933
208103
|
}
|
|
@@ -206176,10 +208346,6 @@ async function buildSecretBlockResult(input2) {
|
|
|
206176
208346
|
warnings: input2.warnings
|
|
206177
208347
|
}
|
|
206178
208348
|
};
|
|
206179
|
-
const result = {
|
|
206180
|
-
...resultWithoutMarkdown,
|
|
206181
|
-
summaryMarkdown: renderMarkdownResult(input2.tool, resultWithoutMarkdown)
|
|
206182
|
-
};
|
|
206183
208349
|
await input2.trace.write({
|
|
206184
208350
|
type: "decision_completed",
|
|
206185
208351
|
traceId: input2.traceId,
|
|
@@ -206191,7 +208357,11 @@ async function buildSecretBlockResult(input2) {
|
|
|
206191
208357
|
traceId: input2.traceId,
|
|
206192
208358
|
timestamp: new Date().toISOString()
|
|
206193
208359
|
});
|
|
206194
|
-
return
|
|
208360
|
+
return await finalizeReviewResult({
|
|
208361
|
+
tool: input2.tool,
|
|
208362
|
+
trace: input2.trace,
|
|
208363
|
+
result: resultWithoutMarkdown
|
|
208364
|
+
});
|
|
206195
208365
|
}
|
|
206196
208366
|
function buildSecretFinding(secretScan, options) {
|
|
206197
208367
|
return {
|
|
@@ -206241,10 +208411,6 @@ async function buildPolicyBlockResult(input2) {
|
|
|
206241
208411
|
warnings: [input2.warning]
|
|
206242
208412
|
}
|
|
206243
208413
|
};
|
|
206244
|
-
const result = {
|
|
206245
|
-
...resultWithoutMarkdown,
|
|
206246
|
-
summaryMarkdown: renderMarkdownResult(input2.tool, resultWithoutMarkdown)
|
|
206247
|
-
};
|
|
206248
208414
|
await input2.trace.write({
|
|
206249
208415
|
type: "decision_completed",
|
|
206250
208416
|
traceId: input2.traceId,
|
|
@@ -206256,7 +208422,30 @@ async function buildPolicyBlockResult(input2) {
|
|
|
206256
208422
|
traceId: input2.traceId,
|
|
206257
208423
|
timestamp: new Date().toISOString()
|
|
206258
208424
|
});
|
|
206259
|
-
return
|
|
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
|
+
};
|
|
206260
208449
|
}
|
|
206261
208450
|
function mergeDenyPatterns(configDeny, requestDeny) {
|
|
206262
208451
|
return Array.from(new Set([...configDeny, ...requestDeny ?? []]));
|
|
@@ -206264,7 +208453,7 @@ function mergeDenyPatterns(configDeny, requestDeny) {
|
|
|
206264
208453
|
function assertTrustedWorkspaceRoot(requestRoot, configRoot, cwd) {
|
|
206265
208454
|
if (!requestRoot)
|
|
206266
208455
|
return;
|
|
206267
|
-
if (
|
|
208456
|
+
if (resolve10(cwd, requestRoot) !== resolve10(cwd, configRoot)) {
|
|
206268
208457
|
throw new KyosoRequestError("workspace.root is not trusted by config", "UNTRUSTED_WORKSPACE_ROOT");
|
|
206269
208458
|
}
|
|
206270
208459
|
}
|
|
@@ -206382,7 +208571,9 @@ async function main() {
|
|
|
206382
208571
|
write: booleanFlag(parsed.flags, "write"),
|
|
206383
208572
|
global: booleanFlag(parsed.flags, "global"),
|
|
206384
208573
|
runner: stringFlag(parsed.flags, "runner"),
|
|
206385
|
-
command: stringFlag(parsed.flags, "command")
|
|
208574
|
+
command: stringFlag(parsed.flags, "command"),
|
|
208575
|
+
skillOnly: booleanFlag(parsed.flags, "skill-only"),
|
|
208576
|
+
force: booleanFlag(parsed.flags, "force")
|
|
206386
208577
|
}));
|
|
206387
208578
|
return;
|
|
206388
208579
|
}
|
|
@@ -206395,6 +208586,7 @@ async function main() {
|
|
|
206395
208586
|
ignoreConfig,
|
|
206396
208587
|
trustConfig: trustConfig2,
|
|
206397
208588
|
allowUnknownConfig,
|
|
208589
|
+
configOverrides: configOverrideFlags(parsed.flags),
|
|
206398
208590
|
promptForTrust: canPromptForConfigTrust()
|
|
206399
208591
|
});
|
|
206400
208592
|
console.log(booleanFlag(parsed.flags, "json") ? JSON.stringify(result, null, 2) : result.summaryMarkdown);
|
|
@@ -206438,7 +208630,7 @@ async function buildDiff(tool, flags) {
|
|
|
206438
208630
|
return;
|
|
206439
208631
|
const base = stringFlag(flags, "base") ?? "main";
|
|
206440
208632
|
const head = stringFlag(flags, "head") ?? "HEAD";
|
|
206441
|
-
const result =
|
|
208633
|
+
const result = spawnSync3("git", ["diff", base, head], { encoding: "utf8" });
|
|
206442
208634
|
if (result.status !== 0) {
|
|
206443
208635
|
throw new Error(result.stderr || `git diff failed for ${base}..${head}`);
|
|
206444
208636
|
}
|
|
@@ -206471,6 +208663,12 @@ function parseNetworkFlag(value) {
|
|
|
206471
208663
|
return value;
|
|
206472
208664
|
throw new Error(`Invalid --network value "${value}". Expected model_only or unrestricted.`);
|
|
206473
208665
|
}
|
|
208666
|
+
function configOverrideFlags(flags) {
|
|
208667
|
+
if (flags.set === true) {
|
|
208668
|
+
throw new Error("Missing value for --set. Expected key=value.");
|
|
208669
|
+
}
|
|
208670
|
+
return stringArrayFlag(flags, "set");
|
|
208671
|
+
}
|
|
206474
208672
|
function canPromptForConfigTrust() {
|
|
206475
208673
|
return process.stdin.isTTY === true && process.stderr.isTTY === true;
|
|
206476
208674
|
}
|
|
@@ -206478,10 +208676,11 @@ var HELP = `Kyoso
|
|
|
206478
208676
|
|
|
206479
208677
|
Usage:
|
|
206480
208678
|
kyoso mcp [--config kyoso.toml|kyoso.config.ts] [--ignore-config] [--trust-config] [--allow-unknown-config] [--network model_only|unrestricted]
|
|
206481
|
-
kyoso setup [codex|claude-code] [--write] [--runner npx|bunx] [--command <command>] [--global]
|
|
206482
|
-
kyoso
|
|
206483
|
-
kyoso
|
|
206484
|
-
kyoso diff --
|
|
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]
|
|
208681
|
+
kyoso plan --goal <text> [--plan <path-or-text>] [--file <path>] [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]
|
|
208682
|
+
kyoso security --goal <text> [--diff <path>] [--file <path>] [--set <key>=<value>]... [--json] [--allow-secret-redaction] [--trust-config] [--allow-unknown-config]
|
|
208683
|
+
kyoso diff --base main --head HEAD [--set <key>=<value>]... [--json] [--trust-config] [--allow-unknown-config]
|
|
206485
208684
|
kyoso doctor [--trust-config] [--allow-unknown-config]
|
|
206486
208685
|
kyoso init [--force]
|
|
206487
208686
|
`;
|