@kyo-so/cli 0.13.1 → 0.14.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 +3 -2
- package/CHANGELOG.md +20 -0
- package/README.ja.md +23 -15
- package/README.md +23 -15
- package/README.zh-CN.md +23 -15
- package/dist/bin/kyoso.js +1646 -258
- package/dist/cli/integration.d.ts +6 -3
- package/dist/cli/knownSkillDigests.d.ts +5 -1
- package/dist/cli/manualMcpInvocation.d.ts +11 -0
- package/dist/cli/packageRunner.d.ts +15 -0
- package/dist/cli/pluginRuntimeContract.d.ts +8 -6
- package/dist/cli/setup.d.ts +33 -1
- package/dist/core/constants.d.ts +1 -1
- package/examples/claude-code-mcp.json +1 -1
- package/examples/codex-config.toml +2 -2
- package/package.json +3 -1
package/dist/bin/kyoso.js
CHANGED
|
@@ -183951,7 +183951,7 @@ var JUDGE_MAX_OUTPUT_TOKENS = 4096;
|
|
|
183951
183951
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
183952
183952
|
var TRACE_DIR = ".kyoso/traces";
|
|
183953
183953
|
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
183954
|
-
var KYOSO_VERSION = "0.
|
|
183954
|
+
var KYOSO_VERSION = "0.14.0";
|
|
183955
183955
|
|
|
183956
183956
|
// src/utils/pathContainment.ts
|
|
183957
183957
|
import { resolve, sep as sep2 } from "node:path";
|
|
@@ -187092,13 +187092,14 @@ function isPlainObject2(value) {
|
|
|
187092
187092
|
import { spawnSync } from "node:child_process";
|
|
187093
187093
|
|
|
187094
187094
|
// src/cli/pluginRuntimeContract.ts
|
|
187095
|
-
var PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION =
|
|
187095
|
+
var PLUGIN_RUNTIME_COMPATIBILITY_SCHEMA_VERSION = 2;
|
|
187096
187096
|
var MINIMUM_SUPPORTED_CODEX_VERSION = "0.144.0-alpha.4";
|
|
187097
187097
|
var PLUGIN_RUNTIME_EXPECTED_CONTRACT = {
|
|
187098
187098
|
distribution: {
|
|
187099
|
-
pluginVersion: "0.7.
|
|
187099
|
+
pluginVersion: "0.7.2",
|
|
187100
187100
|
mcpCommand: "npx",
|
|
187101
|
-
mcpPackagePin: "@kyo-so/cli@0.13.
|
|
187101
|
+
mcpPackagePin: "@kyo-so/cli@0.13.1",
|
|
187102
|
+
mcpExecutable: "kyoso"
|
|
187102
187103
|
},
|
|
187103
187104
|
marketplace: {
|
|
187104
187105
|
name: "kyoso",
|
|
@@ -187501,21 +187502,66 @@ import {
|
|
|
187501
187502
|
relative,
|
|
187502
187503
|
resolve as resolve5
|
|
187503
187504
|
} from "node:path";
|
|
187505
|
+
|
|
187506
|
+
// src/cli/packageRunner.ts
|
|
187507
|
+
var KYOSO_PACKAGE_NAME = "@kyo-so/cli";
|
|
187508
|
+
var KYOSO_EXECUTABLE_NAME = "kyoso";
|
|
187509
|
+
var COMPLETE_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
187510
|
+
function buildKyosoPackageCommand(options) {
|
|
187511
|
+
const packageSpec = options.version === undefined ? KYOSO_PACKAGE_NAME : `${KYOSO_PACKAGE_NAME}@${assertCompleteSemVer(options.version)}`;
|
|
187512
|
+
const cliArgs = [...options.cliArgs];
|
|
187513
|
+
if (options.runner === "npx") {
|
|
187514
|
+
return {
|
|
187515
|
+
command: "npx",
|
|
187516
|
+
args: [
|
|
187517
|
+
"-y",
|
|
187518
|
+
`--package=${packageSpec}`,
|
|
187519
|
+
KYOSO_EXECUTABLE_NAME,
|
|
187520
|
+
...cliArgs
|
|
187521
|
+
]
|
|
187522
|
+
};
|
|
187523
|
+
}
|
|
187524
|
+
return {
|
|
187525
|
+
command: "bunx",
|
|
187526
|
+
args: ["--package", packageSpec, KYOSO_EXECUTABLE_NAME, ...cliArgs]
|
|
187527
|
+
};
|
|
187528
|
+
}
|
|
187529
|
+
function formatKyosoPackageCommand(options) {
|
|
187530
|
+
const command = buildKyosoPackageCommand(options);
|
|
187531
|
+
return [command.command, ...command.args].join(" ");
|
|
187532
|
+
}
|
|
187533
|
+
function isCompleteSemVer(value) {
|
|
187534
|
+
const match = COMPLETE_SEMVER.exec(value);
|
|
187535
|
+
const prerelease = match?.[4];
|
|
187536
|
+
return match !== null && (prerelease === undefined || prerelease.split(".").every((identifier) => {
|
|
187537
|
+
return !/^\d+$/.test(identifier) || identifier === "0" || !identifier.startsWith("0");
|
|
187538
|
+
}));
|
|
187539
|
+
}
|
|
187540
|
+
function assertCompleteSemVer(value) {
|
|
187541
|
+
if (isCompleteSemVer(value))
|
|
187542
|
+
return value;
|
|
187543
|
+
throw new Error(`Kyoso package version must be a complete SemVer; received ${JSON.stringify(value)}.`);
|
|
187544
|
+
}
|
|
187545
|
+
|
|
187546
|
+
// src/cli/integration.ts
|
|
187504
187547
|
function detectCli(options) {
|
|
187505
187548
|
const env = options.env ?? process.env;
|
|
187506
187549
|
const platform = options.platform ?? process.platform;
|
|
187507
187550
|
return {
|
|
187508
187551
|
kyoso: detectInstalledKyoso(options.cwd, env, platform),
|
|
187509
|
-
npx: commandExists("npx", env, platform),
|
|
187510
|
-
bunx: commandExists("bunx", env, platform)
|
|
187552
|
+
npx: commandExists("npx", env, platform) ? "available" : "missing",
|
|
187553
|
+
bunx: commandExists("bunx", env, platform) ? "present-unverified" : "missing"
|
|
187511
187554
|
};
|
|
187512
187555
|
}
|
|
187513
187556
|
function determineNonPluginIntegration(options) {
|
|
187514
|
-
const warnings = manualMcpWarnings(options.manualMcpStatus);
|
|
187557
|
+
const warnings = manualMcpWarnings(options.manualMcpStatus, options.manualMcpRegistrations, options.cli);
|
|
187515
187558
|
if (options.manualMcpStatus === "unknown") {
|
|
187516
187559
|
return { mode: "unknown", warnings };
|
|
187517
187560
|
}
|
|
187518
187561
|
if (options.manualMcpStatus === "enabled") {
|
|
187562
|
+
if (!hasCurrentManualMcp(options.manualMcpRegistrations, options.cli)) {
|
|
187563
|
+
return { mode: "unknown", warnings };
|
|
187564
|
+
}
|
|
187519
187565
|
return {
|
|
187520
187566
|
mode: options.hasSkill ? "manual-mcp" : "mcp-only",
|
|
187521
187567
|
warnings
|
|
@@ -187527,10 +187573,13 @@ function determineNonPluginIntegration(options) {
|
|
|
187527
187573
|
if (options.cli.kyoso.kind === "installed") {
|
|
187528
187574
|
return { mode: "cli-skill", warnings };
|
|
187529
187575
|
}
|
|
187530
|
-
if (options.cli.npx
|
|
187576
|
+
if (options.cli.npx === "available") {
|
|
187531
187577
|
warnings.push("Package-runner fallback may require network access and can drift between versions.");
|
|
187532
187578
|
return { mode: "skill-on-demand", warnings };
|
|
187533
187579
|
}
|
|
187580
|
+
if (options.cli.bunx === "present-unverified") {
|
|
187581
|
+
warnings.push("bunx is present but unverified; install Kyoso on PATH or npx first, then run the client-specific setup with --write --runner bunx to verify Bun 1.3.14 or newer.");
|
|
187582
|
+
}
|
|
187534
187583
|
if (options.cli.kyoso.kind === "unknown") {
|
|
187535
187584
|
return { mode: "unknown", warnings };
|
|
187536
187585
|
}
|
|
@@ -187545,6 +187594,13 @@ function determineNonPluginIntegration(options) {
|
|
|
187545
187594
|
}
|
|
187546
187595
|
return { mode: "missing", warnings };
|
|
187547
187596
|
}
|
|
187597
|
+
function formatRunnerAvailability(runner) {
|
|
187598
|
+
if (runner === "available")
|
|
187599
|
+
return "available";
|
|
187600
|
+
if (runner === "present-unverified")
|
|
187601
|
+
return "present-unverified";
|
|
187602
|
+
return "missing";
|
|
187603
|
+
}
|
|
187548
187604
|
function formatCliAvailability(cli) {
|
|
187549
187605
|
if (cli.kind === "installed") {
|
|
187550
187606
|
return `installed @kyo-so/cli ${cli.version} (${cli.scope})`;
|
|
@@ -187609,7 +187665,9 @@ function realPathOrResolved(path) {
|
|
|
187609
187665
|
return resolve5(path);
|
|
187610
187666
|
}
|
|
187611
187667
|
}
|
|
187612
|
-
function manualMcpWarnings(status) {
|
|
187668
|
+
function manualMcpWarnings(status, registrations, cli) {
|
|
187669
|
+
if (status === "missing")
|
|
187670
|
+
return [];
|
|
187613
187671
|
if (status === "disabled")
|
|
187614
187672
|
return ["Manual MCP registration is disabled."];
|
|
187615
187673
|
if (status === "unknown") {
|
|
@@ -187617,8 +187675,89 @@ function manualMcpWarnings(status) {
|
|
|
187617
187675
|
"Manual MCP registration could not be safely classified from its configuration."
|
|
187618
187676
|
];
|
|
187619
187677
|
}
|
|
187678
|
+
if (registrations.length === 0) {
|
|
187679
|
+
return [
|
|
187680
|
+
"Manual MCP registration is enabled but no exact registration could be verified."
|
|
187681
|
+
];
|
|
187682
|
+
}
|
|
187683
|
+
if (registrations.length !== 1) {
|
|
187684
|
+
return [
|
|
187685
|
+
"Multiple manual MCP registrations were found; their effective precedence is not inferred."
|
|
187686
|
+
];
|
|
187687
|
+
}
|
|
187688
|
+
const registration = registrations[0];
|
|
187689
|
+
const invocation = registration?.invocation;
|
|
187690
|
+
if (invocation?.kind === "legacy") {
|
|
187691
|
+
return [legacyManualMcpRepairWarning(registration, cli)];
|
|
187692
|
+
}
|
|
187693
|
+
if (invocation?.kind === "custom") {
|
|
187694
|
+
return [
|
|
187695
|
+
"Manual MCP registration is custom/unverified and was not treated as a ready Kyoso registration."
|
|
187696
|
+
];
|
|
187697
|
+
}
|
|
187698
|
+
if (invocation?.kind === "unknown") {
|
|
187699
|
+
return [
|
|
187700
|
+
"Manual MCP invocation could not be safely classified and was not treated as a ready Kyoso registration."
|
|
187701
|
+
];
|
|
187702
|
+
}
|
|
187703
|
+
if (invocation?.runner === "npx" && cli.npx !== "available") {
|
|
187704
|
+
return [
|
|
187705
|
+
"Manual MCP registration uses npx, but npx is not available on PATH."
|
|
187706
|
+
];
|
|
187707
|
+
}
|
|
187708
|
+
if (invocation?.runner === "bunx") {
|
|
187709
|
+
if (cli.bunx === "missing") {
|
|
187710
|
+
return [
|
|
187711
|
+
"Manual MCP registration uses bunx, but bunx is not available on PATH."
|
|
187712
|
+
];
|
|
187713
|
+
}
|
|
187714
|
+
const verificationCommand = registration ? manualMcpSetupCommand(registration, cli, [
|
|
187715
|
+
"--write",
|
|
187716
|
+
"--runner",
|
|
187717
|
+
"bunx"
|
|
187718
|
+
]) : undefined;
|
|
187719
|
+
return [
|
|
187720
|
+
`Manual MCP registration uses bunx, but normal doctor does not verify the required Bun capability.${verificationCommand ? ` Run \`${verificationCommand}\` before treating it as ready.` : " Run setup with --write --runner bunx from an installed Kyoso CLI before treating it as ready."}`
|
|
187721
|
+
];
|
|
187722
|
+
}
|
|
187620
187723
|
return [];
|
|
187621
187724
|
}
|
|
187725
|
+
function legacyManualMcpRepairWarning(registration, cli) {
|
|
187726
|
+
if (!registration) {
|
|
187727
|
+
return "Manual MCP registration uses legacy package-runner arguments and requires repair.";
|
|
187728
|
+
}
|
|
187729
|
+
const client = registration.scope === "codex-global" ? "codex" : registration.scope === "claude-project" ? "claude-code" : undefined;
|
|
187730
|
+
if (!client) {
|
|
187731
|
+
return `Manual MCP registration at ${registration.path} uses legacy package-runner arguments. Its ${registration.scope} scope is not automatically migrated; update it manually.`;
|
|
187732
|
+
}
|
|
187733
|
+
const runner = cli.npx === "available" ? "npx" : cli.kyoso.kind === "installed" && cli.bunx === "present-unverified" ? "bunx" : undefined;
|
|
187734
|
+
const command = runner ? manualMcpSetupCommand(registration, cli, [
|
|
187735
|
+
"--write",
|
|
187736
|
+
"--runner",
|
|
187737
|
+
runner,
|
|
187738
|
+
"--force"
|
|
187739
|
+
]) : undefined;
|
|
187740
|
+
if (!command) {
|
|
187741
|
+
return `Manual MCP registration at ${registration.path} uses legacy package-runner arguments, but no executable Kyoso repair path is available. Update it manually.`;
|
|
187742
|
+
}
|
|
187743
|
+
return `Manual MCP registration uses legacy package-runner arguments. Run \`${command}\` to migrate this exact registration.`;
|
|
187744
|
+
}
|
|
187745
|
+
function manualMcpSetupCommand(registration, cli, setupArgs) {
|
|
187746
|
+
const client = registration.scope === "codex-global" ? "codex" : registration.scope === "claude-project" ? "claude-code" : undefined;
|
|
187747
|
+
if (!client)
|
|
187748
|
+
return;
|
|
187749
|
+
const cliArgs = ["setup", client, ...setupArgs];
|
|
187750
|
+
if (cli.kyoso.kind === "installed") {
|
|
187751
|
+
return `kyoso ${cliArgs.join(" ")}`;
|
|
187752
|
+
}
|
|
187753
|
+
if (cli.npx !== "available")
|
|
187754
|
+
return;
|
|
187755
|
+
return formatKyosoPackageCommand({ runner: "npx", cliArgs });
|
|
187756
|
+
}
|
|
187757
|
+
function hasCurrentManualMcp(registrations, cli) {
|
|
187758
|
+
const registration = registrations[0];
|
|
187759
|
+
return registrations.length === 1 && registration?.invocation.kind === "current" && registration.invocation.runner === "npx" && cli.npx === "available";
|
|
187760
|
+
}
|
|
187622
187761
|
function cliIdentityWarnings(cli) {
|
|
187623
187762
|
if (cli.kind === "transient") {
|
|
187624
187763
|
return [
|
|
@@ -187677,12 +187816,206 @@ function isRecord8(value) {
|
|
|
187677
187816
|
|
|
187678
187817
|
// src/cli/setup.ts
|
|
187679
187818
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
187680
|
-
import {
|
|
187681
|
-
import {
|
|
187682
|
-
|
|
187683
|
-
|
|
187819
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
187820
|
+
import {
|
|
187821
|
+
existsSync as existsSync2,
|
|
187822
|
+
readFileSync as readFileSync2,
|
|
187823
|
+
realpathSync as realpathSync2,
|
|
187824
|
+
mkdtempSync,
|
|
187825
|
+
rmSync
|
|
187826
|
+
} from "node:fs";
|
|
187827
|
+
import {
|
|
187828
|
+
chmod,
|
|
187829
|
+
lstat as lstat3,
|
|
187830
|
+
mkdir as mkdir5,
|
|
187831
|
+
open,
|
|
187832
|
+
readFile as readFile6,
|
|
187833
|
+
rename as rename2,
|
|
187834
|
+
rm as rm2,
|
|
187835
|
+
writeFile as writeFile4
|
|
187836
|
+
} from "node:fs/promises";
|
|
187837
|
+
import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
|
|
187838
|
+
import { basename as basename3, delimiter as delimiter2, dirname as dirname8, join as join6, resolve as resolve7 } from "node:path";
|
|
187684
187839
|
import { fileURLToPath } from "node:url";
|
|
187685
187840
|
|
|
187841
|
+
// src/cli/manualMcpInvocation.ts
|
|
187842
|
+
var GENERATED_MCP_ENV_VALUE_NAMES = new Set([
|
|
187843
|
+
"OPENAI_API_KEY",
|
|
187844
|
+
"ANTHROPIC_API_KEY",
|
|
187845
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
187846
|
+
"OPENROUTER_API_KEY"
|
|
187847
|
+
]);
|
|
187848
|
+
var GENERATED_MCP_ENV_VAR_NAMES = new Set([
|
|
187849
|
+
"OPENAI_API_KEY",
|
|
187850
|
+
"CODEX_API_KEY",
|
|
187851
|
+
"CODEX_HOME",
|
|
187852
|
+
"CODEX_ACCESS_TOKEN",
|
|
187853
|
+
"OPENROUTER_API_KEY",
|
|
187854
|
+
"ANTHROPIC_API_KEY",
|
|
187855
|
+
"CLAUDE_CODE_OAUTH_TOKEN"
|
|
187856
|
+
]);
|
|
187857
|
+
function inspectManualMcpInvocation(value) {
|
|
187858
|
+
if (!isRecord9(value)) {
|
|
187859
|
+
return { kind: "unknown", reason: "MCP entry is not an object." };
|
|
187860
|
+
}
|
|
187861
|
+
if (typeof value.command !== "string" || value.command.length === 0) {
|
|
187862
|
+
return {
|
|
187863
|
+
kind: "unknown",
|
|
187864
|
+
reason: "MCP command is missing or is not a string."
|
|
187865
|
+
};
|
|
187866
|
+
}
|
|
187867
|
+
if (!Array.isArray(value.args) || !value.args.every(isString)) {
|
|
187868
|
+
return {
|
|
187869
|
+
kind: "unknown",
|
|
187870
|
+
reason: "MCP args are missing or are not an array of strings."
|
|
187871
|
+
};
|
|
187872
|
+
}
|
|
187873
|
+
const environment = inspectMcpEnvironment(value);
|
|
187874
|
+
if (value.command === "npx") {
|
|
187875
|
+
return applyEnvironmentSafety(inspectNpx(value.args), environment);
|
|
187876
|
+
}
|
|
187877
|
+
if (value.command === "bunx") {
|
|
187878
|
+
return applyEnvironmentSafety(inspectBunx(value.args), environment);
|
|
187879
|
+
}
|
|
187880
|
+
return {
|
|
187881
|
+
kind: "custom",
|
|
187882
|
+
reason: `MCP command ${JSON.stringify(value.command)} is not a Kyoso package runner.`
|
|
187883
|
+
};
|
|
187884
|
+
}
|
|
187885
|
+
function inspectMcpEnvironment(value) {
|
|
187886
|
+
if ("env" in value && !isGeneratedMcpEnvironment(value.env)) {
|
|
187887
|
+
return {
|
|
187888
|
+
kind: "custom",
|
|
187889
|
+
reason: "MCP environment is not limited to generated credential placeholders."
|
|
187890
|
+
};
|
|
187891
|
+
}
|
|
187892
|
+
if ("env_vars" in value && !isGeneratedMcpEnvironmentVariables(value.env_vars)) {
|
|
187893
|
+
return {
|
|
187894
|
+
kind: "custom",
|
|
187895
|
+
reason: "MCP environment variable forwarding is not limited to generated credential names."
|
|
187896
|
+
};
|
|
187897
|
+
}
|
|
187898
|
+
return;
|
|
187899
|
+
}
|
|
187900
|
+
function applyEnvironmentSafety(invocation, environment) {
|
|
187901
|
+
return environment ?? invocation;
|
|
187902
|
+
}
|
|
187903
|
+
function isGeneratedMcpEnvironment(value) {
|
|
187904
|
+
if (!isRecord9(value))
|
|
187905
|
+
return false;
|
|
187906
|
+
return Object.entries(value).every(([name, placeholder]) => GENERATED_MCP_ENV_VALUE_NAMES.has(name) && placeholder === `\${${name}}`);
|
|
187907
|
+
}
|
|
187908
|
+
function isGeneratedMcpEnvironmentVariables(value) {
|
|
187909
|
+
if (!Array.isArray(value) || !value.every(isString))
|
|
187910
|
+
return false;
|
|
187911
|
+
return new Set(value).size === value.length && value.every((name) => GENERATED_MCP_ENV_VAR_NAMES.has(name));
|
|
187912
|
+
}
|
|
187913
|
+
function inspectNpx(args) {
|
|
187914
|
+
const current = parseNpxCurrent(args);
|
|
187915
|
+
if (current) {
|
|
187916
|
+
return {
|
|
187917
|
+
kind: "current",
|
|
187918
|
+
runner: "npx",
|
|
187919
|
+
packageSpec: current.packageSpec,
|
|
187920
|
+
reason: "npx explicitly selects the Kyoso executable from its package."
|
|
187921
|
+
};
|
|
187922
|
+
}
|
|
187923
|
+
const legacy = parseNpxLegacy(args);
|
|
187924
|
+
if (legacy)
|
|
187925
|
+
return legacyInspection("npx", legacy, args);
|
|
187926
|
+
return {
|
|
187927
|
+
kind: "custom",
|
|
187928
|
+
runner: "npx",
|
|
187929
|
+
reason: "npx arguments do not exactly match a supported Kyoso invocation."
|
|
187930
|
+
};
|
|
187931
|
+
}
|
|
187932
|
+
function inspectBunx(args) {
|
|
187933
|
+
const current = parseBunxCurrent(args);
|
|
187934
|
+
if (current) {
|
|
187935
|
+
return {
|
|
187936
|
+
kind: "current",
|
|
187937
|
+
runner: "bunx",
|
|
187938
|
+
packageSpec: current.packageSpec,
|
|
187939
|
+
reason: "bunx explicitly selects the Kyoso executable from its package."
|
|
187940
|
+
};
|
|
187941
|
+
}
|
|
187942
|
+
const legacy = parseBunxLegacy(args);
|
|
187943
|
+
if (legacy)
|
|
187944
|
+
return legacyInspection("bunx", legacy, args);
|
|
187945
|
+
return {
|
|
187946
|
+
kind: "custom",
|
|
187947
|
+
runner: "bunx",
|
|
187948
|
+
reason: "bunx arguments do not exactly match a supported Kyoso invocation."
|
|
187949
|
+
};
|
|
187950
|
+
}
|
|
187951
|
+
function legacyInspection(runner, packageSpec, legacyArgs) {
|
|
187952
|
+
const version2 = versionFromKnownPackageSpec(packageSpec);
|
|
187953
|
+
if (version2 === undefined && packageSpec !== KYOSO_PACKAGE_NAME) {
|
|
187954
|
+
return {
|
|
187955
|
+
kind: "custom",
|
|
187956
|
+
runner,
|
|
187957
|
+
packageSpec,
|
|
187958
|
+
reason: "Kyoso package spec is a tag, range, or malformed pin and is preserved."
|
|
187959
|
+
};
|
|
187960
|
+
}
|
|
187961
|
+
return {
|
|
187962
|
+
kind: "legacy",
|
|
187963
|
+
runner,
|
|
187964
|
+
packageSpec,
|
|
187965
|
+
legacyArgs: [...legacyArgs],
|
|
187966
|
+
replacement: buildKyosoPackageCommand({
|
|
187967
|
+
runner,
|
|
187968
|
+
...version2 === undefined ? {} : { version: version2 },
|
|
187969
|
+
cliArgs: ["mcp"]
|
|
187970
|
+
}),
|
|
187971
|
+
reason: "Kyoso package runner relies on executable inference with a multi-bin package."
|
|
187972
|
+
};
|
|
187973
|
+
}
|
|
187974
|
+
function parseNpxCurrent(args) {
|
|
187975
|
+
if (args.length !== 4 || args[0] !== "-y" || args[2] !== KYOSO_EXECUTABLE_NAME || args[3] !== "mcp") {
|
|
187976
|
+
return;
|
|
187977
|
+
}
|
|
187978
|
+
const packageSpec = args[1]?.startsWith("--package=") ? args[1].slice("--package=".length) : undefined;
|
|
187979
|
+
return packageSpec && isKnownPackageSpec(packageSpec) ? { packageSpec } : undefined;
|
|
187980
|
+
}
|
|
187981
|
+
function parseBunxCurrent(args) {
|
|
187982
|
+
if (args.length !== 4 || args[0] !== "--package" || args[2] !== KYOSO_EXECUTABLE_NAME || args[3] !== "mcp") {
|
|
187983
|
+
return;
|
|
187984
|
+
}
|
|
187985
|
+
const packageSpec = args[1];
|
|
187986
|
+
return packageSpec && isKnownPackageSpec(packageSpec) ? { packageSpec } : undefined;
|
|
187987
|
+
}
|
|
187988
|
+
function parseNpxLegacy(args) {
|
|
187989
|
+
const packageIndex = args[0] === "-y" ? 1 : 0;
|
|
187990
|
+
if (args.length !== packageIndex + 2 || args[packageIndex + 1] !== "mcp") {
|
|
187991
|
+
return;
|
|
187992
|
+
}
|
|
187993
|
+
const packageSpec = args[packageIndex];
|
|
187994
|
+
return packageSpec && packageSpec.startsWith(KYOSO_PACKAGE_NAME) ? packageSpec : undefined;
|
|
187995
|
+
}
|
|
187996
|
+
function parseBunxLegacy(args) {
|
|
187997
|
+
if (args.length !== 2 || args[1] !== "mcp")
|
|
187998
|
+
return;
|
|
187999
|
+
const packageSpec = args[0];
|
|
188000
|
+
return packageSpec && packageSpec.startsWith(KYOSO_PACKAGE_NAME) ? packageSpec : undefined;
|
|
188001
|
+
}
|
|
188002
|
+
function isKnownPackageSpec(packageSpec) {
|
|
188003
|
+
return packageSpec === KYOSO_PACKAGE_NAME || versionFromKnownPackageSpec(packageSpec) !== undefined;
|
|
188004
|
+
}
|
|
188005
|
+
function versionFromKnownPackageSpec(packageSpec) {
|
|
188006
|
+
const prefix = `${KYOSO_PACKAGE_NAME}@`;
|
|
188007
|
+
if (!packageSpec.startsWith(prefix))
|
|
188008
|
+
return;
|
|
188009
|
+
const version2 = packageSpec.slice(prefix.length);
|
|
188010
|
+
return isCompleteSemVer(version2) ? version2 : undefined;
|
|
188011
|
+
}
|
|
188012
|
+
function isRecord9(value) {
|
|
188013
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
188014
|
+
}
|
|
188015
|
+
function isString(value) {
|
|
188016
|
+
return typeof value === "string";
|
|
188017
|
+
}
|
|
188018
|
+
|
|
187686
188019
|
// src/cli/skillInstall.ts
|
|
187687
188020
|
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
187688
188021
|
import {
|
|
@@ -187709,8 +188042,14 @@ import {
|
|
|
187709
188042
|
} from "node:path";
|
|
187710
188043
|
|
|
187711
188044
|
// src/cli/knownSkillDigests.ts
|
|
187712
|
-
var CURRENT_SKILL_DIGEST = "sha256:
|
|
188045
|
+
var CURRENT_SKILL_DIGEST = "sha256:d28acaadf490df9e58e12f195804f181a683d0d33344275d7989efbee26a4504";
|
|
187713
188046
|
var KNOWN_SKILL_DIGESTS_BY_VERSION = {
|
|
188047
|
+
"0.13.1": [
|
|
188048
|
+
{
|
|
188049
|
+
digest: "sha256:8654e68ea61f2acea29027056802bf627ad737f084c9a86ab052946943538409",
|
|
188050
|
+
kind: "historical"
|
|
188051
|
+
}
|
|
188052
|
+
],
|
|
187714
188053
|
"0.11.0": [
|
|
187715
188054
|
{
|
|
187716
188055
|
digest: "sha256:110dd872a3d1c8a71474a0eadb226f51a6addd86f9d4f3ed17b73678b3179a4e",
|
|
@@ -188270,7 +188609,15 @@ async function runSetup(options) {
|
|
|
188270
188609
|
skillOnly: options.skillOnly ?? false,
|
|
188271
188610
|
force: options.force ?? false,
|
|
188272
188611
|
withOpenRouter: options.withOpenRouter ?? false,
|
|
188612
|
+
customCommand: options.command !== undefined,
|
|
188613
|
+
runnerExplicit: options.runner !== undefined,
|
|
188273
188614
|
mcpCommand: command,
|
|
188615
|
+
bunxVersionProbe: options.bunxVersionProbe ?? probeBunxVersion,
|
|
188616
|
+
beforeManualMcpWrite: options.beforeManualMcpWrite,
|
|
188617
|
+
beforeManualMcpCommit: options.beforeManualMcpCommit,
|
|
188618
|
+
afterManualMcpValidation: options.afterManualMcpValidation,
|
|
188619
|
+
beforeManualMcpRename: options.beforeManualMcpRename,
|
|
188620
|
+
manualMcpRename: options.manualMcpRename ?? rename2,
|
|
188274
188621
|
sourceSkillDir: resolveBundledSkillDir()
|
|
188275
188622
|
};
|
|
188276
188623
|
if (!client)
|
|
@@ -188281,10 +188628,7 @@ async function runSetup(options) {
|
|
|
188281
188628
|
return renderResults(context, await setupClaudeCode(context));
|
|
188282
188629
|
}
|
|
188283
188630
|
function commandForRunner(runner) {
|
|
188284
|
-
|
|
188285
|
-
return { command: "bunx", args: ["@kyo-so/cli", "mcp"] };
|
|
188286
|
-
}
|
|
188287
|
-
return { command: "npx", args: ["-y", "@kyo-so/cli", "mcp"] };
|
|
188631
|
+
return buildKyosoPackageCommand({ runner, cliArgs: ["mcp"] });
|
|
188288
188632
|
}
|
|
188289
188633
|
function buildCodexMcpToml(command, withOpenRouter = false) {
|
|
188290
188634
|
const envVars = [
|
|
@@ -188380,16 +188724,18 @@ function detectSetup(options) {
|
|
|
188380
188724
|
]);
|
|
188381
188725
|
return {
|
|
188382
188726
|
codex: {
|
|
188383
|
-
mcp: codexMcp
|
|
188727
|
+
mcp: isCurrentManualMcp(codexMcp),
|
|
188384
188728
|
skill: codexSkillPaths.length > 0,
|
|
188385
188729
|
manualMcpStatus: codexMcp.status,
|
|
188730
|
+
manualMcpRegistrations: codexMcp.registrations,
|
|
188386
188731
|
mcpPaths: codexMcp.paths,
|
|
188387
188732
|
skillPaths: codexSkillPaths
|
|
188388
188733
|
},
|
|
188389
188734
|
"claude-code": {
|
|
188390
|
-
mcp: claudeMcp
|
|
188735
|
+
mcp: isCurrentManualMcp(claudeMcp),
|
|
188391
188736
|
skill: claudeSkillPaths.length > 0,
|
|
188392
188737
|
manualMcpStatus: claudeMcp.status,
|
|
188738
|
+
manualMcpRegistrations: claudeMcp.registrations,
|
|
188393
188739
|
mcpPaths: claudeMcp.paths,
|
|
188394
188740
|
skillPaths: claudeSkillPaths
|
|
188395
188741
|
}
|
|
@@ -188425,14 +188771,19 @@ async function ensureCodexMcp(context) {
|
|
|
188425
188771
|
const configPath = join6(context.codexHome, "config.toml");
|
|
188426
188772
|
const snippet = buildCodexMcpToml(context.mcpCommand, context.withOpenRouter);
|
|
188427
188773
|
const current = await readOptionalFile(configPath);
|
|
188428
|
-
|
|
188774
|
+
const existing = inspectCodexMcpContent(current, configPath, context.cwd, context.home);
|
|
188775
|
+
if (existing) {
|
|
188776
|
+
return ensureExistingCodexMcp(context, current, existing);
|
|
188777
|
+
}
|
|
188778
|
+
const appendSafety = inspectCodexAppendSafety(current, context.cwd, context.home);
|
|
188779
|
+
if (!appendSafety.ok) {
|
|
188429
188780
|
return {
|
|
188430
188781
|
kind: "mcp",
|
|
188431
|
-
registration: "
|
|
188782
|
+
registration: "blocked",
|
|
188432
188783
|
title: "Codex MCP",
|
|
188433
|
-
status: "
|
|
188784
|
+
status: "conflict",
|
|
188434
188785
|
path: configPath,
|
|
188435
|
-
detail:
|
|
188786
|
+
detail: appendSafety.detail
|
|
188436
188787
|
};
|
|
188437
188788
|
}
|
|
188438
188789
|
const detail = diffForAppend(configPath, snippet);
|
|
@@ -188443,9 +188794,18 @@ async function ensureCodexMcp(context) {
|
|
|
188443
188794
|
title: "Codex MCP",
|
|
188444
188795
|
status: "dry-run",
|
|
188445
188796
|
path: configPath,
|
|
188446
|
-
detail
|
|
188797
|
+
detail: `${detail}${bunxVerificationPendingDetail(context, context.mcpCommand)}`
|
|
188447
188798
|
};
|
|
188448
188799
|
}
|
|
188800
|
+
const unsupportedBunx = unsupportedBunxResult(context, "Codex MCP", configPath, context.mcpCommand);
|
|
188801
|
+
if (unsupportedBunx)
|
|
188802
|
+
return unsupportedBunx;
|
|
188803
|
+
if (context.bunxProbe?.status === "verified") {
|
|
188804
|
+
const latest = await readOptionalFile(configPath);
|
|
188805
|
+
if (latest !== current) {
|
|
188806
|
+
return migrationConflictResult("Codex MCP", configPath, "Codex MCP config changed while bunx verification; it was not overwritten.");
|
|
188807
|
+
}
|
|
188808
|
+
}
|
|
188449
188809
|
const separator = current.length > 0 && !current.endsWith(`
|
|
188450
188810
|
`) ? `
|
|
188451
188811
|
|
|
@@ -188466,18 +188826,36 @@ async function ensureClaudeMcp(context) {
|
|
|
188466
188826
|
return ensureClaudeGlobalMcp(context);
|
|
188467
188827
|
}
|
|
188468
188828
|
const configPath = join6(context.cwd, ".mcp.json");
|
|
188469
|
-
const
|
|
188470
|
-
const
|
|
188471
|
-
if (
|
|
188829
|
+
const userConfig = detectClaudeMcp(join6(context.home, ".claude.json"), context.cwd, context.home);
|
|
188830
|
+
const applicableUserRegistrations = userConfig.registrations.filter((registration) => registration.status !== "disabled");
|
|
188831
|
+
if (applicableUserRegistrations.length > 0) {
|
|
188832
|
+
return claudeProjectMcpScopeConflictResult(configPath, {
|
|
188833
|
+
...userConfig,
|
|
188834
|
+
registrations: applicableUserRegistrations,
|
|
188835
|
+
paths: [
|
|
188836
|
+
...new Set(applicableUserRegistrations.map((registration) => registration.path))
|
|
188837
|
+
]
|
|
188838
|
+
});
|
|
188839
|
+
}
|
|
188840
|
+
const content = await readOptionalFile(configPath);
|
|
188841
|
+
let current;
|
|
188842
|
+
try {
|
|
188843
|
+
current = parseJsonObject(configPath, content);
|
|
188844
|
+
} catch (error51) {
|
|
188472
188845
|
return {
|
|
188473
188846
|
kind: "mcp",
|
|
188474
|
-
registration: "
|
|
188847
|
+
registration: "blocked",
|
|
188475
188848
|
title: "Claude Code MCP",
|
|
188476
|
-
status: "
|
|
188849
|
+
status: "conflict",
|
|
188477
188850
|
path: configPath,
|
|
188478
|
-
detail:
|
|
188851
|
+
detail: `Claude Code MCP config could not be parsed and was left unchanged: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
188479
188852
|
};
|
|
188480
188853
|
}
|
|
188854
|
+
const mcpServers = recordValue(current.mcpServers);
|
|
188855
|
+
const existing = inspectClaudeProjectMcp(current, configPath);
|
|
188856
|
+
if (existing) {
|
|
188857
|
+
return ensureExistingClaudeProjectMcp(context, content, existing);
|
|
188858
|
+
}
|
|
188481
188859
|
const next = {
|
|
188482
188860
|
...current,
|
|
188483
188861
|
mcpServers: {
|
|
@@ -188493,9 +188871,19 @@ async function ensureClaudeMcp(context) {
|
|
|
188493
188871
|
title: "Claude Code MCP",
|
|
188494
188872
|
status: "dry-run",
|
|
188495
188873
|
path: configPath,
|
|
188496
|
-
detail
|
|
188874
|
+
detail: `${detail}${bunxVerificationPendingDetail(context, context.mcpCommand)}`
|
|
188497
188875
|
};
|
|
188498
188876
|
}
|
|
188877
|
+
const unsupportedBunx = unsupportedBunxResult(context, "Claude Code MCP", configPath, context.mcpCommand);
|
|
188878
|
+
if (unsupportedBunx)
|
|
188879
|
+
return unsupportedBunx;
|
|
188880
|
+
if (context.bunxProbe?.status === "verified") {
|
|
188881
|
+
const latest = await readOptionalFile(configPath);
|
|
188882
|
+
if (latest !== content) {
|
|
188883
|
+
return migrationConflictResult("Claude Code MCP", configPath, "Claude Code MCP config changed while bunx verification; it was not overwritten.");
|
|
188884
|
+
}
|
|
188885
|
+
}
|
|
188886
|
+
await mkdir5(dirname8(configPath), { recursive: true });
|
|
188499
188887
|
await writeFile4(configPath, `${JSON.stringify(next, null, 2)}
|
|
188500
188888
|
`, "utf8");
|
|
188501
188889
|
return {
|
|
@@ -188507,6 +188895,166 @@ async function ensureClaudeMcp(context) {
|
|
|
188507
188895
|
detail
|
|
188508
188896
|
};
|
|
188509
188897
|
}
|
|
188898
|
+
async function ensureExistingCodexMcp(context, current, existing) {
|
|
188899
|
+
const currentBunxVerification = verifyCurrentBunxRegistration(context, "Codex MCP", existing);
|
|
188900
|
+
if (currentBunxVerification)
|
|
188901
|
+
return currentBunxVerification;
|
|
188902
|
+
if (existing.invocation.kind !== "legacy") {
|
|
188903
|
+
return preservedMcpResult("Codex MCP", existing, codexPreservedDetail(existing));
|
|
188904
|
+
}
|
|
188905
|
+
const replacement = migrationReplacementForContext(context, existing.invocation);
|
|
188906
|
+
if (!replacement) {
|
|
188907
|
+
return preservedMcpResult("Codex MCP", existing, codexPreservedDetail(existing));
|
|
188908
|
+
}
|
|
188909
|
+
const detail = legacyMigrationDetail(existing.path, existing.invocation, replacement);
|
|
188910
|
+
if (!context.write) {
|
|
188911
|
+
return {
|
|
188912
|
+
kind: "mcp",
|
|
188913
|
+
registration: "preserved",
|
|
188914
|
+
title: "Codex MCP",
|
|
188915
|
+
status: "dry-run",
|
|
188916
|
+
path: existing.path,
|
|
188917
|
+
detail: `${detail}${bunxVerificationPendingDetail(context, replacement)}`
|
|
188918
|
+
};
|
|
188919
|
+
}
|
|
188920
|
+
if (!context.force || context.customCommand) {
|
|
188921
|
+
return preservedMcpResult("Codex MCP", existing, `${detail}
|
|
188922
|
+
Legacy registration was kept. Re-run with --write --force to migrate this exact invocation.`);
|
|
188923
|
+
}
|
|
188924
|
+
if (requiresExplicitBunxRunnerForMigration(context, replacement)) {
|
|
188925
|
+
return preservedMcpResult("Codex MCP", existing, `${detail}
|
|
188926
|
+
Legacy Bun registration was kept. Re-run with --write --runner bunx --force to verify and migrate this exact invocation, or with --runner npx --force to migrate it using npx.`);
|
|
188927
|
+
}
|
|
188928
|
+
const unsupportedBunx = unsupportedBunxResult(context, "Codex MCP", existing.path, replacement, { migration: true });
|
|
188929
|
+
if (unsupportedBunx)
|
|
188930
|
+
return unsupportedBunx;
|
|
188931
|
+
const safety = await inspectMigrationFile(existing.path);
|
|
188932
|
+
if (!safety.ok) {
|
|
188933
|
+
return preservedMcpResult("Codex MCP", existing, `${detail}
|
|
188934
|
+
${safety.detail}`);
|
|
188935
|
+
}
|
|
188936
|
+
if (context.beforeManualMcpWrite) {
|
|
188937
|
+
await context.beforeManualMcpWrite(existing.path);
|
|
188938
|
+
}
|
|
188939
|
+
const latest = await readOptionalFile(existing.path);
|
|
188940
|
+
if (latest !== current) {
|
|
188941
|
+
return migrationConflictResult("Codex MCP", existing.path, "Codex MCP config changed after inspection; it was not overwritten.");
|
|
188942
|
+
}
|
|
188943
|
+
const next = patchCodexLegacyInvocation(latest, replacement);
|
|
188944
|
+
if (!next) {
|
|
188945
|
+
return preservedMcpResult("Codex MCP", existing, `${detail}
|
|
188946
|
+
The TOML shape is not a safe single-line legacy target; migrate it manually.`);
|
|
188947
|
+
}
|
|
188948
|
+
const verified = inspectCodexMcpContent(next, existing.path, context.cwd, context.home);
|
|
188949
|
+
if (!isCurrentMcpCommand(verified?.invocation, replacement)) {
|
|
188950
|
+
return preservedMcpResult("Codex MCP", existing, `${detail}
|
|
188951
|
+
The proposed TOML patch could not be verified; it was not written.`);
|
|
188952
|
+
}
|
|
188953
|
+
try {
|
|
188954
|
+
await writeFileAtomically(existing.path, next, safety.safety, latest, {
|
|
188955
|
+
beforeCommit: context.beforeManualMcpCommit,
|
|
188956
|
+
afterExpectedContentsCheck: context.afterManualMcpValidation,
|
|
188957
|
+
beforeRename: context.beforeManualMcpRename,
|
|
188958
|
+
replace: context.manualMcpRename
|
|
188959
|
+
});
|
|
188960
|
+
} catch (error51) {
|
|
188961
|
+
if (error51 instanceof MigrationCommittedError) {
|
|
188962
|
+
return migrationConflictResult("Codex MCP", existing.path, "Codex MCP migration may have been committed but could not be verified; inspect the current config before retrying.");
|
|
188963
|
+
}
|
|
188964
|
+
if (error51 instanceof MigrationConflictError) {
|
|
188965
|
+
return migrationConflictResult("Codex MCP", existing.path, "Codex MCP config changed before migration could be committed; it was not overwritten.");
|
|
188966
|
+
}
|
|
188967
|
+
throw error51;
|
|
188968
|
+
}
|
|
188969
|
+
return {
|
|
188970
|
+
kind: "mcp",
|
|
188971
|
+
registration: "migrated",
|
|
188972
|
+
title: "Codex MCP",
|
|
188973
|
+
status: "updated",
|
|
188974
|
+
path: existing.path,
|
|
188975
|
+
detail: "migrated exact legacy [mcp_servers.kyoso] invocation to explicit package/executable args"
|
|
188976
|
+
};
|
|
188977
|
+
}
|
|
188978
|
+
async function ensureExistingClaudeProjectMcp(context, content, existing) {
|
|
188979
|
+
const currentBunxVerification = verifyCurrentBunxRegistration(context, "Claude Code MCP", existing);
|
|
188980
|
+
if (currentBunxVerification)
|
|
188981
|
+
return currentBunxVerification;
|
|
188982
|
+
if (existing.invocation.kind !== "legacy") {
|
|
188983
|
+
return preservedMcpResult("Claude Code MCP", existing, claudePreservedDetail(existing));
|
|
188984
|
+
}
|
|
188985
|
+
const replacement = migrationReplacementForContext(context, existing.invocation);
|
|
188986
|
+
if (!replacement) {
|
|
188987
|
+
return preservedMcpResult("Claude Code MCP", existing, claudePreservedDetail(existing));
|
|
188988
|
+
}
|
|
188989
|
+
const detail = legacyMigrationDetail(existing.path, existing.invocation, replacement);
|
|
188990
|
+
if (!context.write) {
|
|
188991
|
+
return {
|
|
188992
|
+
kind: "mcp",
|
|
188993
|
+
registration: "preserved",
|
|
188994
|
+
title: "Claude Code MCP",
|
|
188995
|
+
status: "dry-run",
|
|
188996
|
+
path: existing.path,
|
|
188997
|
+
detail: `${detail}${bunxVerificationPendingDetail(context, replacement)}`
|
|
188998
|
+
};
|
|
188999
|
+
}
|
|
189000
|
+
if (!context.force || context.customCommand) {
|
|
189001
|
+
return preservedMcpResult("Claude Code MCP", existing, `${detail}
|
|
189002
|
+
Legacy registration was kept. Re-run with --write --force to migrate this exact invocation.`);
|
|
189003
|
+
}
|
|
189004
|
+
if (requiresExplicitBunxRunnerForMigration(context, replacement)) {
|
|
189005
|
+
return preservedMcpResult("Claude Code MCP", existing, `${detail}
|
|
189006
|
+
Legacy Bun registration was kept. Re-run with --write --runner bunx --force to verify and migrate this exact invocation, or with --runner npx --force to migrate it using npx.`);
|
|
189007
|
+
}
|
|
189008
|
+
const unsupportedBunx = unsupportedBunxResult(context, "Claude Code MCP", existing.path, replacement, { migration: true });
|
|
189009
|
+
if (unsupportedBunx)
|
|
189010
|
+
return unsupportedBunx;
|
|
189011
|
+
const safety = await inspectMigrationFile(existing.path);
|
|
189012
|
+
if (!safety.ok) {
|
|
189013
|
+
return preservedMcpResult("Claude Code MCP", existing, `${detail}
|
|
189014
|
+
${safety.detail}`);
|
|
189015
|
+
}
|
|
189016
|
+
if (context.beforeManualMcpWrite) {
|
|
189017
|
+
await context.beforeManualMcpWrite(existing.path);
|
|
189018
|
+
}
|
|
189019
|
+
const latest = await readOptionalFile(existing.path);
|
|
189020
|
+
if (latest !== content) {
|
|
189021
|
+
return migrationConflictResult("Claude Code MCP", existing.path, "Claude Code MCP config changed after inspection; it was not overwritten.");
|
|
189022
|
+
}
|
|
189023
|
+
const next = patchClaudeProjectMcpInvocation(latest, replacement);
|
|
189024
|
+
if (!next) {
|
|
189025
|
+
return preservedMcpResult("Claude Code MCP", existing, `${detail}
|
|
189026
|
+
The JSON shape is not a safe exact legacy target; migrate it manually.`);
|
|
189027
|
+
}
|
|
189028
|
+
const verified = inspectClaudeProjectMcp(parseJsonObject(existing.path, next), existing.path);
|
|
189029
|
+
if (!isCurrentMcpCommand(verified?.invocation, replacement)) {
|
|
189030
|
+
return preservedMcpResult("Claude Code MCP", existing, `${detail}
|
|
189031
|
+
The proposed JSON patch could not be verified; it was not written.`);
|
|
189032
|
+
}
|
|
189033
|
+
try {
|
|
189034
|
+
await writeFileAtomically(existing.path, next, safety.safety, latest, {
|
|
189035
|
+
beforeCommit: context.beforeManualMcpCommit,
|
|
189036
|
+
afterExpectedContentsCheck: context.afterManualMcpValidation,
|
|
189037
|
+
beforeRename: context.beforeManualMcpRename,
|
|
189038
|
+
replace: context.manualMcpRename
|
|
189039
|
+
});
|
|
189040
|
+
} catch (error51) {
|
|
189041
|
+
if (error51 instanceof MigrationCommittedError) {
|
|
189042
|
+
return migrationConflictResult("Claude Code MCP", existing.path, "Claude Code MCP migration may have been committed but could not be verified; inspect the current config before retrying.");
|
|
189043
|
+
}
|
|
189044
|
+
if (error51 instanceof MigrationConflictError) {
|
|
189045
|
+
return migrationConflictResult("Claude Code MCP", existing.path, "Claude Code MCP config changed before migration could be committed; it was not overwritten.");
|
|
189046
|
+
}
|
|
189047
|
+
throw error51;
|
|
189048
|
+
}
|
|
189049
|
+
return {
|
|
189050
|
+
kind: "mcp",
|
|
189051
|
+
registration: "migrated",
|
|
189052
|
+
title: "Claude Code MCP",
|
|
189053
|
+
status: "updated",
|
|
189054
|
+
path: existing.path,
|
|
189055
|
+
detail: "migrated exact legacy mcpServers.kyoso invocation to explicit package/executable args"
|
|
189056
|
+
};
|
|
189057
|
+
}
|
|
188510
189058
|
function ensureClaudeGlobalMcp(context) {
|
|
188511
189059
|
const configPath = join6(context.home, ".claude.json");
|
|
188512
189060
|
const existingMcp = detectClaudeMcp(configPath, context.cwd, context.home);
|
|
@@ -188517,7 +189065,7 @@ function ensureClaudeGlobalMcp(context) {
|
|
|
188517
189065
|
title: "Claude Code MCP",
|
|
188518
189066
|
status: "skipped",
|
|
188519
189067
|
path: configPath,
|
|
188520
|
-
detail: existingMcp.status === "disabled" ? disabledClaudeMcpDetail(configPath) :
|
|
189068
|
+
detail: existingMcp.status === "disabled" ? disabledClaudeMcpDetail(configPath) : claudeGlobalMcpPreservedDetail(existingMcp)
|
|
188521
189069
|
};
|
|
188522
189070
|
}
|
|
188523
189071
|
const json2 = JSON.stringify(buildClaudeMcpEntry(context.mcpCommand, context.withOpenRouter));
|
|
@@ -188530,9 +189078,12 @@ function ensureClaudeGlobalMcp(context) {
|
|
|
188530
189078
|
title: "Claude Code MCP",
|
|
188531
189079
|
status: "dry-run",
|
|
188532
189080
|
path: configPath,
|
|
188533
|
-
detail: commandLine
|
|
189081
|
+
detail: `${commandLine}${bunxVerificationPendingDetail(context, context.mcpCommand)}`
|
|
188534
189082
|
};
|
|
188535
189083
|
}
|
|
189084
|
+
const unsupportedBunx = unsupportedBunxResult(context, "Claude Code MCP", configPath, context.mcpCommand);
|
|
189085
|
+
if (unsupportedBunx)
|
|
189086
|
+
return unsupportedBunx;
|
|
188536
189087
|
const result = spawnSync2("claude", args, { encoding: "utf8" });
|
|
188537
189088
|
if (result.status !== 0) {
|
|
188538
189089
|
throw new Error(result.stderr || result.stdout || "claude mcp add-json failed");
|
|
@@ -188546,6 +189097,27 @@ function ensureClaudeGlobalMcp(context) {
|
|
|
188546
189097
|
detail: commandLine
|
|
188547
189098
|
};
|
|
188548
189099
|
}
|
|
189100
|
+
function claudeGlobalMcpPreservedDetail(detection) {
|
|
189101
|
+
if (detection.registrations.length !== 1) {
|
|
189102
|
+
return "Multiple existing mcpServers.kyoso registrations were kept; their effective precedence is not inferred.";
|
|
189103
|
+
}
|
|
189104
|
+
const registration = detection.registrations[0];
|
|
189105
|
+
if (registration?.invocation.kind !== "legacy") {
|
|
189106
|
+
return "existing mcpServers.kyoso kept";
|
|
189107
|
+
}
|
|
189108
|
+
const scope = registration.scope === "claude-global" ? "user" : "project-scoped user-config";
|
|
189109
|
+
return `existing ${scope} mcpServers.kyoso uses legacy package-runner arguments and was kept. Automatic migration supports only a project .mcp.json; update ${registration.path} manually.`;
|
|
189110
|
+
}
|
|
189111
|
+
function claudeProjectMcpScopeConflictResult(projectPath, userConfig) {
|
|
189112
|
+
return {
|
|
189113
|
+
kind: "mcp",
|
|
189114
|
+
registration: "preserved",
|
|
189115
|
+
title: "Claude Code MCP",
|
|
189116
|
+
status: "skipped",
|
|
189117
|
+
path: projectPath,
|
|
189118
|
+
detail: `Claude user-config MCP registration${userConfig.registrations.length === 1 ? "" : "s"} at ${userConfig.paths.join(", ")} ${userConfig.registrations.length === 1 ? "was" : "were"} kept. Project setup does not infer effective precedence; update the intended scope manually.`
|
|
189119
|
+
};
|
|
189120
|
+
}
|
|
188549
189121
|
async function ensureSkill(context, client, title) {
|
|
188550
189122
|
const result = await ensureManagedSkill({
|
|
188551
189123
|
sourceDir: context.sourceSkillDir,
|
|
@@ -188570,8 +189142,8 @@ function renderSetupOverview(context) {
|
|
|
188570
189142
|
"Kyoso setup",
|
|
188571
189143
|
"",
|
|
188572
189144
|
"Clients",
|
|
188573
|
-
` codex: MCP ${
|
|
188574
|
-
` claude-code: MCP ${
|
|
189145
|
+
` codex: MCP ${setupOverviewMcpStatus(detected.codex, context)}, skill ${statusWord(detected.codex.skill)}`,
|
|
189146
|
+
` claude-code: MCP ${setupOverviewMcpStatus(detected["claude-code"], context)}, skill ${statusWord(detected["claude-code"].skill)}`,
|
|
188575
189147
|
"",
|
|
188576
189148
|
"Commands",
|
|
188577
189149
|
" kyoso setup codex [--write] [--with-openrouter] [--runner npx|bunx] [--command <command>] [--global] [--force]",
|
|
@@ -188584,151 +189156,852 @@ function renderSetupOverview(context) {
|
|
|
188584
189156
|
].join(`
|
|
188585
189157
|
`);
|
|
188586
189158
|
}
|
|
188587
|
-
function
|
|
188588
|
-
|
|
188589
|
-
|
|
188590
|
-
|
|
188591
|
-
|
|
188592
|
-
|
|
188593
|
-
|
|
188594
|
-
""
|
|
188595
|
-
|
|
188596
|
-
|
|
188597
|
-
|
|
188598
|
-
|
|
188599
|
-
|
|
188600
|
-
|
|
188601
|
-
|
|
188602
|
-
|
|
188603
|
-
|
|
188604
|
-
|
|
188605
|
-
|
|
188606
|
-
|
|
188607
|
-
|
|
188608
|
-
|
|
188609
|
-
|
|
188610
|
-
|
|
188611
|
-
|
|
188612
|
-
|
|
188613
|
-
|
|
188614
|
-
|
|
188615
|
-
|
|
189159
|
+
function setupOverviewMcpStatus(detection, context) {
|
|
189160
|
+
if (detection.manualMcpStatus === "missing")
|
|
189161
|
+
return "missing";
|
|
189162
|
+
if (detection.manualMcpRegistrations.length !== 1)
|
|
189163
|
+
return "unverified";
|
|
189164
|
+
const registration = detection.manualMcpRegistrations[0];
|
|
189165
|
+
if (!registration)
|
|
189166
|
+
return "unverified";
|
|
189167
|
+
if (registration.status === "disabled")
|
|
189168
|
+
return "disabled";
|
|
189169
|
+
if (registration.status !== "enabled")
|
|
189170
|
+
return "unverified";
|
|
189171
|
+
const invocation = registration.invocation;
|
|
189172
|
+
if (invocation.kind === "legacy")
|
|
189173
|
+
return "repair required (legacy)";
|
|
189174
|
+
if (invocation.kind === "custom")
|
|
189175
|
+
return "custom/unverified";
|
|
189176
|
+
if (invocation.kind !== "current")
|
|
189177
|
+
return "unverified";
|
|
189178
|
+
if (invocation.runner === "npx") {
|
|
189179
|
+
return commandExists2("npx", context.env) ? "ok" : "npx missing";
|
|
189180
|
+
}
|
|
189181
|
+
if (invocation.runner === "bunx") {
|
|
189182
|
+
return commandExists2("bunx", context.env) ? "bunx unverified" : "bunx missing";
|
|
189183
|
+
}
|
|
189184
|
+
return "unverified";
|
|
189185
|
+
}
|
|
189186
|
+
function renderResults(context, results) {
|
|
189187
|
+
const mcpSteps = results.filter((result) => result.kind === "mcp");
|
|
189188
|
+
const includesGeneratedMcp = mcpSteps.some((result) => result.registration === "generated");
|
|
189189
|
+
const includesGeneratedCodexMcp = mcpSteps.some((result) => result.registration === "generated" && result.title === "Codex MCP");
|
|
189190
|
+
const includesPreservedMcp = mcpSteps.some((result) => result.registration === "preserved");
|
|
189191
|
+
return [
|
|
189192
|
+
"Kyoso setup",
|
|
189193
|
+
"",
|
|
189194
|
+
...results.flatMap((result) => [
|
|
189195
|
+
`${result.title}: ${result.status}${result.path ? ` (${result.path})` : ""}`,
|
|
189196
|
+
...result.detail ? indent(result.detail).split(`
|
|
189197
|
+
`) : []
|
|
189198
|
+
]),
|
|
189199
|
+
...mcpSteps.length > 0 ? [
|
|
189200
|
+
"",
|
|
189201
|
+
"Credential scope",
|
|
189202
|
+
...includesGeneratedMcp ? [
|
|
189203
|
+
context.withOpenRouter ? " Newly generated and dry-run MCP registrations include OPENROUTER_API_KEY because --with-openrouter was set." : " Newly generated and dry-run MCP registrations omit OPENROUTER_API_KEY. Use --with-openrouter before writing a new registration to include it."
|
|
189204
|
+
] : [],
|
|
189205
|
+
...includesGeneratedCodexMcp ? [
|
|
189206
|
+
" Newly generated and dry-run Codex MCP registrations intentionally forward CODEX_ACCESS_TOKEN for default Codex authentication; OpenRouter mode withholds it from the Codex child."
|
|
189207
|
+
] : [],
|
|
189208
|
+
...includesPreservedMcp ? [
|
|
189209
|
+
" Existing MCP registrations were preserved unchanged; --with-openrouter does not edit them."
|
|
189210
|
+
] : [],
|
|
189211
|
+
" Use --with-openrouter only when OpenRouter is intentionally selected."
|
|
189212
|
+
] : []
|
|
189213
|
+
].join(`
|
|
189214
|
+
`);
|
|
189215
|
+
}
|
|
189216
|
+
function parseClient(client) {
|
|
189217
|
+
if (client === undefined)
|
|
189218
|
+
return;
|
|
189219
|
+
if (client === "codex" || client === "claude-code")
|
|
189220
|
+
return client;
|
|
189221
|
+
throw new Error(`Invalid setup client "${client}". Expected codex or claude-code.`);
|
|
189222
|
+
}
|
|
189223
|
+
function validateSkillOnlyOptions(options, client) {
|
|
189224
|
+
if (!options.skillOnly)
|
|
189225
|
+
return;
|
|
189226
|
+
if (!client) {
|
|
189227
|
+
throw new Error("--skill-only requires setup client codex or claude-code.");
|
|
189228
|
+
}
|
|
189229
|
+
if (options.runner !== undefined) {
|
|
189230
|
+
throw new Error("--skill-only cannot be combined with --runner.");
|
|
189231
|
+
}
|
|
189232
|
+
if (options.command !== undefined) {
|
|
189233
|
+
throw new Error("--skill-only cannot be combined with --command.");
|
|
189234
|
+
}
|
|
189235
|
+
if (options.withOpenRouter) {
|
|
189236
|
+
throw new Error("--skill-only cannot be combined with --with-openrouter.");
|
|
189237
|
+
}
|
|
189238
|
+
}
|
|
189239
|
+
function parseRunner(runner) {
|
|
189240
|
+
if (runner === undefined || runner === "npx")
|
|
189241
|
+
return "npx";
|
|
189242
|
+
if (runner === "bunx")
|
|
189243
|
+
return "bunx";
|
|
189244
|
+
throw new Error(`Invalid --runner value "${runner}". Expected npx or bunx.`);
|
|
189245
|
+
}
|
|
189246
|
+
function parseCommandSpec(value) {
|
|
189247
|
+
const parts = splitCommand(value);
|
|
189248
|
+
const [command, ...args] = parts;
|
|
189249
|
+
if (!command)
|
|
189250
|
+
throw new Error("--command must not be empty");
|
|
189251
|
+
return { command, args };
|
|
189252
|
+
}
|
|
189253
|
+
function splitCommand(value) {
|
|
189254
|
+
const parts = [];
|
|
189255
|
+
let current = "";
|
|
189256
|
+
let quote;
|
|
189257
|
+
for (const char of value.trim()) {
|
|
189258
|
+
if (quote) {
|
|
189259
|
+
if (char === quote) {
|
|
189260
|
+
quote = undefined;
|
|
189261
|
+
} else {
|
|
189262
|
+
current += char;
|
|
189263
|
+
}
|
|
189264
|
+
continue;
|
|
189265
|
+
}
|
|
189266
|
+
if (char === '"' || char === "'") {
|
|
189267
|
+
quote = char;
|
|
189268
|
+
continue;
|
|
189269
|
+
}
|
|
189270
|
+
if (/\s/.test(char)) {
|
|
189271
|
+
if (current.length > 0) {
|
|
189272
|
+
parts.push(current);
|
|
189273
|
+
current = "";
|
|
189274
|
+
}
|
|
189275
|
+
continue;
|
|
189276
|
+
}
|
|
189277
|
+
current += char;
|
|
189278
|
+
}
|
|
189279
|
+
if (quote)
|
|
189280
|
+
throw new Error("--command has an unterminated quote");
|
|
189281
|
+
if (current.length > 0)
|
|
189282
|
+
parts.push(current);
|
|
189283
|
+
return parts;
|
|
189284
|
+
}
|
|
189285
|
+
function resolveBundledSkillDir() {
|
|
189286
|
+
const start = dirname8(fileURLToPath(import.meta.url));
|
|
189287
|
+
let current = start;
|
|
189288
|
+
for (let depth = 0;depth < 5; depth += 1) {
|
|
189289
|
+
const candidate = join6(current, ".agents", "skills", "kyoso-review");
|
|
189290
|
+
if (existsSync2(join6(candidate, "SKILL.md")))
|
|
189291
|
+
return candidate;
|
|
189292
|
+
current = dirname8(current);
|
|
189293
|
+
}
|
|
189294
|
+
throw new Error("Bundled kyoso-review skill was not found in this package.");
|
|
189295
|
+
}
|
|
189296
|
+
async function readOptionalFile(path) {
|
|
189297
|
+
try {
|
|
189298
|
+
return await readFile6(path, "utf8");
|
|
189299
|
+
} catch (error51) {
|
|
189300
|
+
if (isMissingPathError4(error51))
|
|
189301
|
+
return "";
|
|
189302
|
+
throw error51;
|
|
189303
|
+
}
|
|
189304
|
+
}
|
|
189305
|
+
function parseJsonObject(path, content) {
|
|
189306
|
+
if (content.trim().length === 0)
|
|
189307
|
+
return {};
|
|
189308
|
+
const parsed = JSON.parse(content);
|
|
189309
|
+
if (!isRecord10(parsed))
|
|
189310
|
+
throw new Error(`${path} must contain a JSON object`);
|
|
189311
|
+
return parsed;
|
|
189312
|
+
}
|
|
189313
|
+
function inspectCodexAppendSafety(content, cwd, home) {
|
|
189314
|
+
if (content.trim().length === 0)
|
|
189315
|
+
return { ok: true };
|
|
189316
|
+
try {
|
|
189317
|
+
const parsed = parse5(content);
|
|
189318
|
+
if (!isRecord10(parsed)) {
|
|
189319
|
+
return {
|
|
189320
|
+
ok: false,
|
|
189321
|
+
detail: "Codex config is not a TOML object and was left unchanged."
|
|
189322
|
+
};
|
|
189323
|
+
}
|
|
189324
|
+
if (hasUnprobedProjectIntegrationOverride(parsed, cwd, home)) {
|
|
189325
|
+
return {
|
|
189326
|
+
ok: false,
|
|
189327
|
+
detail: "Codex has a project-scoped MCP or Plugin override; the global config was left unchanged."
|
|
189328
|
+
};
|
|
189329
|
+
}
|
|
189330
|
+
if ("mcp_servers" in parsed && !isRecord10(parsed.mcp_servers)) {
|
|
189331
|
+
return {
|
|
189332
|
+
ok: false,
|
|
189333
|
+
detail: "Codex mcp_servers is malformed and was left unchanged."
|
|
189334
|
+
};
|
|
189335
|
+
}
|
|
189336
|
+
if (isRecord10(parsed.mcp_servers) && "kyoso" in parsed.mcp_servers) {
|
|
189337
|
+
return {
|
|
189338
|
+
ok: false,
|
|
189339
|
+
detail: "Codex already defines mcp_servers.kyoso in a form setup cannot safely extend; migrate it manually."
|
|
189340
|
+
};
|
|
189341
|
+
}
|
|
189342
|
+
if (hasTomlMcpServersAssignment(content)) {
|
|
189343
|
+
return {
|
|
189344
|
+
ok: false,
|
|
189345
|
+
detail: "Codex defines mcp_servers with an inline assignment setup cannot safely extend; add the registration manually."
|
|
189346
|
+
};
|
|
189347
|
+
}
|
|
189348
|
+
return { ok: true };
|
|
189349
|
+
} catch {
|
|
189350
|
+
return {
|
|
189351
|
+
ok: false,
|
|
189352
|
+
detail: "Codex config could not be parsed and was left unchanged."
|
|
189353
|
+
};
|
|
189354
|
+
}
|
|
189355
|
+
}
|
|
189356
|
+
function hasTomlMcpServersAssignment(content) {
|
|
189357
|
+
return /^[ \t]*(?:mcp_servers|"mcp_servers")[ \t]*=/m.test(content);
|
|
189358
|
+
}
|
|
189359
|
+
function inspectCodexMcpContent(content, path, cwd, home) {
|
|
189360
|
+
if (!hasCodexMcpContent(content))
|
|
189361
|
+
return;
|
|
189362
|
+
try {
|
|
189363
|
+
const parsed = parse5(content);
|
|
189364
|
+
if (hasUnprobedProjectIntegrationOverride(parsed, cwd, home)) {
|
|
189365
|
+
return manualMcpRegistration({
|
|
189366
|
+
path,
|
|
189367
|
+
scope: "codex-global",
|
|
189368
|
+
status: "unknown",
|
|
189369
|
+
value: undefined
|
|
189370
|
+
});
|
|
189371
|
+
}
|
|
189372
|
+
if (!isRecord10(parsed) || !isRecord10(parsed.mcp_servers)) {
|
|
189373
|
+
return manualMcpRegistration({
|
|
189374
|
+
path,
|
|
189375
|
+
scope: "codex-global",
|
|
189376
|
+
status: "unknown",
|
|
189377
|
+
value: undefined
|
|
189378
|
+
});
|
|
189379
|
+
}
|
|
189380
|
+
if (!("kyoso" in parsed.mcp_servers))
|
|
189381
|
+
return;
|
|
189382
|
+
return manualMcpRegistration({
|
|
189383
|
+
path,
|
|
189384
|
+
scope: "codex-global",
|
|
189385
|
+
status: mcpEntryStatus(parsed.mcp_servers.kyoso),
|
|
189386
|
+
value: parsed.mcp_servers.kyoso
|
|
189387
|
+
});
|
|
189388
|
+
} catch {
|
|
189389
|
+
return manualMcpRegistration({
|
|
189390
|
+
path,
|
|
189391
|
+
scope: "codex-global",
|
|
189392
|
+
status: "unknown",
|
|
189393
|
+
value: undefined
|
|
189394
|
+
});
|
|
189395
|
+
}
|
|
189396
|
+
}
|
|
189397
|
+
function inspectClaudeProjectMcp(current, path) {
|
|
189398
|
+
if (!("mcpServers" in current))
|
|
189399
|
+
return;
|
|
189400
|
+
if (!isRecord10(current.mcpServers)) {
|
|
189401
|
+
return manualMcpRegistration({
|
|
189402
|
+
path,
|
|
189403
|
+
scope: "claude-project",
|
|
189404
|
+
status: "unknown",
|
|
189405
|
+
value: undefined
|
|
189406
|
+
});
|
|
189407
|
+
}
|
|
189408
|
+
if (!("kyoso" in current.mcpServers))
|
|
189409
|
+
return;
|
|
189410
|
+
return manualMcpRegistration({
|
|
189411
|
+
path,
|
|
189412
|
+
scope: "claude-project",
|
|
189413
|
+
status: mcpEntryStatus(current.mcpServers.kyoso),
|
|
189414
|
+
value: current.mcpServers.kyoso
|
|
189415
|
+
});
|
|
189416
|
+
}
|
|
189417
|
+
function manualMcpRegistration(options) {
|
|
189418
|
+
const { value, ...registration } = options;
|
|
189419
|
+
const invocation = inspectManualMcpInvocation(value);
|
|
189420
|
+
return {
|
|
189421
|
+
...registration,
|
|
189422
|
+
invocation,
|
|
189423
|
+
autoMigrationEligible: (options.scope === "codex-global" || options.scope === "claude-project") && invocation.kind === "legacy"
|
|
189424
|
+
};
|
|
189425
|
+
}
|
|
189426
|
+
function preservedMcpResult(title, registration, detail) {
|
|
189427
|
+
return {
|
|
189428
|
+
kind: "mcp",
|
|
189429
|
+
registration: "preserved",
|
|
189430
|
+
title,
|
|
189431
|
+
status: "skipped",
|
|
189432
|
+
path: registration.path,
|
|
189433
|
+
detail
|
|
189434
|
+
};
|
|
189435
|
+
}
|
|
189436
|
+
function codexPreservedDetail(registration) {
|
|
189437
|
+
if (registration.status === "disabled") {
|
|
189438
|
+
return disabledCodexMcpDetail(registration.path);
|
|
189439
|
+
}
|
|
189440
|
+
if (registration.invocation.kind === "current") {
|
|
189441
|
+
return "existing [mcp_servers.kyoso] uses the current explicit package/executable invocation and was kept.";
|
|
189442
|
+
}
|
|
189443
|
+
if (registration.invocation.kind === "legacy") {
|
|
189444
|
+
return "existing [mcp_servers.kyoso] uses a legacy invocation and was kept unchanged.";
|
|
189445
|
+
}
|
|
189446
|
+
return `existing [mcp_servers.kyoso] is ${formatInvocationKind(registration.invocation.kind)} and was kept unchanged. ${registration.invocation.reason}`;
|
|
189447
|
+
}
|
|
189448
|
+
function claudePreservedDetail(registration) {
|
|
189449
|
+
if (registration.status === "disabled") {
|
|
189450
|
+
return disabledClaudeMcpDetail(registration.path);
|
|
189451
|
+
}
|
|
189452
|
+
if (registration.invocation.kind === "current") {
|
|
189453
|
+
return "existing mcpServers.kyoso uses the current explicit package/executable invocation and was kept.";
|
|
189454
|
+
}
|
|
189455
|
+
if (registration.invocation.kind === "legacy") {
|
|
189456
|
+
return "existing mcpServers.kyoso uses a legacy invocation and was kept unchanged.";
|
|
189457
|
+
}
|
|
189458
|
+
return `existing mcpServers.kyoso is ${formatInvocationKind(registration.invocation.kind)} and was kept unchanged. ${registration.invocation.reason}`;
|
|
189459
|
+
}
|
|
189460
|
+
function formatInvocationKind(kind) {
|
|
189461
|
+
if (kind === "custom")
|
|
189462
|
+
return "custom/unverified";
|
|
189463
|
+
return kind;
|
|
189464
|
+
}
|
|
189465
|
+
function legacyMigrationDetail(path, invocation, replacement) {
|
|
189466
|
+
const legacyArgs = invocation.legacyArgs;
|
|
189467
|
+
if (!invocation.runner || !legacyArgs) {
|
|
189468
|
+
return "legacy package-runner invocation detected; only --write --force may migrate it. The migration preview is unavailable because its exact command arguments could not be reconstructed.";
|
|
189469
|
+
}
|
|
189470
|
+
return [
|
|
189471
|
+
"legacy package-runner invocation detected; only --write --force may migrate it.",
|
|
189472
|
+
`--- ${formatMigrationPreviewValue(path)}`,
|
|
189473
|
+
`+++ ${formatMigrationPreviewValue(path)}`,
|
|
189474
|
+
"@@ Kyoso MCP invocation",
|
|
189475
|
+
`- command = ${formatMigrationPreviewValue(invocation.runner)}`,
|
|
189476
|
+
`- args = ${formatMigrationPreviewArgs(legacyArgs)}`,
|
|
189477
|
+
`+ command = ${formatMigrationPreviewValue(replacement.command)}`,
|
|
189478
|
+
`+ args = ${formatMigrationPreviewArgs(replacement.args)}`,
|
|
189479
|
+
"Only the Kyoso command and arguments are shown; other configuration values remain unchanged."
|
|
189480
|
+
].join(`
|
|
189481
|
+
`);
|
|
189482
|
+
}
|
|
189483
|
+
function patchCodexLegacyInvocation(content, replacement) {
|
|
189484
|
+
const tableMatches = [...content.matchAll(/^\s*\[mcp_servers\.kyoso]\s*$/gm)];
|
|
189485
|
+
if (tableMatches.length !== 1)
|
|
189486
|
+
return;
|
|
189487
|
+
const table = tableMatches[0];
|
|
189488
|
+
if (table?.index === undefined)
|
|
189489
|
+
return;
|
|
189490
|
+
const bodyStart = table.index + table[0].length;
|
|
189491
|
+
const remaining = content.slice(bodyStart);
|
|
189492
|
+
const nextTableOffset = remaining.search(/^\s*\[/m);
|
|
189493
|
+
const bodyEnd = nextTableOffset === -1 ? content.length : bodyStart + nextTableOffset;
|
|
189494
|
+
const body = content.slice(bodyStart, bodyEnd);
|
|
189495
|
+
const commandMatches = [
|
|
189496
|
+
...body.matchAll(/^([ \t]*command[ \t]*=[ \t]*)"[^"\r\n]*"([ \t]*(?:#.*)?)(\r?\n|$)/gm)
|
|
189497
|
+
];
|
|
189498
|
+
const argsMatches = [
|
|
189499
|
+
...body.matchAll(/^([ \t]*args[ \t]*=[ \t]*)([^\r\n]*)(\r?\n|$)/gm)
|
|
189500
|
+
];
|
|
189501
|
+
if (commandMatches.length !== 1 || argsMatches.length !== 1)
|
|
189502
|
+
return;
|
|
189503
|
+
const argsMatch = argsMatches[0];
|
|
189504
|
+
const argsLine = argsMatch?.[0];
|
|
189505
|
+
const argsPrefix = argsMatch?.[1];
|
|
189506
|
+
const argsValue = argsMatch?.[2];
|
|
189507
|
+
const argsNewline = argsMatch?.[3];
|
|
189508
|
+
if (argsLine === undefined || argsPrefix === undefined || argsValue === undefined || argsNewline === undefined) {
|
|
189509
|
+
return;
|
|
189510
|
+
}
|
|
189511
|
+
const patchedArgsValue = patchTomlInlineArrayValue(argsValue, replacement.args);
|
|
189512
|
+
if (patchedArgsValue === undefined)
|
|
189513
|
+
return;
|
|
189514
|
+
const commandPatched = body.replace(/^([ \t]*command[ \t]*=[ \t]*)"[^"\r\n]*"([ \t]*(?:#.*)?)(\r?\n|$)/m, (_line, prefix, suffix, newline) => `${prefix}${JSON.stringify(replacement.command)}${suffix}${newline}`);
|
|
189515
|
+
const nextBody = commandPatched.replace(argsLine, `${argsPrefix}${patchedArgsValue}${argsNewline}`);
|
|
189516
|
+
return `${content.slice(0, bodyStart)}${nextBody}${content.slice(bodyEnd)}`;
|
|
189517
|
+
}
|
|
189518
|
+
function patchTomlInlineArrayValue(value, replacement) {
|
|
189519
|
+
const closingIndex = tomlInlineArrayClosingIndex(value);
|
|
189520
|
+
if (closingIndex === undefined)
|
|
189521
|
+
return;
|
|
189522
|
+
const suffix = value.slice(closingIndex + 1);
|
|
189523
|
+
if (!/^[ \t]*(?:#.*)?$/.test(suffix))
|
|
189524
|
+
return;
|
|
189525
|
+
return `${JSON.stringify(replacement)}${suffix}`;
|
|
189526
|
+
}
|
|
189527
|
+
function tomlInlineArrayClosingIndex(value) {
|
|
189528
|
+
if (!value.startsWith("["))
|
|
189529
|
+
return;
|
|
189530
|
+
let depth = 0;
|
|
189531
|
+
let quote;
|
|
189532
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
189533
|
+
const character = value[index];
|
|
189534
|
+
if (character === undefined)
|
|
189535
|
+
return;
|
|
189536
|
+
if (quote) {
|
|
189537
|
+
if (quote === '"' && character === "\\") {
|
|
189538
|
+
index += 1;
|
|
189539
|
+
continue;
|
|
189540
|
+
}
|
|
189541
|
+
if (character === quote) {
|
|
189542
|
+
if (quote === "'" && value[index + 1] === "'") {
|
|
189543
|
+
index += 1;
|
|
189544
|
+
continue;
|
|
189545
|
+
}
|
|
189546
|
+
quote = undefined;
|
|
189547
|
+
}
|
|
189548
|
+
continue;
|
|
189549
|
+
}
|
|
189550
|
+
if (character === '"' || character === "'") {
|
|
189551
|
+
quote = character;
|
|
189552
|
+
continue;
|
|
189553
|
+
}
|
|
189554
|
+
if (character === "[") {
|
|
189555
|
+
depth += 1;
|
|
189556
|
+
continue;
|
|
189557
|
+
}
|
|
189558
|
+
if (character === "]") {
|
|
189559
|
+
depth -= 1;
|
|
189560
|
+
if (depth === 0)
|
|
189561
|
+
return index;
|
|
189562
|
+
if (depth < 0)
|
|
189563
|
+
return;
|
|
189564
|
+
}
|
|
189565
|
+
}
|
|
189566
|
+
return;
|
|
189567
|
+
}
|
|
189568
|
+
function isCurrentMcpCommand(invocation, replacement) {
|
|
189569
|
+
if (invocation?.kind !== "current" || invocation.runner !== replacement.command) {
|
|
189570
|
+
return false;
|
|
189571
|
+
}
|
|
189572
|
+
const packageSpec = replacement.command === "npx" ? replacement.args[1]?.slice("--package=".length) : replacement.args[1];
|
|
189573
|
+
return invocation.packageSpec === packageSpec;
|
|
189574
|
+
}
|
|
189575
|
+
function migrationReplacementForContext(context, invocation) {
|
|
189576
|
+
const replacement = invocation.replacement;
|
|
189577
|
+
if (!replacement || context.customCommand || !context.runnerExplicit || !isKyosoPackageRunner(context.mcpCommand.command) || context.mcpCommand.command === replacement.command) {
|
|
189578
|
+
return replacement;
|
|
189579
|
+
}
|
|
189580
|
+
const packageSpec = invocation.packageSpec;
|
|
189581
|
+
if (packageSpec === undefined || packageSpec !== KYOSO_PACKAGE_NAME && !packageSpec.startsWith(`${KYOSO_PACKAGE_NAME}@`)) {
|
|
189582
|
+
return replacement;
|
|
189583
|
+
}
|
|
189584
|
+
const version2 = packageSpec === KYOSO_PACKAGE_NAME ? undefined : packageSpec.slice(`${KYOSO_PACKAGE_NAME}@`.length);
|
|
189585
|
+
return buildKyosoPackageCommand({
|
|
189586
|
+
runner: context.mcpCommand.command,
|
|
189587
|
+
...version2 === undefined ? {} : { version: version2 },
|
|
189588
|
+
cliArgs: ["mcp"]
|
|
189589
|
+
});
|
|
189590
|
+
}
|
|
189591
|
+
function isKyosoPackageRunner(value) {
|
|
189592
|
+
return value === "npx" || value === "bunx";
|
|
189593
|
+
}
|
|
189594
|
+
function patchClaudeProjectMcpInvocation(content, replacement) {
|
|
189595
|
+
try {
|
|
189596
|
+
JSON.parse(content);
|
|
189597
|
+
const root = scanJsonValue(content, skipJsonWhitespace(content, 0));
|
|
189598
|
+
if (skipJsonWhitespace(content, root.end) !== content.length)
|
|
189599
|
+
return;
|
|
189600
|
+
const mcpServers = singleJsonObjectProperty(root, "mcpServers");
|
|
189601
|
+
const kyoso = mcpServers && singleJsonObjectProperty(mcpServers, "kyoso");
|
|
189602
|
+
const command = kyoso && singleJsonProperty(kyoso, "command");
|
|
189603
|
+
const args = kyoso && singleJsonProperty(kyoso, "args");
|
|
189604
|
+
if (!command || !args)
|
|
189605
|
+
return;
|
|
189606
|
+
return replaceJsonValueSpans(content, [
|
|
189607
|
+
{ ...command.value, replacement: JSON.stringify(replacement.command) },
|
|
189608
|
+
{ ...args.value, replacement: JSON.stringify(replacement.args) }
|
|
189609
|
+
]);
|
|
189610
|
+
} catch {
|
|
189611
|
+
return;
|
|
189612
|
+
}
|
|
189613
|
+
}
|
|
189614
|
+
function scanJsonValue(content, start) {
|
|
189615
|
+
const index = skipJsonWhitespace(content, start);
|
|
189616
|
+
const character = content[index];
|
|
189617
|
+
if (character === "{")
|
|
189618
|
+
return scanJsonObject(content, index);
|
|
189619
|
+
if (character === "[")
|
|
189620
|
+
return scanJsonArray(content, index);
|
|
189621
|
+
if (character === '"') {
|
|
189622
|
+
const end2 = scanJsonStringEnd(content, index);
|
|
189623
|
+
return { kind: "scalar", start: index, end: end2 };
|
|
189624
|
+
}
|
|
189625
|
+
let end = index;
|
|
189626
|
+
while (end < content.length && !/[\s,\]}]/.test(content[end] ?? "")) {
|
|
189627
|
+
end += 1;
|
|
189628
|
+
}
|
|
189629
|
+
if (end === index)
|
|
189630
|
+
throw new Error("expected JSON value");
|
|
189631
|
+
return { kind: "scalar", start: index, end };
|
|
189632
|
+
}
|
|
189633
|
+
function scanJsonObject(content, start) {
|
|
189634
|
+
let index = skipJsonWhitespace(content, start + 1);
|
|
189635
|
+
const entries = [];
|
|
189636
|
+
if (content[index] === "}") {
|
|
189637
|
+
return { kind: "object", start, end: index + 1, entries };
|
|
189638
|
+
}
|
|
189639
|
+
for (;; ) {
|
|
189640
|
+
if (content[index] !== '"')
|
|
189641
|
+
throw new Error("expected JSON object key");
|
|
189642
|
+
const keyEnd = scanJsonStringEnd(content, index);
|
|
189643
|
+
const key = JSON.parse(content.slice(index, keyEnd));
|
|
189644
|
+
if (typeof key !== "string")
|
|
189645
|
+
throw new Error("invalid JSON object key");
|
|
189646
|
+
index = skipJsonWhitespace(content, keyEnd);
|
|
189647
|
+
if (content[index] !== ":")
|
|
189648
|
+
throw new Error("expected JSON object separator");
|
|
189649
|
+
const value = scanJsonValue(content, index + 1);
|
|
189650
|
+
entries.push({ key, value });
|
|
189651
|
+
index = skipJsonWhitespace(content, value.end);
|
|
189652
|
+
if (content[index] === "}") {
|
|
189653
|
+
return { kind: "object", start, end: index + 1, entries };
|
|
189654
|
+
}
|
|
189655
|
+
if (content[index] !== ",")
|
|
189656
|
+
throw new Error("expected JSON object delimiter");
|
|
189657
|
+
index = skipJsonWhitespace(content, index + 1);
|
|
189658
|
+
}
|
|
189659
|
+
}
|
|
189660
|
+
function scanJsonArray(content, start) {
|
|
189661
|
+
let index = skipJsonWhitespace(content, start + 1);
|
|
189662
|
+
if (content[index] === "]")
|
|
189663
|
+
return { kind: "array", start, end: index + 1 };
|
|
189664
|
+
for (;; ) {
|
|
189665
|
+
const value = scanJsonValue(content, index);
|
|
189666
|
+
index = skipJsonWhitespace(content, value.end);
|
|
189667
|
+
if (content[index] === "]")
|
|
189668
|
+
return { kind: "array", start, end: index + 1 };
|
|
189669
|
+
if (content[index] !== ",")
|
|
189670
|
+
throw new Error("expected JSON array delimiter");
|
|
189671
|
+
index = skipJsonWhitespace(content, index + 1);
|
|
189672
|
+
}
|
|
189673
|
+
}
|
|
189674
|
+
function scanJsonStringEnd(content, start) {
|
|
189675
|
+
let escaped = false;
|
|
189676
|
+
for (let index = start + 1;index < content.length; index += 1) {
|
|
189677
|
+
const character = content[index];
|
|
189678
|
+
if (escaped) {
|
|
189679
|
+
escaped = false;
|
|
189680
|
+
continue;
|
|
189681
|
+
}
|
|
189682
|
+
if (character === "\\") {
|
|
189683
|
+
escaped = true;
|
|
189684
|
+
continue;
|
|
189685
|
+
}
|
|
189686
|
+
if (character === '"')
|
|
189687
|
+
return index + 1;
|
|
189688
|
+
}
|
|
189689
|
+
throw new Error("unterminated JSON string");
|
|
189690
|
+
}
|
|
189691
|
+
function skipJsonWhitespace(content, start) {
|
|
189692
|
+
let index = start;
|
|
189693
|
+
while (index < content.length && /\s/.test(content[index] ?? ""))
|
|
189694
|
+
index += 1;
|
|
189695
|
+
return index;
|
|
189696
|
+
}
|
|
189697
|
+
function singleJsonObjectProperty(object2, key) {
|
|
189698
|
+
const property = singleJsonProperty(object2, key);
|
|
189699
|
+
return property?.value.kind === "object" ? property.value : undefined;
|
|
189700
|
+
}
|
|
189701
|
+
function singleJsonProperty(object2, key) {
|
|
189702
|
+
if (object2.kind !== "object")
|
|
189703
|
+
return;
|
|
189704
|
+
const matches = object2.entries?.filter((entry) => entry.key === key) ?? [];
|
|
189705
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
189706
|
+
}
|
|
189707
|
+
function replaceJsonValueSpans(content, replacements) {
|
|
189708
|
+
const sorted = [...replacements].sort((left, right) => right.start - left.start);
|
|
189709
|
+
if (sorted[0]?.start === sorted[1]?.start)
|
|
189710
|
+
return;
|
|
189711
|
+
let next = content;
|
|
189712
|
+
for (const replacement of sorted) {
|
|
189713
|
+
next = `${next.slice(0, replacement.start)}${replacement.replacement}${next.slice(replacement.end)}`;
|
|
189714
|
+
}
|
|
189715
|
+
return next;
|
|
189716
|
+
}
|
|
189717
|
+
async function inspectMigrationFile(path, options = {}) {
|
|
189718
|
+
try {
|
|
189719
|
+
const before = await lstat3(path);
|
|
189720
|
+
if (!isSafeMigrationFile(before, options.expectedSafety, options.expectedNlink)) {
|
|
189721
|
+
return {
|
|
189722
|
+
ok: false,
|
|
189723
|
+
detail: "The existing config is not an unlinked regular file and was left for manual migration."
|
|
189724
|
+
};
|
|
189725
|
+
}
|
|
189726
|
+
if (options.expectedContents !== undefined) {
|
|
189727
|
+
const contents = await readFile6(path, "utf8");
|
|
189728
|
+
const after = await lstat3(path);
|
|
189729
|
+
if (!isSafeMigrationFile(after, options.expectedSafety, options.expectedNlink) || !sameMigrationFileIdentity(before, after) || contents !== options.expectedContents) {
|
|
189730
|
+
return {
|
|
189731
|
+
ok: false,
|
|
189732
|
+
detail: "The existing config changed after final migration validation and was left unchanged."
|
|
189733
|
+
};
|
|
189734
|
+
}
|
|
189735
|
+
return { ok: true, safety: migrationFileSafety(after) };
|
|
189736
|
+
}
|
|
189737
|
+
return { ok: true, safety: migrationFileSafety(before) };
|
|
189738
|
+
} catch (error51) {
|
|
189739
|
+
return {
|
|
189740
|
+
ok: false,
|
|
189741
|
+
detail: `The existing config could not be safely inspected: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
189742
|
+
};
|
|
189743
|
+
}
|
|
189744
|
+
}
|
|
189745
|
+
function isSafeMigrationFile(metadata, expectedSafety, expectedNlink = 1) {
|
|
189746
|
+
if (metadata.isSymbolicLink() || !metadata.isFile() || metadata.nlink !== expectedNlink) {
|
|
189747
|
+
return false;
|
|
189748
|
+
}
|
|
189749
|
+
return expectedSafety === undefined || sameMigrationFileIdentity(metadata, expectedSafety) && (metadata.mode & 511) === expectedSafety.mode;
|
|
189750
|
+
}
|
|
189751
|
+
function sameMigrationFileIdentity(metadata, expected) {
|
|
189752
|
+
return metadata.dev === expected.dev && metadata.ino === expected.ino;
|
|
189753
|
+
}
|
|
189754
|
+
function migrationFileSafety(metadata) {
|
|
189755
|
+
return {
|
|
189756
|
+
mode: metadata.mode & 511,
|
|
189757
|
+
dev: metadata.dev,
|
|
189758
|
+
ino: metadata.ino
|
|
189759
|
+
};
|
|
189760
|
+
}
|
|
189761
|
+
|
|
189762
|
+
class MigrationConflictError extends Error {
|
|
189763
|
+
}
|
|
189764
|
+
|
|
189765
|
+
class MigrationCommittedError extends Error {
|
|
189766
|
+
}
|
|
189767
|
+
function migrationConflictResult(title, path, detail) {
|
|
189768
|
+
return {
|
|
189769
|
+
kind: "mcp",
|
|
189770
|
+
registration: "preserved",
|
|
189771
|
+
title,
|
|
189772
|
+
status: "conflict",
|
|
189773
|
+
path,
|
|
189774
|
+
detail
|
|
189775
|
+
};
|
|
189776
|
+
}
|
|
189777
|
+
async function writeFileAtomically(path, contents, expectedSafety, expectedContents, options) {
|
|
189778
|
+
const directory = dirname8(path);
|
|
189779
|
+
const temporaryPath = join6(directory, `.${basename3(path)}.kyoso-${process.pid}-${randomUUID2()}.tmp`);
|
|
189780
|
+
let handle;
|
|
189781
|
+
let replacementCommitted = false;
|
|
189782
|
+
try {
|
|
189783
|
+
handle = await open(temporaryPath, "wx", expectedSafety.mode);
|
|
189784
|
+
await handle.writeFile(contents, "utf8");
|
|
189785
|
+
await chmod(temporaryPath, expectedSafety.mode);
|
|
189786
|
+
await handle.sync();
|
|
189787
|
+
await handle.close();
|
|
189788
|
+
handle = undefined;
|
|
189789
|
+
await options.beforeCommit?.(path);
|
|
189790
|
+
await options.afterExpectedContentsCheck?.(path);
|
|
189791
|
+
await options.beforeRename?.(path);
|
|
189792
|
+
const beforeRename = await inspectMigrationFile(path, {
|
|
189793
|
+
expectedContents,
|
|
189794
|
+
expectedSafety
|
|
189795
|
+
});
|
|
189796
|
+
if (!beforeRename.ok) {
|
|
189797
|
+
throw new MigrationConflictError(beforeRename.detail);
|
|
189798
|
+
}
|
|
189799
|
+
try {
|
|
189800
|
+
await (options.replace ?? rename2)(temporaryPath, path);
|
|
189801
|
+
replacementCommitted = true;
|
|
189802
|
+
} catch (error51) {
|
|
189803
|
+
const installed2 = await inspectMigrationFile(path, {
|
|
189804
|
+
expectedContents: contents
|
|
189805
|
+
});
|
|
189806
|
+
if (installed2.ok && installed2.safety.mode === expectedSafety.mode) {
|
|
189807
|
+
throw new MigrationCommittedError("manual MCP migration may have been committed before replacement reported an error");
|
|
189808
|
+
}
|
|
189809
|
+
throw error51;
|
|
189810
|
+
}
|
|
189811
|
+
await syncDirectory(directory);
|
|
189812
|
+
const installed = await inspectMigrationFile(path, {
|
|
189813
|
+
expectedContents: contents
|
|
189814
|
+
});
|
|
189815
|
+
if (!installed.ok || installed.safety.mode !== expectedSafety.mode) {
|
|
189816
|
+
throw new MigrationCommittedError("the replacement config could not be verified after installation");
|
|
189817
|
+
}
|
|
189818
|
+
} catch (error51) {
|
|
189819
|
+
if (error51 instanceof MigrationConflictError || error51 instanceof MigrationCommittedError) {
|
|
189820
|
+
throw error51;
|
|
189821
|
+
}
|
|
189822
|
+
if (replacementCommitted) {
|
|
189823
|
+
throw new MigrationCommittedError(`manual MCP migration may have been committed but could not be verified: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
189824
|
+
}
|
|
189825
|
+
throw new MigrationConflictError(`manual MCP migration failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
189826
|
+
} finally {
|
|
189827
|
+
await handle?.close().catch(() => {
|
|
189828
|
+
return;
|
|
189829
|
+
});
|
|
189830
|
+
await rm2(temporaryPath, { force: true }).catch(() => {
|
|
189831
|
+
return;
|
|
189832
|
+
});
|
|
189833
|
+
}
|
|
189834
|
+
}
|
|
189835
|
+
async function syncDirectory(path) {
|
|
189836
|
+
let handle;
|
|
189837
|
+
try {
|
|
189838
|
+
handle = await open(path, "r");
|
|
189839
|
+
await handle.sync();
|
|
189840
|
+
} catch (error51) {
|
|
189841
|
+
if (isUnsupportedDirectorySyncError(error51))
|
|
189842
|
+
return;
|
|
189843
|
+
throw error51;
|
|
189844
|
+
} finally {
|
|
189845
|
+
await handle?.close().catch(() => {
|
|
189846
|
+
return;
|
|
189847
|
+
});
|
|
189848
|
+
}
|
|
188616
189849
|
}
|
|
188617
|
-
function
|
|
188618
|
-
|
|
189850
|
+
function isUnsupportedDirectorySyncError(error51) {
|
|
189851
|
+
const code = errorCode(error51);
|
|
189852
|
+
return code === "EINVAL" || code === "ENOTSUP" || code === "EOPNOTSUPP" || process.platform === "win32" && code === "EPERM";
|
|
189853
|
+
}
|
|
189854
|
+
function errorCode(error51) {
|
|
189855
|
+
if (typeof error51 !== "object" || error51 === null || !("code" in error51)) {
|
|
188619
189856
|
return;
|
|
188620
|
-
|
|
188621
|
-
|
|
188622
|
-
throw new Error(`Invalid setup client "${client}". Expected codex or claude-code.`);
|
|
189857
|
+
}
|
|
189858
|
+
return typeof error51.code === "string" ? error51.code : undefined;
|
|
188623
189859
|
}
|
|
188624
|
-
function
|
|
188625
|
-
if (!options.
|
|
189860
|
+
function unsupportedBunxResult(context, title, path, command, options = {}) {
|
|
189861
|
+
if (!context.write || options.migration && !context.force || context.customCommand || command.command !== "bunx") {
|
|
188626
189862
|
return;
|
|
188627
|
-
if (!client) {
|
|
188628
|
-
throw new Error("--skill-only requires setup client codex or claude-code.");
|
|
188629
189863
|
}
|
|
188630
|
-
|
|
188631
|
-
|
|
189864
|
+
const probe = ensureBunxProbe(context);
|
|
189865
|
+
if (probe.status === "verified")
|
|
189866
|
+
return;
|
|
189867
|
+
const fallback = formatBunxFallbackCommand(context, title, options);
|
|
189868
|
+
return {
|
|
189869
|
+
kind: "mcp",
|
|
189870
|
+
registration: "blocked",
|
|
189871
|
+
title,
|
|
189872
|
+
status: "skipped",
|
|
189873
|
+
path,
|
|
189874
|
+
detail: `bunx was not verified for explicit package selection (${probe.detail}). No MCP config was written. Use ${fallback}, or install Bun 1.3.14 or newer and retry --runner bunx.`
|
|
189875
|
+
};
|
|
189876
|
+
}
|
|
189877
|
+
function verifyCurrentBunxRegistration(context, title, existing) {
|
|
189878
|
+
if (!context.write || !context.runnerExplicit || context.customCommand || context.mcpCommand.command !== "bunx" || existing.invocation.kind !== "current" || existing.invocation.runner !== "bunx") {
|
|
189879
|
+
return;
|
|
188632
189880
|
}
|
|
188633
|
-
|
|
188634
|
-
|
|
189881
|
+
const probe = ensureBunxProbe(context);
|
|
189882
|
+
if (probe.status !== "verified") {
|
|
189883
|
+
return {
|
|
189884
|
+
kind: "mcp",
|
|
189885
|
+
registration: "blocked",
|
|
189886
|
+
title,
|
|
189887
|
+
status: "skipped",
|
|
189888
|
+
path: existing.path,
|
|
189889
|
+
detail: `existing current bunx registration was kept unchanged. bunx could not be verified for explicit package selection (${probe.detail}). Install Bun 1.3.14 or newer before treating this registration as ready.`
|
|
189890
|
+
};
|
|
188635
189891
|
}
|
|
188636
|
-
|
|
188637
|
-
|
|
189892
|
+
const detail = title === "Codex MCP" ? codexPreservedDetail(existing) : claudePreservedDetail(existing);
|
|
189893
|
+
return preservedMcpResult(title, existing, `${detail} bunx ${probe.version} was verified for explicit package selection; no MCP config bytes changed.`);
|
|
189894
|
+
}
|
|
189895
|
+
function ensureBunxProbe(context) {
|
|
189896
|
+
return context.bunxProbe ?? (context.bunxProbe = context.bunxVersionProbe({
|
|
189897
|
+
cwd: context.cwd,
|
|
189898
|
+
env: context.env
|
|
189899
|
+
}));
|
|
189900
|
+
}
|
|
189901
|
+
function requiresExplicitBunxRunnerForMigration(context, command) {
|
|
189902
|
+
return command.command === "bunx" && !context.runnerExplicit;
|
|
189903
|
+
}
|
|
189904
|
+
function formatBunxFallbackCommand(context, title, options) {
|
|
189905
|
+
const cliArgs = [
|
|
189906
|
+
"setup",
|
|
189907
|
+
title === "Codex MCP" ? "codex" : "claude-code",
|
|
189908
|
+
"--write",
|
|
189909
|
+
"--runner",
|
|
189910
|
+
"npx"
|
|
189911
|
+
];
|
|
189912
|
+
if (context.scope === "global")
|
|
189913
|
+
cliArgs.push("--global");
|
|
189914
|
+
if (context.withOpenRouter)
|
|
189915
|
+
cliArgs.push("--with-openrouter");
|
|
189916
|
+
if (options.migration)
|
|
189917
|
+
cliArgs.push("--force");
|
|
189918
|
+
return formatKyosoPackageCommand({ runner: "npx", cliArgs });
|
|
189919
|
+
}
|
|
189920
|
+
function bunxVerificationPendingDetail(context, command) {
|
|
189921
|
+
if (context.customCommand || command.command !== "bunx")
|
|
189922
|
+
return "";
|
|
189923
|
+
if (!context.runnerExplicit) {
|
|
189924
|
+
return `
|
|
189925
|
+
Legacy Bun registration will stay unchanged unless you rerun with --write --runner bunx --force to verify and migrate it, or with --runner npx --force to migrate it using npx.`;
|
|
188638
189926
|
}
|
|
189927
|
+
return `
|
|
189928
|
+
Bun 1.3.14 or newer will be verified before any MCP config is written.`;
|
|
188639
189929
|
}
|
|
188640
|
-
function
|
|
188641
|
-
|
|
188642
|
-
return "npx";
|
|
188643
|
-
if (runner === "bunx")
|
|
188644
|
-
return "bunx";
|
|
188645
|
-
throw new Error(`Invalid --runner value "${runner}". Expected npx or bunx.`);
|
|
189930
|
+
function formatMigrationPreviewValue(value) {
|
|
189931
|
+
return JSON.stringify(sanitizeTextForDisplay(value));
|
|
188646
189932
|
}
|
|
188647
|
-
function
|
|
188648
|
-
|
|
188649
|
-
const [command, ...args] = parts;
|
|
188650
|
-
if (!command)
|
|
188651
|
-
throw new Error("--command must not be empty");
|
|
188652
|
-
return { command, args };
|
|
189933
|
+
function formatMigrationPreviewArgs(args) {
|
|
189934
|
+
return `[${args.map(formatMigrationPreviewValue).join(", ")}]`;
|
|
188653
189935
|
}
|
|
188654
|
-
function
|
|
188655
|
-
const
|
|
188656
|
-
|
|
188657
|
-
|
|
188658
|
-
|
|
188659
|
-
|
|
188660
|
-
|
|
188661
|
-
|
|
188662
|
-
|
|
188663
|
-
|
|
188664
|
-
|
|
188665
|
-
|
|
189936
|
+
function probeBunxVersion(options) {
|
|
189937
|
+
const probeDirectory = mkdtempSync(join6(tmpdir2(), "kyoso-bunx-probe-"));
|
|
189938
|
+
try {
|
|
189939
|
+
const result = spawnSync2("bunx", ["--version"], {
|
|
189940
|
+
cwd: probeDirectory,
|
|
189941
|
+
env: sanitizedBunxProbeEnv(options.env, probeDirectory),
|
|
189942
|
+
encoding: "utf8",
|
|
189943
|
+
timeout: 2000,
|
|
189944
|
+
shell: false
|
|
189945
|
+
});
|
|
189946
|
+
const errorCode2 = result.error && "code" in result.error ? result.error.code : undefined;
|
|
189947
|
+
if (errorCode2 === "ENOENT") {
|
|
189948
|
+
return { status: "missing", detail: "bunx was not found on PATH" };
|
|
188666
189949
|
}
|
|
188667
|
-
if (
|
|
188668
|
-
|
|
188669
|
-
continue;
|
|
189950
|
+
if (errorCode2 === "ETIMEDOUT" || result.signal) {
|
|
189951
|
+
return { status: "timeout", detail: "bunx --version timed out" };
|
|
188670
189952
|
}
|
|
188671
|
-
if (
|
|
188672
|
-
|
|
188673
|
-
|
|
188674
|
-
|
|
188675
|
-
}
|
|
188676
|
-
continue;
|
|
189953
|
+
if (result.status !== 0) {
|
|
189954
|
+
return {
|
|
189955
|
+
status: "failed",
|
|
189956
|
+
detail: `bunx --version exited ${result.status ?? "without a status"}`
|
|
189957
|
+
};
|
|
188677
189958
|
}
|
|
188678
|
-
|
|
189959
|
+
const version2 = result.stdout.trim();
|
|
189960
|
+
if (!/^\d+\.\d+\.\d+$/.test(version2)) {
|
|
189961
|
+
return {
|
|
189962
|
+
status: "invalid",
|
|
189963
|
+
detail: "bunx --version did not return a stable SemVer"
|
|
189964
|
+
};
|
|
189965
|
+
}
|
|
189966
|
+
if (!isCompleteSemVer(version2) || !isMinimumBunVersion(version2, "1.3.14")) {
|
|
189967
|
+
return {
|
|
189968
|
+
status: "unsupported",
|
|
189969
|
+
detail: `bunx ${version2} is older than the verified minimum 1.3.14`
|
|
189970
|
+
};
|
|
189971
|
+
}
|
|
189972
|
+
return { status: "verified", version: version2 };
|
|
189973
|
+
} finally {
|
|
189974
|
+
rmSync(probeDirectory, { force: true, recursive: true });
|
|
188679
189975
|
}
|
|
188680
|
-
if (quote)
|
|
188681
|
-
throw new Error("--command has an unterminated quote");
|
|
188682
|
-
if (current.length > 0)
|
|
188683
|
-
parts.push(current);
|
|
188684
|
-
return parts;
|
|
188685
189976
|
}
|
|
188686
|
-
function
|
|
188687
|
-
const
|
|
188688
|
-
|
|
188689
|
-
|
|
188690
|
-
|
|
188691
|
-
|
|
188692
|
-
|
|
188693
|
-
|
|
189977
|
+
function sanitizedBunxProbeEnv(env, temporaryDirectory) {
|
|
189978
|
+
const result = {
|
|
189979
|
+
PATH: env.PATH ?? "",
|
|
189980
|
+
HOME: env.HOME ?? "",
|
|
189981
|
+
TMPDIR: temporaryDirectory
|
|
189982
|
+
};
|
|
189983
|
+
for (const key of ["SystemRoot", "ComSpec", "PATHEXT", "WINDIR"]) {
|
|
189984
|
+
if (env[key])
|
|
189985
|
+
result[key] = env[key];
|
|
188694
189986
|
}
|
|
188695
|
-
|
|
189987
|
+
return result;
|
|
188696
189988
|
}
|
|
188697
|
-
|
|
188698
|
-
|
|
188699
|
-
|
|
188700
|
-
|
|
188701
|
-
|
|
188702
|
-
|
|
188703
|
-
|
|
189989
|
+
function isMinimumBunVersion(version2, minimum) {
|
|
189990
|
+
const actualParts = version2.split(".").map(Number);
|
|
189991
|
+
const minimumParts = minimum.split(".").map(Number);
|
|
189992
|
+
for (let index = 0;index < minimumParts.length; index += 1) {
|
|
189993
|
+
const actual = actualParts[index] ?? 0;
|
|
189994
|
+
const required2 = minimumParts[index] ?? 0;
|
|
189995
|
+
if (actual > required2)
|
|
189996
|
+
return true;
|
|
189997
|
+
if (actual < required2)
|
|
189998
|
+
return false;
|
|
188704
189999
|
}
|
|
188705
|
-
|
|
188706
|
-
async function readJsonObject(path) {
|
|
188707
|
-
const content = await readOptionalFile(path);
|
|
188708
|
-
if (content.trim().length === 0)
|
|
188709
|
-
return {};
|
|
188710
|
-
const parsed = JSON.parse(content);
|
|
188711
|
-
if (!isRecord9(parsed))
|
|
188712
|
-
throw new Error(`${path} must contain a JSON object`);
|
|
188713
|
-
return parsed;
|
|
190000
|
+
return true;
|
|
188714
190001
|
}
|
|
188715
190002
|
function hasCodexMcpContent(content) {
|
|
188716
190003
|
return /^\s*\[mcp_servers\.(?:"kyoso"|kyoso)]\s*$/m.test(content);
|
|
188717
190004
|
}
|
|
188718
|
-
function codexMcpStatusFromContent(content) {
|
|
188719
|
-
try {
|
|
188720
|
-
const parsed = parse5(content);
|
|
188721
|
-
if (!isRecord9(parsed))
|
|
188722
|
-
return "unknown";
|
|
188723
|
-
if (!isRecord9(parsed.mcp_servers))
|
|
188724
|
-
return "missing";
|
|
188725
|
-
if (!("kyoso" in parsed.mcp_servers))
|
|
188726
|
-
return "missing";
|
|
188727
|
-
return mcpEntryStatus(parsed.mcp_servers.kyoso);
|
|
188728
|
-
} catch {
|
|
188729
|
-
return "unknown";
|
|
188730
|
-
}
|
|
188731
|
-
}
|
|
188732
190005
|
function disabledCodexMcpDetail(configPath) {
|
|
188733
190006
|
return [
|
|
188734
190007
|
"existing [mcp_servers.kyoso] is disabled and was kept unchanged.",
|
|
@@ -188743,75 +190016,146 @@ function disabledClaudeMcpDetail(configPath) {
|
|
|
188743
190016
|
}
|
|
188744
190017
|
function detectCodexMcp(path, cwd, home) {
|
|
188745
190018
|
if (!existsSync2(path))
|
|
188746
|
-
return
|
|
190019
|
+
return missingMcpDetection();
|
|
188747
190020
|
try {
|
|
188748
190021
|
const parsed = parse5(readTextSync(path));
|
|
188749
190022
|
if (hasUnprobedProjectIntegrationOverride(parsed, cwd, home)) {
|
|
188750
|
-
return {
|
|
190023
|
+
return singleMcpDetection(manualMcpRegistration({
|
|
190024
|
+
path,
|
|
190025
|
+
scope: "codex-global",
|
|
190026
|
+
status: "unknown",
|
|
190027
|
+
value: undefined
|
|
190028
|
+
}));
|
|
190029
|
+
}
|
|
190030
|
+
if (!isRecord10(parsed)) {
|
|
190031
|
+
return singleMcpDetection(manualMcpRegistration({
|
|
190032
|
+
path,
|
|
190033
|
+
scope: "codex-global",
|
|
190034
|
+
status: "unknown",
|
|
190035
|
+
value: undefined
|
|
190036
|
+
}));
|
|
188751
190037
|
}
|
|
188752
|
-
if (!isRecord9(parsed))
|
|
188753
|
-
return { status: "unknown", paths: [path] };
|
|
188754
190038
|
if (!("mcp_servers" in parsed))
|
|
188755
|
-
return
|
|
188756
|
-
if (!
|
|
188757
|
-
return {
|
|
190039
|
+
return missingMcpDetection();
|
|
190040
|
+
if (!isRecord10(parsed.mcp_servers)) {
|
|
190041
|
+
return singleMcpDetection(manualMcpRegistration({
|
|
190042
|
+
path,
|
|
190043
|
+
scope: "codex-global",
|
|
190044
|
+
status: "unknown",
|
|
190045
|
+
value: undefined
|
|
190046
|
+
}));
|
|
188758
190047
|
}
|
|
188759
190048
|
if (!("kyoso" in parsed.mcp_servers)) {
|
|
188760
|
-
return
|
|
190049
|
+
return missingMcpDetection();
|
|
188761
190050
|
}
|
|
188762
|
-
return {
|
|
190051
|
+
return singleMcpDetection(manualMcpRegistration({
|
|
190052
|
+
path,
|
|
190053
|
+
scope: "codex-global",
|
|
190054
|
+
status: mcpEntryStatus(parsed.mcp_servers.kyoso),
|
|
190055
|
+
value: parsed.mcp_servers.kyoso
|
|
190056
|
+
}));
|
|
188763
190057
|
} catch {
|
|
188764
|
-
return {
|
|
190058
|
+
return singleMcpDetection(manualMcpRegistration({
|
|
190059
|
+
path,
|
|
190060
|
+
scope: "codex-global",
|
|
190061
|
+
status: "unknown",
|
|
190062
|
+
value: undefined
|
|
190063
|
+
}));
|
|
188765
190064
|
}
|
|
188766
190065
|
}
|
|
188767
190066
|
function detectClaudeMcp(path, cwd, home) {
|
|
188768
190067
|
if (!existsSync2(path))
|
|
188769
|
-
return
|
|
190068
|
+
return missingMcpDetection();
|
|
188770
190069
|
try {
|
|
188771
190070
|
const parsed = JSON.parse(readTextSync(path));
|
|
188772
|
-
const
|
|
188773
|
-
|
|
188774
|
-
|
|
188775
|
-
|
|
190071
|
+
const registrations = jsonMcpRegistrations(parsed, path, cwd, home);
|
|
190072
|
+
return registrations.length === 0 ? missingMcpDetection() : mergeMcpDetections([
|
|
190073
|
+
{
|
|
190074
|
+
status: mergeMcpStatuses(registrations.map((entry) => entry.status)),
|
|
190075
|
+
paths: [path],
|
|
190076
|
+
registrations
|
|
190077
|
+
}
|
|
190078
|
+
]);
|
|
188776
190079
|
} catch {
|
|
188777
|
-
return {
|
|
190080
|
+
return singleMcpDetection(manualMcpRegistration({
|
|
190081
|
+
path,
|
|
190082
|
+
scope: path.endsWith(".mcp.json") ? "claude-project" : "claude-global",
|
|
190083
|
+
status: "unknown",
|
|
190084
|
+
value: undefined
|
|
190085
|
+
}));
|
|
188778
190086
|
}
|
|
188779
190087
|
}
|
|
188780
|
-
function
|
|
188781
|
-
|
|
188782
|
-
|
|
188783
|
-
|
|
190088
|
+
function jsonMcpRegistrations(value, path, cwd, home) {
|
|
190089
|
+
const directScope = path.endsWith(".mcp.json") ? "claude-project" : "claude-global";
|
|
190090
|
+
if (!isRecord10(value)) {
|
|
190091
|
+
return [
|
|
190092
|
+
manualMcpRegistration({
|
|
190093
|
+
path,
|
|
190094
|
+
scope: directScope,
|
|
190095
|
+
status: "unknown",
|
|
190096
|
+
value: undefined
|
|
190097
|
+
})
|
|
190098
|
+
];
|
|
190099
|
+
}
|
|
190100
|
+
const registrations = directMcpRegistrations(value, path, directScope);
|
|
188784
190101
|
if (!("projects" in value))
|
|
188785
|
-
return
|
|
188786
|
-
if (!
|
|
188787
|
-
return [
|
|
190102
|
+
return registrations;
|
|
190103
|
+
if (!isRecord10(value.projects)) {
|
|
190104
|
+
return [
|
|
190105
|
+
...registrations,
|
|
190106
|
+
manualMcpRegistration({
|
|
190107
|
+
path,
|
|
190108
|
+
scope: "claude-global-project",
|
|
190109
|
+
status: "unknown",
|
|
190110
|
+
value: undefined
|
|
190111
|
+
})
|
|
190112
|
+
];
|
|
190113
|
+
}
|
|
188788
190114
|
const currentProject = normalizeProjectPath(cwd, home);
|
|
188789
190115
|
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
188790
190116
|
if (normalizeProjectPath(projectPath, home) !== currentProject)
|
|
188791
190117
|
continue;
|
|
188792
|
-
if (!
|
|
188793
|
-
|
|
190118
|
+
if (!isRecord10(projectConfig)) {
|
|
190119
|
+
registrations.push(manualMcpRegistration({
|
|
190120
|
+
path,
|
|
190121
|
+
scope: "claude-global-project",
|
|
190122
|
+
status: "unknown",
|
|
190123
|
+
value: undefined
|
|
190124
|
+
}));
|
|
188794
190125
|
continue;
|
|
188795
190126
|
}
|
|
188796
|
-
|
|
190127
|
+
registrations.push(...directMcpRegistrations(projectConfig, path, "claude-global-project"));
|
|
188797
190128
|
}
|
|
188798
|
-
return
|
|
190129
|
+
return registrations;
|
|
188799
190130
|
}
|
|
188800
|
-
function
|
|
188801
|
-
|
|
188802
|
-
|
|
188803
|
-
|
|
188804
|
-
|
|
188805
|
-
|
|
188806
|
-
|
|
188807
|
-
|
|
190131
|
+
function directMcpRegistrations(value, path, scope) {
|
|
190132
|
+
if (!("mcpServers" in value))
|
|
190133
|
+
return [];
|
|
190134
|
+
if (!isRecord10(value.mcpServers)) {
|
|
190135
|
+
return [
|
|
190136
|
+
manualMcpRegistration({
|
|
190137
|
+
path,
|
|
190138
|
+
scope,
|
|
190139
|
+
status: "unknown",
|
|
190140
|
+
value: undefined
|
|
190141
|
+
})
|
|
190142
|
+
];
|
|
188808
190143
|
}
|
|
188809
|
-
|
|
190144
|
+
if (!("kyoso" in value.mcpServers))
|
|
190145
|
+
return [];
|
|
190146
|
+
return [
|
|
190147
|
+
manualMcpRegistration({
|
|
190148
|
+
path,
|
|
190149
|
+
scope,
|
|
190150
|
+
status: mcpEntryStatus(value.mcpServers.kyoso),
|
|
190151
|
+
value: value.mcpServers.kyoso
|
|
190152
|
+
})
|
|
190153
|
+
];
|
|
188810
190154
|
}
|
|
188811
190155
|
function nestedMcpEntryStatus(value, path) {
|
|
188812
190156
|
let current = value;
|
|
188813
190157
|
for (const key of path) {
|
|
188814
|
-
if (!
|
|
190158
|
+
if (!isRecord10(current))
|
|
188815
190159
|
return "unknown";
|
|
188816
190160
|
if (!(key in current))
|
|
188817
190161
|
return "missing";
|
|
@@ -188820,11 +190164,11 @@ function nestedMcpEntryStatus(value, path) {
|
|
|
188820
190164
|
return mcpEntryStatus(current);
|
|
188821
190165
|
}
|
|
188822
190166
|
function hasUnprobedProjectIntegrationOverride(value, cwd, home) {
|
|
188823
|
-
if (!
|
|
190167
|
+
if (!isRecord10(value) || !isRecord10(value.projects))
|
|
188824
190168
|
return false;
|
|
188825
190169
|
const currentProject = normalizeProjectPath(cwd, home);
|
|
188826
190170
|
for (const [projectPath, projectConfig] of Object.entries(value.projects)) {
|
|
188827
|
-
if (normalizeProjectPath(projectPath, home) !== currentProject || !
|
|
190171
|
+
if (normalizeProjectPath(projectPath, home) !== currentProject || !isRecord10(projectConfig)) {
|
|
188828
190172
|
continue;
|
|
188829
190173
|
}
|
|
188830
190174
|
if ("mcp_servers" in projectConfig || "plugins" in projectConfig) {
|
|
@@ -188847,7 +190191,7 @@ function normalizeProjectPath(path, home) {
|
|
|
188847
190191
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
188848
190192
|
}
|
|
188849
190193
|
function mcpEntryStatus(value) {
|
|
188850
|
-
if (!
|
|
190194
|
+
if (!isRecord10(value))
|
|
188851
190195
|
return "unknown";
|
|
188852
190196
|
if (!("enabled" in value))
|
|
188853
190197
|
return "enabled";
|
|
@@ -188858,11 +190202,28 @@ function mcpEntryStatus(value) {
|
|
|
188858
190202
|
return "unknown";
|
|
188859
190203
|
}
|
|
188860
190204
|
function mergeMcpDetections(detections) {
|
|
190205
|
+
const registrations = detections.flatMap((detection) => detection.registrations);
|
|
190206
|
+
if (registrations.length === 0)
|
|
190207
|
+
return missingMcpDetection();
|
|
190208
|
+
return {
|
|
190209
|
+
status: registrations.length === 1 ? registrations[0]?.status ?? "unknown" : "unknown",
|
|
190210
|
+
paths: [...new Set(registrations.map((registration) => registration.path))],
|
|
190211
|
+
registrations
|
|
190212
|
+
};
|
|
190213
|
+
}
|
|
190214
|
+
function missingMcpDetection() {
|
|
190215
|
+
return { status: "missing", paths: [], registrations: [] };
|
|
190216
|
+
}
|
|
190217
|
+
function singleMcpDetection(registration) {
|
|
188861
190218
|
return {
|
|
188862
|
-
status:
|
|
188863
|
-
paths:
|
|
190219
|
+
status: registration.status,
|
|
190220
|
+
paths: [registration.path],
|
|
190221
|
+
registrations: [registration]
|
|
188864
190222
|
};
|
|
188865
190223
|
}
|
|
190224
|
+
function isCurrentManualMcp(detection) {
|
|
190225
|
+
return detection.status === "enabled" && detection.registrations.length === 1 && detection.registrations[0]?.invocation.kind === "current";
|
|
190226
|
+
}
|
|
188866
190227
|
function mergeMcpStatuses(statuses) {
|
|
188867
190228
|
if (statuses.includes("enabled"))
|
|
188868
190229
|
return "enabled";
|
|
@@ -188881,7 +190242,7 @@ function readTextSync(path) {
|
|
|
188881
190242
|
function recordValue(value) {
|
|
188882
190243
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
188883
190244
|
}
|
|
188884
|
-
function
|
|
190245
|
+
function isRecord10(value) {
|
|
188885
190246
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
188886
190247
|
}
|
|
188887
190248
|
function diffForAppend(path, snippet) {
|
|
@@ -189010,8 +190371,8 @@ async function runDoctor(options) {
|
|
|
189010
190371
|
});
|
|
189011
190372
|
const claudeIntegration = determineClientIntegration("claude-code", setup["claude-code"], cli);
|
|
189012
190373
|
lines.push("", "MCP", " stdio server: ok");
|
|
189013
|
-
lines.push(` Codex registration: ${formatManualMcpStatus(setup.codex
|
|
189014
|
-
lines.push(` Claude Code registration: ${formatManualMcpStatus(setup["claude-code"]
|
|
190374
|
+
lines.push(` Codex registration: ${formatManualMcpStatus(setup.codex, cli)}`);
|
|
190375
|
+
lines.push(` Claude Code registration: ${formatManualMcpStatus(setup["claude-code"], cli)}`);
|
|
189015
190376
|
lines.push("", "Skills");
|
|
189016
190377
|
lines.push(` Codex kyoso-review: ${setup.codex.skill ? "ok" : "missing"}`);
|
|
189017
190378
|
lines.push(` Claude Code kyoso-review: ${setup["claude-code"].skill ? "ok" : "missing"}`);
|
|
@@ -189199,6 +190560,7 @@ function isCodexProjectProviderAllowlistIssue(_issue, path) {
|
|
|
189199
190560
|
}
|
|
189200
190561
|
function determineCodexIntegration(options) {
|
|
189201
190562
|
const fallback = determineClientIntegration("codex", options.setup, options.cli);
|
|
190563
|
+
const hasReadyManualMcp = fallback.mode === "manual-mcp" || fallback.mode === "mcp-only";
|
|
189202
190564
|
const plugin = options.pluginInspector({
|
|
189203
190565
|
cwd: options.cwd,
|
|
189204
190566
|
env: options.env
|
|
@@ -189225,7 +190587,13 @@ function determineCodexIntegration(options) {
|
|
|
189225
190587
|
"Plugin MCP origin is unknown because the manual MCP configuration could not be classified."
|
|
189226
190588
|
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
189227
190589
|
}
|
|
189228
|
-
if (options.setup.manualMcpStatus === "enabled" &&
|
|
190590
|
+
if (options.setup.manualMcpStatus === "enabled" && !hasReadyManualMcp) {
|
|
190591
|
+
return withPluginDetails(withIntegrationWarnings(fallback, [
|
|
190592
|
+
...pluginWarnings,
|
|
190593
|
+
"Plugin MCP origin is unknown because an enabled manual MCP registration is legacy, custom, unverified, or its runner is unavailable."
|
|
190594
|
+
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
190595
|
+
}
|
|
190596
|
+
if (hasReadyManualMcp && override.status !== "disabled") {
|
|
189229
190597
|
return withPluginDetails(withIntegrationWarnings({
|
|
189230
190598
|
...fallback,
|
|
189231
190599
|
mode: "manual-mcp",
|
|
@@ -189242,7 +190610,7 @@ function determineCodexIntegration(options) {
|
|
|
189242
190610
|
"Plugin MCP override could not be safely classified from the Codex configuration."
|
|
189243
190611
|
], "unknown", "unknown"), "installed, enabled", "unknown");
|
|
189244
190612
|
}
|
|
189245
|
-
if (override.status === "disabled" &&
|
|
190613
|
+
if (override.status === "disabled" && hasReadyManualMcp) {
|
|
189246
190614
|
return withPluginDetails(withIntegrationWarnings({
|
|
189247
190615
|
...fallback,
|
|
189248
190616
|
mode: "manual-mcp",
|
|
@@ -189306,6 +190674,7 @@ function determineCodexIntegration(options) {
|
|
|
189306
190674
|
function determineClientIntegration(client, setup, cli) {
|
|
189307
190675
|
const integration = determineNonPluginIntegration({
|
|
189308
190676
|
manualMcpStatus: setup.manualMcpStatus,
|
|
190677
|
+
manualMcpRegistrations: setup.manualMcpRegistrations,
|
|
189309
190678
|
hasSkill: setup.skill,
|
|
189310
190679
|
cli
|
|
189311
190680
|
});
|
|
@@ -189341,7 +190710,7 @@ function pluginSkillWarnings(setup) {
|
|
|
189341
190710
|
}
|
|
189342
190711
|
function appendIntegration(lines, integration) {
|
|
189343
190712
|
lines.push(` ${integration.client} integration: ${integration.mode}`);
|
|
189344
|
-
lines.push(` manual MCP: ${formatManualMcpStatus(integration.setup.
|
|
190713
|
+
lines.push(` manual MCP: ${formatManualMcpStatus(integration.setup, integration.cli)}`);
|
|
189345
190714
|
if (integration.setup.mcpPaths.length > 0) {
|
|
189346
190715
|
lines.push(` manual MCP path(s): ${integration.setup.mcpPaths.join(", ")}`);
|
|
189347
190716
|
}
|
|
@@ -189350,8 +190719,8 @@ function appendIntegration(lines, integration) {
|
|
|
189350
190719
|
lines.push(` manual Skill path(s): ${integration.setup.skillPaths.join(", ")}`);
|
|
189351
190720
|
}
|
|
189352
190721
|
lines.push(` CLI: ${formatCliAvailability(integration.cli.kyoso)}`);
|
|
189353
|
-
lines.push(` npx: ${integration.cli.npx
|
|
189354
|
-
lines.push(` bunx: ${integration.cli.bunx
|
|
190722
|
+
lines.push(` npx: ${formatRunnerAvailability(integration.cli.npx)}`);
|
|
190723
|
+
lines.push(` bunx: ${formatRunnerAvailability(integration.cli.bunx)}`);
|
|
189355
190724
|
if (integration.plugin)
|
|
189356
190725
|
lines.push(` Plugin: ${integration.plugin}`);
|
|
189357
190726
|
if (integration.pluginMcp) {
|
|
@@ -189362,10 +190731,26 @@ function appendIntegration(lines, integration) {
|
|
|
189362
190731
|
}
|
|
189363
190732
|
lines.push(` ${integration.advice}`);
|
|
189364
190733
|
}
|
|
189365
|
-
function formatManualMcpStatus(
|
|
189366
|
-
if (
|
|
189367
|
-
return
|
|
189368
|
-
|
|
190734
|
+
function formatManualMcpStatus(setup, cli) {
|
|
190735
|
+
if (setup.manualMcpStatus !== "enabled")
|
|
190736
|
+
return setup.manualMcpStatus;
|
|
190737
|
+
if (setup.manualMcpRegistrations.length !== 1)
|
|
190738
|
+
return "unknown";
|
|
190739
|
+
const invocation = setup.manualMcpRegistrations[0]?.invocation;
|
|
190740
|
+
if (invocation?.kind === "current") {
|
|
190741
|
+
if (invocation.runner === "npx") {
|
|
190742
|
+
return cli.npx === "available" ? "ok" : "npx missing";
|
|
190743
|
+
}
|
|
190744
|
+
if (invocation.runner === "bunx") {
|
|
190745
|
+
return cli.bunx === "missing" ? "bunx missing" : "bunx unverified";
|
|
190746
|
+
}
|
|
190747
|
+
return "unknown";
|
|
190748
|
+
}
|
|
190749
|
+
if (invocation?.kind === "legacy")
|
|
190750
|
+
return "repair required (legacy)";
|
|
190751
|
+
if (invocation?.kind === "custom")
|
|
190752
|
+
return "custom/unverified";
|
|
190753
|
+
return "unknown";
|
|
189369
190754
|
}
|
|
189370
190755
|
function integrationAdvice(mode, client) {
|
|
189371
190756
|
if (mode === "plugin-mcp" || mode === "manual-mcp") {
|
|
@@ -189381,7 +190766,10 @@ function integrationAdvice(mode, client) {
|
|
|
189381
190766
|
return "status: runnable on demand; package-runner fallback may need network access.";
|
|
189382
190767
|
}
|
|
189383
190768
|
if (mode === "mcp-only") {
|
|
189384
|
-
return `next: run
|
|
190769
|
+
return `next: run \`${formatKyosoPackageCommand({
|
|
190770
|
+
runner: "npx",
|
|
190771
|
+
cliArgs: ["setup", client, "--write", "--skill-only"]
|
|
190772
|
+
})}\``;
|
|
189385
190773
|
}
|
|
189386
190774
|
if (mode === "cli-only") {
|
|
189387
190775
|
return `next: run \`kyoso setup ${client} --write --skill-only\``;
|
|
@@ -189516,8 +190904,8 @@ Do not use this skill for every coding task. It is intended for deliberate revie
|
|
|
189516
190904
|
`;
|
|
189517
190905
|
|
|
189518
190906
|
// src/cli/openRouterAcpSmoke.ts
|
|
189519
|
-
import { mkdir as mkdir6, mkdtemp as mkdtemp2, rm as
|
|
189520
|
-
import { tmpdir as
|
|
190907
|
+
import { mkdir as mkdir6, mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
|
|
190908
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
189521
190909
|
import { join as join8 } from "node:path";
|
|
189522
190910
|
|
|
189523
190911
|
// src/acp/AcpAgentProcess.ts
|
|
@@ -191915,14 +193303,14 @@ var zGuardCreateElicitationResponseCancel = object({
|
|
|
191915
193303
|
});
|
|
191916
193304
|
// node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
191917
193305
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
191918
|
-
function
|
|
193306
|
+
function isRecord11(value) {
|
|
191919
193307
|
return typeof value === "object" && value !== null;
|
|
191920
193308
|
}
|
|
191921
193309
|
function isJsonRpcId(value) {
|
|
191922
193310
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
191923
193311
|
}
|
|
191924
193312
|
function cancelRequestId(params) {
|
|
191925
|
-
if (!
|
|
193313
|
+
if (!isRecord11(params) || !isJsonRpcId(params["requestId"])) {
|
|
191926
193314
|
return;
|
|
191927
193315
|
}
|
|
191928
193316
|
return params["requestId"];
|
|
@@ -192269,7 +193657,7 @@ class Connection {
|
|
|
192269
193657
|
if (this.abortController.signal.aborted) {
|
|
192270
193658
|
return;
|
|
192271
193659
|
}
|
|
192272
|
-
if (!
|
|
193660
|
+
if (!isRecord11(message)) {
|
|
192273
193661
|
console.error("Invalid message", { message });
|
|
192274
193662
|
return;
|
|
192275
193663
|
}
|
|
@@ -192362,7 +193750,7 @@ class Connection {
|
|
|
192362
193750
|
pendingResponse.cleanup?.();
|
|
192363
193751
|
if ("result" in response) {
|
|
192364
193752
|
pendingResponse.resolve(response.result);
|
|
192365
|
-
} else if ("error" in response &&
|
|
193753
|
+
} else if ("error" in response && isRecord11(response.error)) {
|
|
192366
193754
|
const { code, message, data } = response.error;
|
|
192367
193755
|
pendingResponse.reject(new RequestError(code, message, data));
|
|
192368
193756
|
} else {
|
|
@@ -192572,7 +193960,7 @@ function ndJsonStream(output2, input2) {
|
|
|
192572
193960
|
if (trimmedLine) {
|
|
192573
193961
|
try {
|
|
192574
193962
|
const message = JSON.parse(trimmedLine);
|
|
192575
|
-
if (
|
|
193963
|
+
if (isRecord11(message)) {
|
|
192576
193964
|
controller.enqueue(message);
|
|
192577
193965
|
} else {
|
|
192578
193966
|
console.warn("Skipping JSON line that is not an object:", trimmedLine);
|
|
@@ -193981,7 +195369,7 @@ function isSeverity(value) {
|
|
|
193981
195369
|
return typeof value === "string" && severities.includes(value);
|
|
193982
195370
|
}
|
|
193983
195371
|
function normalizeCisaSecureByDesign(value) {
|
|
193984
|
-
if (!
|
|
195372
|
+
if (!isRecord12(value))
|
|
193985
195373
|
return;
|
|
193986
195374
|
const normalized = {};
|
|
193987
195375
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -194022,7 +195410,7 @@ function isEvidenceQuality(value) {
|
|
|
194022
195410
|
return typeof value === "string" && evidenceQualities.includes(value);
|
|
194023
195411
|
}
|
|
194024
195412
|
function isStrictAgentOpinion(value) {
|
|
194025
|
-
if (!
|
|
195413
|
+
if (!isRecord12(value) || !hasOnlyKeys(value, STRICT_ROOT_KEYS))
|
|
194026
195414
|
return false;
|
|
194027
195415
|
if (typeof value.summary !== "string")
|
|
194028
195416
|
return false;
|
|
@@ -194032,7 +195420,7 @@ function isStrictAgentOpinion(value) {
|
|
|
194032
195420
|
return value.cisaSecureByDesign === undefined || isStrictCisaSecureByDesign(value.cisaSecureByDesign);
|
|
194033
195421
|
}
|
|
194034
195422
|
function isStrictFinding(value) {
|
|
194035
|
-
if (!
|
|
195423
|
+
if (!isRecord12(value) || !hasOnlyKeys(value, STRICT_FINDING_KEYS)) {
|
|
194036
195424
|
return false;
|
|
194037
195425
|
}
|
|
194038
195426
|
if (!isSeverity(value.severity) || !isCategory(value.category) || !isNonEmptyString(value.title) || !isNonEmptyString(value.evidence) || !isNonEmptyString(value.recommendation) || !isConfidence(value.confidence)) {
|
|
@@ -194044,11 +195432,11 @@ function isStrictFinding(value) {
|
|
|
194044
195432
|
return true;
|
|
194045
195433
|
}
|
|
194046
195434
|
function isStrictFindingFiles(value) {
|
|
194047
|
-
return Array.isArray(value) && value.every((item) =>
|
|
195435
|
+
return Array.isArray(value) && value.every((item) => isRecord12(item) && hasOnlyKeys(item, STRICT_FILE_KEYS) && isNonEmptyString(item.path) && isOptionalLineNumber(item.lineStart) && isOptionalLineNumber(item.lineEnd));
|
|
194048
195436
|
}
|
|
194049
195437
|
function isStrictEvidenceRefs(value) {
|
|
194050
195438
|
return Array.isArray(value) && value.length <= MAX_EVIDENCE_REFS2 && value.every((item) => {
|
|
194051
|
-
if (!
|
|
195439
|
+
if (!isRecord12(item) || !hasOnlyKeys(item, STRICT_EVIDENCE_REF_KEYS)) {
|
|
194052
195440
|
return false;
|
|
194053
195441
|
}
|
|
194054
195442
|
if (item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
@@ -194064,7 +195452,7 @@ function isStrictEvidenceRefs(value) {
|
|
|
194064
195452
|
});
|
|
194065
195453
|
}
|
|
194066
195454
|
function isStrictCisaSecureByDesign(value) {
|
|
194067
|
-
if (!
|
|
195455
|
+
if (!isRecord12(value) || !hasOnlyKeys(value, STRICT_CISA_KEYS))
|
|
194068
195456
|
return false;
|
|
194069
195457
|
for (const key of [
|
|
194070
195458
|
"customerSecurityOutcomes",
|
|
@@ -194103,7 +195491,7 @@ function normalizeFindingFiles(value) {
|
|
|
194103
195491
|
if (!Array.isArray(value))
|
|
194104
195492
|
return;
|
|
194105
195493
|
const files = value.flatMap((item) => {
|
|
194106
|
-
if (!
|
|
195494
|
+
if (!isRecord12(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
194107
195495
|
return [];
|
|
194108
195496
|
}
|
|
194109
195497
|
const file2 = {
|
|
@@ -194123,7 +195511,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
194123
195511
|
if (!Array.isArray(value))
|
|
194124
195512
|
return;
|
|
194125
195513
|
const references = value.slice(0, MAX_EVIDENCE_REFS2).flatMap((item) => {
|
|
194126
|
-
if (!
|
|
195514
|
+
if (!isRecord12(item) || item.kind !== "file" && item.kind !== "diff_hunk" && item.kind !== "plan_clause") {
|
|
194127
195515
|
return [];
|
|
194128
195516
|
}
|
|
194129
195517
|
const reference = { kind: item.kind };
|
|
@@ -194146,7 +195534,7 @@ function normalizeEvidenceRefs2(value) {
|
|
|
194146
195534
|
function normalizeLineNumber(value) {
|
|
194147
195535
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= MAX_EVIDENCE_LINE2 ? value : undefined;
|
|
194148
195536
|
}
|
|
194149
|
-
function
|
|
195537
|
+
function isRecord12(value) {
|
|
194150
195538
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
194151
195539
|
}
|
|
194152
195540
|
|
|
@@ -194554,7 +195942,7 @@ function normalizeUsage3(usage) {
|
|
|
194554
195942
|
return normalizeModelTokenUsage(usage);
|
|
194555
195943
|
}
|
|
194556
195944
|
function withReportedExecutionIdentity(identity, metadata) {
|
|
194557
|
-
const record2 =
|
|
195945
|
+
const record2 = isRecord13(metadata) ? metadata : {};
|
|
194558
195946
|
return createModelExecutionIdentity({
|
|
194559
195947
|
providerRoute: identity.providerRoute,
|
|
194560
195948
|
requestedModel: identity.requestedModel,
|
|
@@ -194562,7 +195950,7 @@ function withReportedExecutionIdentity(identity, metadata) {
|
|
|
194562
195950
|
reportedModel: record2.model
|
|
194563
195951
|
});
|
|
194564
195952
|
}
|
|
194565
|
-
function
|
|
195953
|
+
function isRecord13(value) {
|
|
194566
195954
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
194567
195955
|
}
|
|
194568
195956
|
function resolveEffortConfigOption(agent, effort) {
|
|
@@ -194764,7 +196152,7 @@ async function runOpenRouterCodexAcpSmoke(options = {}) {
|
|
|
194764
196152
|
const env = options.env ?? process.env;
|
|
194765
196153
|
const { model } = validateOpenRouterCodexAcpSmoke(env);
|
|
194766
196154
|
const config2 = createOpenRouterCodexAcpSmokeConfig(model);
|
|
194767
|
-
const smokeRoot = await mkdtemp2(join8(
|
|
196155
|
+
const smokeRoot = await mkdtemp2(join8(tmpdir3(), "kyoso-openrouter-acp-smoke-"));
|
|
194768
196156
|
const workspaceDir = join8(smokeRoot, "workspace");
|
|
194769
196157
|
const homeDir = join8(smokeRoot, "home");
|
|
194770
196158
|
const codexHomeDir = join8(smokeRoot, "codex-home");
|
|
@@ -194808,7 +196196,7 @@ async function runOpenRouterCodexAcpSmoke(options = {}) {
|
|
|
194808
196196
|
}
|
|
194809
196197
|
return "OpenRouter Codex ACP smoke passed.";
|
|
194810
196198
|
} finally {
|
|
194811
|
-
await
|
|
196199
|
+
await rm3(smokeRoot, { recursive: true, force: true });
|
|
194812
196200
|
}
|
|
194813
196201
|
}
|
|
194814
196202
|
function assertUsableEnvironmentValue(env, key) {
|
|
@@ -208825,7 +210213,7 @@ function clearInheritedOpenRouterModelForProviderReset(baseConfig, overridden, o
|
|
|
208825
210213
|
return;
|
|
208826
210214
|
}
|
|
208827
210215
|
const codex = readPath2(overridden, ["agents", "codex"]);
|
|
208828
|
-
if (
|
|
210216
|
+
if (isRecord14(codex))
|
|
208829
210217
|
delete codex.model;
|
|
208830
210218
|
}
|
|
208831
210219
|
function findAssignmentForPath(overrides, path) {
|
|
@@ -208870,7 +210258,7 @@ function parseConfigOverrideValue(value, currentValue) {
|
|
|
208870
210258
|
function readPath2(target, path) {
|
|
208871
210259
|
let current = target;
|
|
208872
210260
|
for (const key of path) {
|
|
208873
|
-
if (!
|
|
210261
|
+
if (!isRecord14(current))
|
|
208874
210262
|
return;
|
|
208875
210263
|
current = current[key];
|
|
208876
210264
|
}
|
|
@@ -208880,7 +210268,7 @@ function writePath2(target, path, value) {
|
|
|
208880
210268
|
let current = target;
|
|
208881
210269
|
for (const key of path.slice(0, -1)) {
|
|
208882
210270
|
const child = current[key];
|
|
208883
|
-
if (!
|
|
210271
|
+
if (!isRecord14(child)) {
|
|
208884
210272
|
throw new Error(`Cannot apply --set key ${JSON.stringify(path.join("."))}.`);
|
|
208885
210273
|
}
|
|
208886
210274
|
current = child;
|
|
@@ -208889,7 +210277,7 @@ function writePath2(target, path, value) {
|
|
|
208889
210277
|
if (leaf)
|
|
208890
210278
|
current[leaf] = value;
|
|
208891
210279
|
}
|
|
208892
|
-
function
|
|
210280
|
+
function isRecord14(value) {
|
|
208893
210281
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
208894
210282
|
}
|
|
208895
210283
|
|
|
@@ -209594,7 +210982,7 @@ function normalizeTitle(value) {
|
|
|
209594
210982
|
|
|
209595
210983
|
// src/audit/safeTraceFile.ts
|
|
209596
210984
|
import { constants as constants2 } from "node:fs";
|
|
209597
|
-
import { lstat as
|
|
210985
|
+
import { lstat as lstat4, open as open2, realpath as realpath5, stat } from "node:fs/promises";
|
|
209598
210986
|
import { join as join9 } from "node:path";
|
|
209599
210987
|
var AUDIT_WARNING_UNSUPPORTED_OPEN_CAPABILITY = "AUDIT_DISABLED_UNSUPPORTED_CAPABILITY: Audit trace writing requires unavailable filesystem capabilities.";
|
|
209600
210988
|
async function openVerifiedTraceFile(options) {
|
|
@@ -209619,7 +211007,7 @@ async function openVerifiedTraceFile(options) {
|
|
|
209619
211007
|
throw new Error("trace path already exists");
|
|
209620
211008
|
}
|
|
209621
211009
|
await options.beforeOpen?.(tracePath);
|
|
209622
|
-
const handle = await
|
|
211010
|
+
const handle = await open2(tracePath, flags, 384);
|
|
209623
211011
|
try {
|
|
209624
211012
|
const [handleStat, pathStat, realTracePath] = await Promise.all([
|
|
209625
211013
|
handle.stat({ bigint: true }),
|
|
@@ -209657,7 +211045,7 @@ function isSafeTraceId(traceId) {
|
|
|
209657
211045
|
}
|
|
209658
211046
|
async function optionalLstat3(path) {
|
|
209659
211047
|
try {
|
|
209660
|
-
return await
|
|
211048
|
+
return await lstat4(path);
|
|
209661
211049
|
} catch (error51) {
|
|
209662
211050
|
if (typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT") {
|
|
209663
211051
|
return;
|
|
@@ -209971,7 +211359,7 @@ function validateReviewContract(request) {
|
|
|
209971
211359
|
const contract = request.reviewContract;
|
|
209972
211360
|
if (contract === undefined)
|
|
209973
211361
|
return;
|
|
209974
|
-
if (!
|
|
211362
|
+
if (!isRecord15(contract)) {
|
|
209975
211363
|
throw new KyosoRequestError("reviewContract must be an object", "VALIDATION_ERROR");
|
|
209976
211364
|
}
|
|
209977
211365
|
const allowedKeys = new Set(["focus", "nonGoals", "acceptedRisks"]);
|
|
@@ -209988,7 +211376,7 @@ function validateReviewContract(request) {
|
|
|
209988
211376
|
throw new KyosoRequestError("reviewContract.nonGoals must contain at most 20 non-empty strings of 500 characters or fewer", "VALIDATION_ERROR");
|
|
209989
211377
|
}
|
|
209990
211378
|
const acceptedRisks = contract.acceptedRisks;
|
|
209991
|
-
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !
|
|
211379
|
+
if (acceptedRisks !== undefined && (!Array.isArray(acceptedRisks) || acceptedRisks.length > 20 || acceptedRisks.some((risk) => !isRecord15(risk) || typeof risk.findingFingerprint !== "string" || !/^sha256:[0-9a-f]{64}$/.test(risk.findingFingerprint) || typeof risk.rationale !== "string" || risk.rationale.trim().length === 0 || risk.rationale.length > 500))) {
|
|
209992
211380
|
throw new KyosoRequestError("reviewContract.acceptedRisks must contain valid finding fingerprints and bounded rationales", "VALIDATION_ERROR");
|
|
209993
211381
|
}
|
|
209994
211382
|
}
|
|
@@ -210000,13 +211388,13 @@ function validateSelectedFiles(request) {
|
|
|
210000
211388
|
throw new KyosoRequestError("selectedFiles must be an array", "VALIDATION_ERROR");
|
|
210001
211389
|
}
|
|
210002
211390
|
for (const file2 of selectedFiles) {
|
|
210003
|
-
if (!
|
|
211391
|
+
if (!isRecord15(file2) || typeof file2.path !== "string" || file2.path.trim().length === 0 || typeof file2.content !== "string" || file2.language !== undefined && typeof file2.language !== "string" || file2.truncated !== undefined && typeof file2.truncated !== "boolean") {
|
|
210004
211392
|
throw new KyosoRequestError("selectedFiles entries require string path/content and valid optional metadata", "VALIDATION_ERROR");
|
|
210005
211393
|
}
|
|
210006
211394
|
normalizeRelativePath(file2.path);
|
|
210007
211395
|
}
|
|
210008
211396
|
}
|
|
210009
|
-
function
|
|
211397
|
+
function isRecord15(value) {
|
|
210010
211398
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
210011
211399
|
}
|
|
210012
211400
|
|
|
@@ -210431,11 +211819,11 @@ function decide(input2) {
|
|
|
210431
211819
|
}
|
|
210432
211820
|
|
|
210433
211821
|
// src/workspace/createSnapshot.ts
|
|
210434
|
-
import { chmod, mkdir as mkdir7, mkdtemp as mkdtemp3, writeFile as writeFile6 } from "node:fs/promises";
|
|
211822
|
+
import { chmod as chmod2, mkdir as mkdir7, mkdtemp as mkdtemp3, writeFile as writeFile6 } from "node:fs/promises";
|
|
210435
211823
|
import { dirname as dirname9, join as join10 } from "node:path";
|
|
210436
|
-
import { tmpdir as
|
|
211824
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
210437
211825
|
async function createSnapshot(traceId, tool, request, options = {}) {
|
|
210438
|
-
const root = await mkdtemp3(join10(
|
|
211826
|
+
const root = await mkdtemp3(join10(tmpdir4(), `kyoso-${traceId}-`));
|
|
210439
211827
|
const repoDir = join10(root, "repo");
|
|
210440
211828
|
const contextDir = join10(root, "context");
|
|
210441
211829
|
await mkdir7(repoDir, { recursive: true });
|
|
@@ -210450,7 +211838,7 @@ async function createSnapshot(traceId, tool, request, options = {}) {
|
|
|
210450
211838
|
const dest = join10(repoDir, relative4);
|
|
210451
211839
|
await mkdir7(dirname9(dest), { recursive: true });
|
|
210452
211840
|
await writeFile6(dest, file2.content, "utf8");
|
|
210453
|
-
await
|
|
211841
|
+
await chmod2(dest, 292).catch(() => {
|
|
210454
211842
|
return;
|
|
210455
211843
|
});
|
|
210456
211844
|
fileCount += 1;
|
|
@@ -210486,17 +211874,17 @@ function buildSelectedFilesManifest(request) {
|
|
|
210486
211874
|
}
|
|
210487
211875
|
|
|
210488
211876
|
// src/workspace/cleanup.ts
|
|
210489
|
-
import { rm as
|
|
211877
|
+
import { rm as rm4 } from "node:fs/promises";
|
|
210490
211878
|
async function cleanupSnapshot(path) {
|
|
210491
211879
|
if (process.env.KYOSO_KEEP_TEMP === "1")
|
|
210492
211880
|
return;
|
|
210493
|
-
await
|
|
211881
|
+
await rm4(path, { recursive: true, force: true });
|
|
210494
211882
|
}
|
|
210495
211883
|
|
|
210496
211884
|
// src/utils/ids.ts
|
|
210497
|
-
import { randomUUID as
|
|
211885
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
210498
211886
|
function newTraceId() {
|
|
210499
|
-
return `tr_${
|
|
211887
|
+
return `tr_${randomUUID3()}`;
|
|
210500
211888
|
}
|
|
210501
211889
|
|
|
210502
211890
|
// src/core/requestFingerprint.ts
|
|
@@ -210537,11 +211925,11 @@ function canonicalJson(value) {
|
|
|
210537
211925
|
function canonicalize(value) {
|
|
210538
211926
|
if (Array.isArray(value))
|
|
210539
211927
|
return value.map(canonicalize);
|
|
210540
|
-
if (!
|
|
211928
|
+
if (!isRecord16(value))
|
|
210541
211929
|
return value;
|
|
210542
211930
|
return Object.fromEntries(Object.entries(value).filter(([, child]) => child !== undefined).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, canonicalize(child)]));
|
|
210543
211931
|
}
|
|
210544
|
-
function
|
|
211932
|
+
function isRecord16(value) {
|
|
210545
211933
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
210546
211934
|
}
|
|
210547
211935
|
|
|
@@ -210555,7 +211943,7 @@ var REVIEW_BUDGET_KEYS = new Set([
|
|
|
210555
211943
|
]);
|
|
210556
211944
|
var MODEL_CALL_KINDS = ["primary", "verifier", "judge"];
|
|
210557
211945
|
function resolveReviewBudget(ceiling, requested) {
|
|
210558
|
-
if (requested !== undefined && !
|
|
211946
|
+
if (requested !== undefined && !isRecord17(requested)) {
|
|
210559
211947
|
throw new KyosoRequestError("options.reviewBudget must be an object.", "REVIEW_BUDGET_INVALID");
|
|
210560
211948
|
}
|
|
210561
211949
|
for (const [key, value] of Object.entries(requested ?? {})) {
|
|
@@ -210899,7 +212287,7 @@ function emptyReviewModelCallPlan() {
|
|
|
210899
212287
|
ceilingEffects: []
|
|
210900
212288
|
};
|
|
210901
212289
|
}
|
|
210902
|
-
function
|
|
212290
|
+
function isRecord17(value) {
|
|
210903
212291
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
210904
212292
|
}
|
|
210905
212293
|
|
|
@@ -210961,7 +212349,7 @@ function parseVerificationVerdicts(rawText) {
|
|
|
210961
212349
|
if (!Array.isArray(parsed.verdicts))
|
|
210962
212350
|
return;
|
|
210963
212351
|
return parsed.verdicts.flatMap((item) => {
|
|
210964
|
-
if (!
|
|
212352
|
+
if (!isRecord18(item))
|
|
210965
212353
|
return [];
|
|
210966
212354
|
if (typeof item.findingId !== "string")
|
|
210967
212355
|
return [];
|
|
@@ -211039,7 +212427,7 @@ function verificationNote(reasoning) {
|
|
|
211039
212427
|
function isVerdict(value) {
|
|
211040
212428
|
return value === "confirmed" || value === "refuted" || value === "uncertain";
|
|
211041
212429
|
}
|
|
211042
|
-
function
|
|
212430
|
+
function isRecord18(value) {
|
|
211043
212431
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
211044
212432
|
}
|
|
211045
212433
|
|