@kyo-so/cli 0.1.0 → 0.3.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/README.ja.md +297 -0
- package/README.md +98 -5
- package/README.zh-CN.md +297 -0
- package/dist/acp/prompts.d.ts +2 -2
- package/dist/bin/kyoso.js +645 -89
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/setup.d.ts +29 -0
- package/dist/config/schema.d.ts +2 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/types.d.ts +4 -1
- package/dist/index.js +71 -19
- package/dist/workspace/createSnapshot.d.ts +2 -1
- package/examples/claude-only.config.ts +9 -0
- package/examples/codex-only.config.ts +9 -0
- package/package.json +1 -1
package/dist/bin/kyoso.js
CHANGED
|
@@ -169495,18 +169495,21 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
169495
169495
|
});
|
|
169496
169496
|
|
|
169497
169497
|
// src/cli/main.ts
|
|
169498
|
-
import { spawnSync } from "node:child_process";
|
|
169498
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
169499
169499
|
|
|
169500
169500
|
// src/cli/args.ts
|
|
169501
169501
|
function parseArgs(argv) {
|
|
169502
169502
|
const [command = "help", ...rest] = argv;
|
|
169503
|
+
const positionals = [];
|
|
169503
169504
|
const flags = {};
|
|
169504
169505
|
for (let index = 0;index < rest.length; index += 1) {
|
|
169505
169506
|
const item = rest[index];
|
|
169506
169507
|
if (!item)
|
|
169507
169508
|
continue;
|
|
169508
|
-
if (!item.startsWith("--"))
|
|
169509
|
+
if (!item.startsWith("--")) {
|
|
169510
|
+
positionals.push(item);
|
|
169509
169511
|
continue;
|
|
169512
|
+
}
|
|
169510
169513
|
const key = item.slice(2);
|
|
169511
169514
|
const next = rest[index + 1];
|
|
169512
169515
|
const value = next && !next.startsWith("--") ? next : true;
|
|
@@ -169521,7 +169524,7 @@ function parseArgs(argv) {
|
|
|
169521
169524
|
flags[key] = [String(existing), String(value)];
|
|
169522
169525
|
}
|
|
169523
169526
|
}
|
|
169524
|
-
return { command, flags };
|
|
169527
|
+
return { command, positionals, flags };
|
|
169525
169528
|
}
|
|
169526
169529
|
function stringFlag(flags, key) {
|
|
169527
169530
|
const value = flags[key];
|
|
@@ -169597,7 +169600,7 @@ function languageFromPath(path) {
|
|
|
169597
169600
|
|
|
169598
169601
|
// src/cli/doctor.ts
|
|
169599
169602
|
import { accessSync } from "node:fs";
|
|
169600
|
-
import { delimiter } from "node:path";
|
|
169603
|
+
import { delimiter as delimiter2 } from "node:path";
|
|
169601
169604
|
|
|
169602
169605
|
// src/config/loadConfig.ts
|
|
169603
169606
|
import { access as access2, readFile as readFile3 } from "node:fs/promises";
|
|
@@ -184010,7 +184013,11 @@ var agentSchema = exports_external.object({
|
|
|
184010
184013
|
command: exports_external.string(),
|
|
184011
184014
|
args: exports_external.array(exports_external.string()).default([]),
|
|
184012
184015
|
model: exports_external.string().optional(),
|
|
184013
|
-
role: exports_external.enum([
|
|
184016
|
+
role: exports_external.enum([
|
|
184017
|
+
"implementation_reviewer",
|
|
184018
|
+
"architecture_security_reviewer",
|
|
184019
|
+
"combined_reviewer"
|
|
184020
|
+
]),
|
|
184014
184021
|
timeoutMs: exports_external.number().int().positive().default(120000),
|
|
184015
184022
|
env: exports_external.record(exports_external.string(), exports_external.string()).default({}),
|
|
184016
184023
|
auth: exports_external.object({
|
|
@@ -184290,7 +184297,7 @@ var REDACTION = "[KYOSO_REDACTED]";
|
|
|
184290
184297
|
var DEFAULT_AGENT_TIMEOUT_MS = 120000;
|
|
184291
184298
|
var RAW_OUTPUT_MAX_CHARS = 16384;
|
|
184292
184299
|
var KYOSO_CHILD_AGENT = "KYOSO_CHILD_AGENT";
|
|
184293
|
-
var KYOSO_VERSION = "0.
|
|
184300
|
+
var KYOSO_VERSION = "0.3.0";
|
|
184294
184301
|
|
|
184295
184302
|
// src/security/sanitizeText.ts
|
|
184296
184303
|
var SENSITIVE_TEXT_PATTERNS = [
|
|
@@ -184547,13 +184554,475 @@ function hasEnv(env, key) {
|
|
|
184547
184554
|
return typeof env[key] === "string" && env[key].trim().length > 0;
|
|
184548
184555
|
}
|
|
184549
184556
|
|
|
184557
|
+
// src/cli/setup.ts
|
|
184558
|
+
import { spawnSync } from "node:child_process";
|
|
184559
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
184560
|
+
import { cp, mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
184561
|
+
import { homedir as homedir2 } from "node:os";
|
|
184562
|
+
import { delimiter, dirname as dirname4, join as join2 } from "node:path";
|
|
184563
|
+
import { fileURLToPath } from "node:url";
|
|
184564
|
+
async function runSetup(options) {
|
|
184565
|
+
const client = parseClient(options.client);
|
|
184566
|
+
const runner = parseRunner(options.runner);
|
|
184567
|
+
const command = options.command ? parseCommandSpec(options.command) : commandForRunner(runner);
|
|
184568
|
+
const context = {
|
|
184569
|
+
cwd: options.cwd,
|
|
184570
|
+
home: options.env?.HOME ?? homedir2(),
|
|
184571
|
+
env: options.env ?? process.env,
|
|
184572
|
+
write: options.write,
|
|
184573
|
+
scope: options.global ? "global" : "project",
|
|
184574
|
+
mcpCommand: command,
|
|
184575
|
+
sourceSkillDir: resolveBundledSkillDir()
|
|
184576
|
+
};
|
|
184577
|
+
if (!client)
|
|
184578
|
+
return renderSetupOverview(context);
|
|
184579
|
+
if (client === "codex")
|
|
184580
|
+
return renderResults(await setupCodex(context));
|
|
184581
|
+
return renderResults(await setupClaudeCode(context));
|
|
184582
|
+
}
|
|
184583
|
+
function commandForRunner(runner) {
|
|
184584
|
+
if (runner === "bunx") {
|
|
184585
|
+
return { command: "bunx", args: ["@kyo-so/cli", "mcp"] };
|
|
184586
|
+
}
|
|
184587
|
+
return { command: "npx", args: ["-y", "@kyo-so/cli", "mcp"] };
|
|
184588
|
+
}
|
|
184589
|
+
function buildCodexMcpToml(command) {
|
|
184590
|
+
return [
|
|
184591
|
+
"[mcp_servers.kyoso]",
|
|
184592
|
+
`command = ${JSON.stringify(command.command)}`,
|
|
184593
|
+
`args = ${JSON.stringify(command.args)}`,
|
|
184594
|
+
'env_vars = ["OPENAI_API_KEY", "CODEX_API_KEY", "ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN"]',
|
|
184595
|
+
"startup_timeout_sec = 20",
|
|
184596
|
+
"tool_timeout_sec = 360",
|
|
184597
|
+
"enabled = true",
|
|
184598
|
+
""
|
|
184599
|
+
].join(`
|
|
184600
|
+
`);
|
|
184601
|
+
}
|
|
184602
|
+
function buildClaudeMcpEntry(command) {
|
|
184603
|
+
return {
|
|
184604
|
+
command: command.command,
|
|
184605
|
+
args: command.args,
|
|
184606
|
+
env: {
|
|
184607
|
+
OPENAI_API_KEY: "${OPENAI_API_KEY}",
|
|
184608
|
+
ANTHROPIC_API_KEY: "${ANTHROPIC_API_KEY}",
|
|
184609
|
+
CLAUDE_CODE_OAUTH_TOKEN: "${CLAUDE_CODE_OAUTH_TOKEN}"
|
|
184610
|
+
}
|
|
184611
|
+
};
|
|
184612
|
+
}
|
|
184613
|
+
function skillDestination(client, scope, cwd, home) {
|
|
184614
|
+
if (client === "codex") {
|
|
184615
|
+
const root2 = scope === "global" ? home : cwd;
|
|
184616
|
+
return join2(root2, ".agents", "skills", "kyoso-review");
|
|
184617
|
+
}
|
|
184618
|
+
const root = scope === "global" ? home : cwd;
|
|
184619
|
+
return join2(root, ".claude", "skills", "kyoso-review");
|
|
184620
|
+
}
|
|
184621
|
+
function detectSetup(options) {
|
|
184622
|
+
const home = options.home ?? homedir2();
|
|
184623
|
+
return {
|
|
184624
|
+
codex: {
|
|
184625
|
+
mcp: hasCodexMcp(join2(home, ".codex", "config.toml")),
|
|
184626
|
+
skill: existsSync(join2(options.cwd, ".agents", "skills", "kyoso-review", "SKILL.md")) || existsSync(join2(home, ".agents", "skills", "kyoso-review", "SKILL.md"))
|
|
184627
|
+
},
|
|
184628
|
+
"claude-code": {
|
|
184629
|
+
mcp: hasClaudeMcp(join2(options.cwd, ".mcp.json")) || hasClaudeMcp(join2(home, ".claude.json")),
|
|
184630
|
+
skill: existsSync(join2(options.cwd, ".claude", "skills", "kyoso-review", "SKILL.md")) || existsSync(join2(home, ".claude", "skills", "kyoso-review", "SKILL.md"))
|
|
184631
|
+
}
|
|
184632
|
+
};
|
|
184633
|
+
}
|
|
184634
|
+
async function setupCodex(context) {
|
|
184635
|
+
return [
|
|
184636
|
+
await ensureCodexMcp(context),
|
|
184637
|
+
await ensureSkill({
|
|
184638
|
+
title: "Codex skill",
|
|
184639
|
+
sourceDir: context.sourceSkillDir,
|
|
184640
|
+
destinationDir: skillDestination("codex", context.scope, context.cwd, context.home),
|
|
184641
|
+
write: context.write
|
|
184642
|
+
}),
|
|
184643
|
+
...singleAgentAdvice(context, "codex")
|
|
184644
|
+
];
|
|
184645
|
+
}
|
|
184646
|
+
async function setupClaudeCode(context) {
|
|
184647
|
+
return [
|
|
184648
|
+
await ensureClaudeMcp(context),
|
|
184649
|
+
await ensureSkill({
|
|
184650
|
+
title: "Claude Code skill",
|
|
184651
|
+
sourceDir: context.sourceSkillDir,
|
|
184652
|
+
destinationDir: skillDestination("claude-code", context.scope, context.cwd, context.home),
|
|
184653
|
+
write: context.write
|
|
184654
|
+
}),
|
|
184655
|
+
...singleAgentAdvice(context, "claude-code")
|
|
184656
|
+
];
|
|
184657
|
+
}
|
|
184658
|
+
async function ensureCodexMcp(context) {
|
|
184659
|
+
const configPath = join2(context.home, ".codex", "config.toml");
|
|
184660
|
+
const snippet = buildCodexMcpToml(context.mcpCommand);
|
|
184661
|
+
const current = await readOptionalFile(configPath);
|
|
184662
|
+
if (hasCodexMcpContent(current)) {
|
|
184663
|
+
return {
|
|
184664
|
+
title: "Codex MCP",
|
|
184665
|
+
status: "skipped",
|
|
184666
|
+
path: configPath,
|
|
184667
|
+
detail: "existing [mcp_servers.kyoso] kept"
|
|
184668
|
+
};
|
|
184669
|
+
}
|
|
184670
|
+
const detail = diffForAppend(configPath, snippet);
|
|
184671
|
+
if (!context.write) {
|
|
184672
|
+
return { title: "Codex MCP", status: "dry-run", path: configPath, detail };
|
|
184673
|
+
}
|
|
184674
|
+
const separator = current.length > 0 && !current.endsWith(`
|
|
184675
|
+
`) ? `
|
|
184676
|
+
|
|
184677
|
+
` : "";
|
|
184678
|
+
await mkdir3(dirname4(configPath), { recursive: true });
|
|
184679
|
+
await writeFile3(configPath, `${current}${separator}${snippet}`, "utf8");
|
|
184680
|
+
return {
|
|
184681
|
+
title: "Codex MCP",
|
|
184682
|
+
status: current.length > 0 ? "updated" : "created",
|
|
184683
|
+
path: configPath,
|
|
184684
|
+
detail
|
|
184685
|
+
};
|
|
184686
|
+
}
|
|
184687
|
+
async function ensureClaudeMcp(context) {
|
|
184688
|
+
if (context.scope === "global") {
|
|
184689
|
+
return ensureClaudeGlobalMcp(context);
|
|
184690
|
+
}
|
|
184691
|
+
const configPath = join2(context.cwd, ".mcp.json");
|
|
184692
|
+
const current = await readJsonObject(configPath);
|
|
184693
|
+
const mcpServers = recordValue(current.mcpServers);
|
|
184694
|
+
if (isRecord4(mcpServers.kyoso)) {
|
|
184695
|
+
return {
|
|
184696
|
+
title: "Claude Code MCP",
|
|
184697
|
+
status: "skipped",
|
|
184698
|
+
path: configPath,
|
|
184699
|
+
detail: "existing mcpServers.kyoso kept"
|
|
184700
|
+
};
|
|
184701
|
+
}
|
|
184702
|
+
const next = {
|
|
184703
|
+
...current,
|
|
184704
|
+
mcpServers: {
|
|
184705
|
+
...mcpServers,
|
|
184706
|
+
kyoso: buildClaudeMcpEntry(context.mcpCommand)
|
|
184707
|
+
}
|
|
184708
|
+
};
|
|
184709
|
+
const detail = diffForJson(configPath, current, next);
|
|
184710
|
+
if (!context.write) {
|
|
184711
|
+
return {
|
|
184712
|
+
title: "Claude Code MCP",
|
|
184713
|
+
status: "dry-run",
|
|
184714
|
+
path: configPath,
|
|
184715
|
+
detail
|
|
184716
|
+
};
|
|
184717
|
+
}
|
|
184718
|
+
await writeFile3(configPath, `${JSON.stringify(next, null, 2)}
|
|
184719
|
+
`, "utf8");
|
|
184720
|
+
return {
|
|
184721
|
+
title: "Claude Code MCP",
|
|
184722
|
+
status: Object.keys(current).length > 0 ? "updated" : "created",
|
|
184723
|
+
path: configPath,
|
|
184724
|
+
detail
|
|
184725
|
+
};
|
|
184726
|
+
}
|
|
184727
|
+
function ensureClaudeGlobalMcp(context) {
|
|
184728
|
+
const configPath = join2(context.home, ".claude.json");
|
|
184729
|
+
if (hasClaudeMcp(configPath)) {
|
|
184730
|
+
return {
|
|
184731
|
+
title: "Claude Code MCP",
|
|
184732
|
+
status: "skipped",
|
|
184733
|
+
path: configPath,
|
|
184734
|
+
detail: "existing mcpServers.kyoso kept"
|
|
184735
|
+
};
|
|
184736
|
+
}
|
|
184737
|
+
const json2 = JSON.stringify(buildClaudeMcpEntry(context.mcpCommand));
|
|
184738
|
+
const args = ["mcp", "add-json", "kyoso", json2, "--scope", "user"];
|
|
184739
|
+
const commandLine = ["claude", ...args.map(shellQuote)].join(" ");
|
|
184740
|
+
if (!context.write) {
|
|
184741
|
+
return {
|
|
184742
|
+
title: "Claude Code MCP",
|
|
184743
|
+
status: "dry-run",
|
|
184744
|
+
path: configPath,
|
|
184745
|
+
detail: commandLine
|
|
184746
|
+
};
|
|
184747
|
+
}
|
|
184748
|
+
const result = spawnSync("claude", args, { encoding: "utf8" });
|
|
184749
|
+
if (result.status !== 0) {
|
|
184750
|
+
throw new Error(result.stderr || result.stdout || "claude mcp add-json failed");
|
|
184751
|
+
}
|
|
184752
|
+
return {
|
|
184753
|
+
title: "Claude Code MCP",
|
|
184754
|
+
status: "updated",
|
|
184755
|
+
path: configPath,
|
|
184756
|
+
detail: commandLine
|
|
184757
|
+
};
|
|
184758
|
+
}
|
|
184759
|
+
async function ensureSkill(options) {
|
|
184760
|
+
const destinationSkill = join2(options.destinationDir, "SKILL.md");
|
|
184761
|
+
if (existsSync(destinationSkill)) {
|
|
184762
|
+
return {
|
|
184763
|
+
title: options.title,
|
|
184764
|
+
status: "skipped",
|
|
184765
|
+
path: options.destinationDir,
|
|
184766
|
+
detail: "existing kyoso-review skill kept"
|
|
184767
|
+
};
|
|
184768
|
+
}
|
|
184769
|
+
const detail = [
|
|
184770
|
+
`copy ${options.sourceDir}`,
|
|
184771
|
+
`to ${options.destinationDir}`
|
|
184772
|
+
].join(`
|
|
184773
|
+
`);
|
|
184774
|
+
if (!options.write) {
|
|
184775
|
+
return {
|
|
184776
|
+
title: options.title,
|
|
184777
|
+
status: "dry-run",
|
|
184778
|
+
path: options.destinationDir,
|
|
184779
|
+
detail
|
|
184780
|
+
};
|
|
184781
|
+
}
|
|
184782
|
+
await mkdir3(dirname4(options.destinationDir), { recursive: true });
|
|
184783
|
+
await cp(options.sourceDir, options.destinationDir, {
|
|
184784
|
+
recursive: true,
|
|
184785
|
+
force: false
|
|
184786
|
+
});
|
|
184787
|
+
return {
|
|
184788
|
+
title: options.title,
|
|
184789
|
+
status: "created",
|
|
184790
|
+
path: options.destinationDir,
|
|
184791
|
+
detail
|
|
184792
|
+
};
|
|
184793
|
+
}
|
|
184794
|
+
function renderSetupOverview(context) {
|
|
184795
|
+
const detected = detectSetup({ cwd: context.cwd, home: context.home });
|
|
184796
|
+
return [
|
|
184797
|
+
"Kyoso setup",
|
|
184798
|
+
"",
|
|
184799
|
+
"Clients",
|
|
184800
|
+
` codex: MCP ${statusWord(detected.codex.mcp)}, skill ${statusWord(detected.codex.skill)}`,
|
|
184801
|
+
` claude-code: MCP ${statusWord(detected["claude-code"].mcp)}, skill ${statusWord(detected["claude-code"].skill)}`,
|
|
184802
|
+
"",
|
|
184803
|
+
"Commands",
|
|
184804
|
+
" kyoso setup codex [--write] [--runner npx|bunx] [--global]",
|
|
184805
|
+
" kyoso setup claude-code [--write] [--runner npx|bunx] [--global]",
|
|
184806
|
+
"",
|
|
184807
|
+
`Default MCP command: ${context.mcpCommand.command} ${context.mcpCommand.args.join(" ")}`,
|
|
184808
|
+
"Dry-run is the default. Add --write to modify files."
|
|
184809
|
+
].join(`
|
|
184810
|
+
`);
|
|
184811
|
+
}
|
|
184812
|
+
function renderResults(results) {
|
|
184813
|
+
return [
|
|
184814
|
+
"Kyoso setup",
|
|
184815
|
+
"",
|
|
184816
|
+
...results.flatMap((result) => [
|
|
184817
|
+
`${result.title}: ${result.status}${result.path ? ` (${result.path})` : ""}`,
|
|
184818
|
+
...result.detail ? indent(result.detail).split(`
|
|
184819
|
+
`) : []
|
|
184820
|
+
])
|
|
184821
|
+
].join(`
|
|
184822
|
+
`);
|
|
184823
|
+
}
|
|
184824
|
+
function parseClient(client) {
|
|
184825
|
+
if (client === undefined)
|
|
184826
|
+
return;
|
|
184827
|
+
if (client === "codex" || client === "claude-code")
|
|
184828
|
+
return client;
|
|
184829
|
+
throw new Error(`Invalid setup client "${client}". Expected codex or claude-code.`);
|
|
184830
|
+
}
|
|
184831
|
+
function parseRunner(runner) {
|
|
184832
|
+
if (runner === undefined || runner === "npx")
|
|
184833
|
+
return "npx";
|
|
184834
|
+
if (runner === "bunx")
|
|
184835
|
+
return "bunx";
|
|
184836
|
+
throw new Error(`Invalid --runner value "${runner}". Expected npx or bunx.`);
|
|
184837
|
+
}
|
|
184838
|
+
function parseCommandSpec(value) {
|
|
184839
|
+
const parts = splitCommand(value);
|
|
184840
|
+
const [command, ...args] = parts;
|
|
184841
|
+
if (!command)
|
|
184842
|
+
throw new Error("--command must not be empty");
|
|
184843
|
+
return { command, args };
|
|
184844
|
+
}
|
|
184845
|
+
function splitCommand(value) {
|
|
184846
|
+
const parts = [];
|
|
184847
|
+
let current = "";
|
|
184848
|
+
let quote;
|
|
184849
|
+
for (const char of value.trim()) {
|
|
184850
|
+
if (quote) {
|
|
184851
|
+
if (char === quote) {
|
|
184852
|
+
quote = undefined;
|
|
184853
|
+
} else {
|
|
184854
|
+
current += char;
|
|
184855
|
+
}
|
|
184856
|
+
continue;
|
|
184857
|
+
}
|
|
184858
|
+
if (char === '"' || char === "'") {
|
|
184859
|
+
quote = char;
|
|
184860
|
+
continue;
|
|
184861
|
+
}
|
|
184862
|
+
if (/\s/.test(char)) {
|
|
184863
|
+
if (current.length > 0) {
|
|
184864
|
+
parts.push(current);
|
|
184865
|
+
current = "";
|
|
184866
|
+
}
|
|
184867
|
+
continue;
|
|
184868
|
+
}
|
|
184869
|
+
current += char;
|
|
184870
|
+
}
|
|
184871
|
+
if (quote)
|
|
184872
|
+
throw new Error("--command has an unterminated quote");
|
|
184873
|
+
if (current.length > 0)
|
|
184874
|
+
parts.push(current);
|
|
184875
|
+
return parts;
|
|
184876
|
+
}
|
|
184877
|
+
function resolveBundledSkillDir() {
|
|
184878
|
+
const start = dirname4(fileURLToPath(import.meta.url));
|
|
184879
|
+
let current = start;
|
|
184880
|
+
for (let depth = 0;depth < 5; depth += 1) {
|
|
184881
|
+
const candidate = join2(current, ".agents", "skills", "kyoso-review");
|
|
184882
|
+
if (existsSync(join2(candidate, "SKILL.md")))
|
|
184883
|
+
return candidate;
|
|
184884
|
+
current = dirname4(current);
|
|
184885
|
+
}
|
|
184886
|
+
throw new Error("Bundled kyoso-review skill was not found in this package.");
|
|
184887
|
+
}
|
|
184888
|
+
async function readOptionalFile(path) {
|
|
184889
|
+
try {
|
|
184890
|
+
return await readFile4(path, "utf8");
|
|
184891
|
+
} catch (error51) {
|
|
184892
|
+
if (isMissingPathError2(error51))
|
|
184893
|
+
return "";
|
|
184894
|
+
throw error51;
|
|
184895
|
+
}
|
|
184896
|
+
}
|
|
184897
|
+
async function readJsonObject(path) {
|
|
184898
|
+
const content = await readOptionalFile(path);
|
|
184899
|
+
if (content.trim().length === 0)
|
|
184900
|
+
return {};
|
|
184901
|
+
const parsed = JSON.parse(content);
|
|
184902
|
+
if (!isRecord4(parsed))
|
|
184903
|
+
throw new Error(`${path} must contain a JSON object`);
|
|
184904
|
+
return parsed;
|
|
184905
|
+
}
|
|
184906
|
+
function hasCodexMcp(path) {
|
|
184907
|
+
return existsSync(path) && hasCodexMcpContent(readTextSync(path));
|
|
184908
|
+
}
|
|
184909
|
+
function hasCodexMcpContent(content) {
|
|
184910
|
+
return /^\s*\[mcp_servers\.(?:"kyoso"|kyoso)]\s*$/m.test(content);
|
|
184911
|
+
}
|
|
184912
|
+
function hasClaudeMcp(path) {
|
|
184913
|
+
if (!existsSync(path))
|
|
184914
|
+
return false;
|
|
184915
|
+
try {
|
|
184916
|
+
const parsed = JSON.parse(readTextSync(path));
|
|
184917
|
+
return jsonHasKyosoMcp(parsed);
|
|
184918
|
+
} catch {
|
|
184919
|
+
return false;
|
|
184920
|
+
}
|
|
184921
|
+
}
|
|
184922
|
+
function jsonHasKyosoMcp(value) {
|
|
184923
|
+
if (!isRecord4(value))
|
|
184924
|
+
return false;
|
|
184925
|
+
if (isRecord4(value.mcpServers) && isRecord4(value.mcpServers.kyoso)) {
|
|
184926
|
+
return true;
|
|
184927
|
+
}
|
|
184928
|
+
return Object.values(value).some((child) => jsonHasKyosoMcp(child));
|
|
184929
|
+
}
|
|
184930
|
+
function readTextSync(path) {
|
|
184931
|
+
return readFileSync(path, "utf8");
|
|
184932
|
+
}
|
|
184933
|
+
function recordValue(value) {
|
|
184934
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
184935
|
+
}
|
|
184936
|
+
function isRecord4(value) {
|
|
184937
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
184938
|
+
}
|
|
184939
|
+
function diffForAppend(path, snippet) {
|
|
184940
|
+
return [
|
|
184941
|
+
`--- ${path}`,
|
|
184942
|
+
`+++ ${path}`,
|
|
184943
|
+
"@@",
|
|
184944
|
+
...snippet.split(`
|
|
184945
|
+
`).map((line) => `+${line}`)
|
|
184946
|
+
].join(`
|
|
184947
|
+
`);
|
|
184948
|
+
}
|
|
184949
|
+
function diffForJson(path, before, after) {
|
|
184950
|
+
const beforeText = JSON.stringify(before, null, 2).split(`
|
|
184951
|
+
`);
|
|
184952
|
+
const afterText = JSON.stringify(after, null, 2).split(`
|
|
184953
|
+
`);
|
|
184954
|
+
return [
|
|
184955
|
+
`--- ${path}`,
|
|
184956
|
+
`+++ ${path}`,
|
|
184957
|
+
"@@",
|
|
184958
|
+
...beforeText.map((line) => `-${line}`),
|
|
184959
|
+
...afterText.map((line) => `+${line}`)
|
|
184960
|
+
].join(`
|
|
184961
|
+
`);
|
|
184962
|
+
}
|
|
184963
|
+
function statusWord(value) {
|
|
184964
|
+
return value ? "ok" : "missing";
|
|
184965
|
+
}
|
|
184966
|
+
function singleAgentAdvice(context, client) {
|
|
184967
|
+
if (client === "claude-code" && !commandExists("codex", context.env)) {
|
|
184968
|
+
return [
|
|
184969
|
+
{
|
|
184970
|
+
title: "Single-agent config",
|
|
184971
|
+
status: "skipped",
|
|
184972
|
+
detail: [
|
|
184973
|
+
"codex was not found on PATH. To use Claude only, add:",
|
|
184974
|
+
"agents: {",
|
|
184975
|
+
" codex: { enabled: false },",
|
|
184976
|
+
"}",
|
|
184977
|
+
"Claude will run as combined_reviewer and cross-model verification will be marked unavailable."
|
|
184978
|
+
].join(`
|
|
184979
|
+
`)
|
|
184980
|
+
}
|
|
184981
|
+
];
|
|
184982
|
+
}
|
|
184983
|
+
if (client === "codex" && !commandExists("claude", context.env)) {
|
|
184984
|
+
return [
|
|
184985
|
+
{
|
|
184986
|
+
title: "Single-agent config",
|
|
184987
|
+
status: "skipped",
|
|
184988
|
+
detail: [
|
|
184989
|
+
"claude was not found on PATH. To use Codex only, add:",
|
|
184990
|
+
"agents: {",
|
|
184991
|
+
" claude: { enabled: false },",
|
|
184992
|
+
"}",
|
|
184993
|
+
"Codex will run as combined_reviewer and cross-model verification will be marked unavailable."
|
|
184994
|
+
].join(`
|
|
184995
|
+
`)
|
|
184996
|
+
}
|
|
184997
|
+
];
|
|
184998
|
+
}
|
|
184999
|
+
return [];
|
|
185000
|
+
}
|
|
185001
|
+
function indent(value) {
|
|
185002
|
+
return value.split(`
|
|
185003
|
+
`).map((line) => ` ${line}`).join(`
|
|
185004
|
+
`);
|
|
185005
|
+
}
|
|
185006
|
+
function shellQuote(value) {
|
|
185007
|
+
if (/^[A-Za-z0-9_./:=@{}$,-]+$/.test(value))
|
|
185008
|
+
return value;
|
|
185009
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
185010
|
+
}
|
|
185011
|
+
function commandExists(command, env) {
|
|
185012
|
+
const paths = env.PATH?.split(delimiter) ?? [];
|
|
185013
|
+
return paths.some((path) => existsSync(join2(path, command)));
|
|
185014
|
+
}
|
|
185015
|
+
function isMissingPathError2(error51) {
|
|
185016
|
+
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
185017
|
+
}
|
|
185018
|
+
|
|
184550
185019
|
// src/cli/doctor.ts
|
|
184551
185020
|
async function runDoctor(options) {
|
|
184552
185021
|
const env = options.env ?? process.env;
|
|
184553
185022
|
const loaded = await loadConfig(options);
|
|
184554
185023
|
const lines = ["Kyoso doctor", "", "Runtime"];
|
|
184555
|
-
lines.push(` Bun: ${
|
|
184556
|
-
lines.push(` Node/npm: ${
|
|
185024
|
+
lines.push(` Bun: ${commandExists2("bun", env) ? "ok" : "warning not found"}`);
|
|
185025
|
+
lines.push(` Node/npm: ${commandExists2("npm", env) ? "ok" : "warning npm not found"}`);
|
|
184557
185026
|
lines.push("", "Config");
|
|
184558
185027
|
lines.push(` kyoso.config.ts: ${loaded.configPath ? `found ${loaded.configPath}` : "not found; using defaults"}`);
|
|
184559
185028
|
lines.push(` trusted config: ${formatTrustStatus(loaded.configTrustStatus)}`);
|
|
@@ -184561,14 +185030,36 @@ async function runDoctor(options) {
|
|
|
184561
185030
|
lines.push(` config hash: ${loaded.configHash}`);
|
|
184562
185031
|
for (const warning of loaded.warnings)
|
|
184563
185032
|
lines.push(` warning: ${warning}`);
|
|
185033
|
+
const setup = detectSetup({ cwd: options.cwd, home: env.HOME });
|
|
184564
185034
|
lines.push("", "MCP", " stdio server: ok");
|
|
185035
|
+
lines.push(` Codex registration: ${setup.codex.mcp ? "ok" : "missing"}`);
|
|
185036
|
+
lines.push(` Claude Code registration: ${setup["claude-code"].mcp ? "ok" : "missing"}`);
|
|
185037
|
+
if (!setup.codex.mcp) {
|
|
185038
|
+
lines.push(" next: run `npx @kyo-so/cli setup codex --write`");
|
|
185039
|
+
}
|
|
185040
|
+
if (!setup["claude-code"].mcp) {
|
|
185041
|
+
lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
|
|
185042
|
+
}
|
|
185043
|
+
lines.push("", "Skills");
|
|
185044
|
+
lines.push(` Codex kyoso-review: ${setup.codex.skill ? "ok" : "missing"}`);
|
|
185045
|
+
lines.push(` Claude Code kyoso-review: ${setup["claude-code"].skill ? "ok" : "missing"}`);
|
|
185046
|
+
if (!setup.codex.skill) {
|
|
185047
|
+
lines.push(" next: run `npx @kyo-so/cli setup codex --write`");
|
|
185048
|
+
}
|
|
185049
|
+
if (!setup["claude-code"].skill) {
|
|
185050
|
+
lines.push(" next: run `npx @kyo-so/cli setup claude-code --write`");
|
|
185051
|
+
}
|
|
184565
185052
|
lines.push("", "ACP agents");
|
|
185053
|
+
const agentCommandExists = {
|
|
185054
|
+
codex: commandExists2(loaded.config.agents.codex.command, env),
|
|
185055
|
+
claude: commandExists2(loaded.config.agents.claude.command, env)
|
|
185056
|
+
};
|
|
184566
185057
|
for (const agent of ["codex", "claude"]) {
|
|
184567
185058
|
const config2 = loaded.config.agents[agent];
|
|
184568
|
-
const exists3 =
|
|
185059
|
+
const exists3 = agentCommandExists[agent];
|
|
184569
185060
|
lines.push(` ${agent === "codex" ? "Codex" : "Claude"}: ${exists3 ? "ok" : "warning command not found"}`);
|
|
184570
185061
|
lines.push(` command: ${[config2.command, ...config2.args].join(" ")}`);
|
|
184571
|
-
if (!exists3 && config2.command === "npx" &&
|
|
185062
|
+
if (!exists3 && config2.command === "npx" && commandExists2("bunx", env)) {
|
|
184572
185063
|
lines.push(' hint: replace command "npx" with "bunx" in kyoso.config.ts');
|
|
184573
185064
|
}
|
|
184574
185065
|
if (agent === "claude") {
|
|
@@ -184588,6 +185079,11 @@ async function runDoctor(options) {
|
|
|
184588
185079
|
lines.push(" auth: detected or delegated");
|
|
184589
185080
|
}
|
|
184590
185081
|
}
|
|
185082
|
+
if (agentCommandExists.codex !== agentCommandExists.claude) {
|
|
185083
|
+
const missing = agentCommandExists.codex ? "claude" : "codex";
|
|
185084
|
+
const remaining = agentCommandExists.codex ? "codex" : "claude";
|
|
185085
|
+
lines.push(` single-agent mode: set agents.${missing}.enabled: false to use ${remaining} only; the remaining agent will cover both review roles.`);
|
|
185086
|
+
}
|
|
184591
185087
|
const judgeProvider = loaded.config.judge.mode === "deterministic_only" ? "deterministic_fallback" : resolveJudgeProvider(loaded.config.judge.provider, env);
|
|
184592
185088
|
lines.push("", "Judge");
|
|
184593
185089
|
lines.push(` provider: ${judgeProvider}`);
|
|
@@ -184622,8 +185118,8 @@ function formatClaudeDualAuthWarning(preferApiKey) {
|
|
|
184622
185118
|
function hasEnv2(env, key) {
|
|
184623
185119
|
return typeof env[key] === "string" && env[key].trim().length > 0;
|
|
184624
185120
|
}
|
|
184625
|
-
function
|
|
184626
|
-
const paths = env.PATH?.split(
|
|
185121
|
+
function commandExists2(command, env) {
|
|
185122
|
+
const paths = env.PATH?.split(delimiter2) ?? [];
|
|
184627
185123
|
return paths.some((path) => {
|
|
184628
185124
|
try {
|
|
184629
185125
|
accessSync(`${path}/${command}`);
|
|
@@ -184635,12 +185131,12 @@ function commandExists(command, env) {
|
|
|
184635
185131
|
}
|
|
184636
185132
|
|
|
184637
185133
|
// src/cli/init.ts
|
|
184638
|
-
import { readFile as
|
|
184639
|
-
import { join as
|
|
185134
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
|
|
185135
|
+
import { join as join3 } from "node:path";
|
|
184640
185136
|
async function runInit(options) {
|
|
184641
|
-
const configPath =
|
|
184642
|
-
const skillPath =
|
|
184643
|
-
const gitignorePath =
|
|
185137
|
+
const configPath = join3(options.cwd, "kyoso.config.ts");
|
|
185138
|
+
const skillPath = join3(options.cwd, ".agents/skills/kyoso-review/SKILL.md");
|
|
185139
|
+
const gitignorePath = join3(options.cwd, ".gitignore");
|
|
184644
185140
|
const configResult = await writeFileWithOverwritePrompt(configPath, CONFIG_TEMPLATE, options.force);
|
|
184645
185141
|
const skillResult = await writeFileWithOverwritePrompt(skillPath, SKILL_TEMPLATE, options.force);
|
|
184646
185142
|
const gitignoreResult = await ensureGitignoreEntry(gitignorePath, ".kyoso/");
|
|
@@ -184654,11 +185150,11 @@ async function runInit(options) {
|
|
|
184654
185150
|
async function ensureGitignoreEntry(path, entry) {
|
|
184655
185151
|
let content = "";
|
|
184656
185152
|
try {
|
|
184657
|
-
content = await
|
|
185153
|
+
content = await readFile5(path, "utf8");
|
|
184658
185154
|
} catch (error51) {
|
|
184659
|
-
if (!
|
|
185155
|
+
if (!isMissingPathError3(error51))
|
|
184660
185156
|
throw error51;
|
|
184661
|
-
await
|
|
185157
|
+
await writeFile4(path, `${entry}
|
|
184662
185158
|
`, "utf8");
|
|
184663
185159
|
return "created";
|
|
184664
185160
|
}
|
|
@@ -184668,11 +185164,11 @@ async function ensureGitignoreEntry(path, entry) {
|
|
|
184668
185164
|
const separator = content.length > 0 && !content.endsWith(`
|
|
184669
185165
|
`) ? `
|
|
184670
185166
|
` : "";
|
|
184671
|
-
await
|
|
185167
|
+
await writeFile4(path, `${content}${separator}${entry}
|
|
184672
185168
|
`, "utf8");
|
|
184673
185169
|
return "updated";
|
|
184674
185170
|
}
|
|
184675
|
-
function
|
|
185171
|
+
function isMissingPathError3(error51) {
|
|
184676
185172
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
184677
185173
|
}
|
|
184678
185174
|
var CONFIG_TEMPLATE = `import { defineConfig } from "@kyo-so/cli";
|
|
@@ -186479,7 +186975,7 @@ var Protocol = class {
|
|
|
186479
186975
|
const { relatedRequestId, resumptionToken, onresumptiontoken } = options ?? {};
|
|
186480
186976
|
let onAbort;
|
|
186481
186977
|
let cleanupMessageId;
|
|
186482
|
-
return new Promise((
|
|
186978
|
+
return new Promise((resolve4, reject) => {
|
|
186483
186979
|
const earlyReject = (error51) => {
|
|
186484
186980
|
reject(error51);
|
|
186485
186981
|
};
|
|
@@ -186539,7 +187035,7 @@ var Protocol = class {
|
|
|
186539
187035
|
return reject(response);
|
|
186540
187036
|
validateStandardSchema(resultSchema, response.result).then((parseResult) => {
|
|
186541
187037
|
if (parseResult.success)
|
|
186542
|
-
|
|
187038
|
+
resolve4(parseResult.data);
|
|
186543
187039
|
else
|
|
186544
187040
|
reject(new SdkError(SdkErrorCode.InvalidResult, `Invalid result for ${request.method}: ${parseResult.error}`));
|
|
186545
187041
|
}, reject);
|
|
@@ -189617,7 +190113,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
189617
190113
|
const schOrFunc = root.refs[ref];
|
|
189618
190114
|
if (schOrFunc)
|
|
189619
190115
|
return schOrFunc;
|
|
189620
|
-
let _sch =
|
|
190116
|
+
let _sch = resolve4.call(this, root, ref);
|
|
189621
190117
|
if (_sch === undefined) {
|
|
189622
190118
|
const schema = (_a3 = root.localRefs) === null || _a3 === undefined ? undefined : _a3[ref];
|
|
189623
190119
|
const { schemaId } = this.opts;
|
|
@@ -189648,7 +190144,7 @@ var require_compile = /* @__PURE__ */ __commonJSMin((exports) => {
|
|
|
189648
190144
|
function sameSchemaEnv(s1, s2) {
|
|
189649
190145
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
189650
190146
|
}
|
|
189651
|
-
function
|
|
190147
|
+
function resolve4(root, ref) {
|
|
189652
190148
|
let sch;
|
|
189653
190149
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
189654
190150
|
ref = sch;
|
|
@@ -190154,7 +190650,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190154
190650
|
uri = parse5(serialize(uri, options), options);
|
|
190155
190651
|
return uri;
|
|
190156
190652
|
}
|
|
190157
|
-
function
|
|
190653
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
190158
190654
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
190159
190655
|
const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
190160
190656
|
schemelessOptions.skipEscape = true;
|
|
@@ -190365,7 +190861,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin((exports, module) => {
|
|
|
190365
190861
|
const fastUri = {
|
|
190366
190862
|
SCHEMES,
|
|
190367
190863
|
normalize,
|
|
190368
|
-
resolve:
|
|
190864
|
+
resolve: resolve4,
|
|
190369
190865
|
resolveComponent,
|
|
190370
190866
|
equal,
|
|
190371
190867
|
serialize,
|
|
@@ -194087,7 +194583,7 @@ var StdioServerTransport = class {
|
|
|
194087
194583
|
send(message) {
|
|
194088
194584
|
if (this._closed)
|
|
194089
194585
|
return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
194090
|
-
return new Promise((
|
|
194586
|
+
return new Promise((resolve4, reject) => {
|
|
194091
194587
|
const json2 = serializeMessage(message);
|
|
194092
194588
|
let settled = false;
|
|
194093
194589
|
const onError = (error51) => {
|
|
@@ -194104,7 +194600,7 @@ var StdioServerTransport = class {
|
|
|
194104
194600
|
settled = true;
|
|
194105
194601
|
this._stdout.off("error", onError);
|
|
194106
194602
|
this._stdout.off("drain", onDrain);
|
|
194107
|
-
|
|
194603
|
+
resolve4();
|
|
194108
194604
|
};
|
|
194109
194605
|
this._stdout.once("error", onError);
|
|
194110
194606
|
if (this._stdout.write(json2)) {
|
|
@@ -194112,7 +194608,7 @@ var StdioServerTransport = class {
|
|
|
194112
194608
|
return;
|
|
194113
194609
|
settled = true;
|
|
194114
194610
|
this._stdout.off("error", onError);
|
|
194115
|
-
|
|
194611
|
+
resolve4();
|
|
194116
194612
|
} else if (!settled)
|
|
194117
194613
|
this._stdout.once("drain", onDrain);
|
|
194118
194614
|
});
|
|
@@ -194120,12 +194616,12 @@ var StdioServerTransport = class {
|
|
|
194120
194616
|
};
|
|
194121
194617
|
|
|
194122
194618
|
// src/core/runReview.ts
|
|
194123
|
-
import { resolve as
|
|
194619
|
+
import { resolve as resolve5 } from "node:path";
|
|
194124
194620
|
|
|
194125
194621
|
// src/acp/AcpAgentProcess.ts
|
|
194126
194622
|
import { spawn } from "node:child_process";
|
|
194127
|
-
import { readFile as
|
|
194128
|
-
import { isAbsolute, relative, resolve as
|
|
194623
|
+
import { readFile as readFile6, realpath } from "node:fs/promises";
|
|
194624
|
+
import { isAbsolute, relative, resolve as resolve4 } from "node:path";
|
|
194129
194625
|
import { Readable, Writable } from "node:stream";
|
|
194130
194626
|
|
|
194131
194627
|
// node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
@@ -195999,14 +196495,14 @@ function ndJsonStream(output2, input2) {
|
|
|
195999
196495
|
}
|
|
196000
196496
|
// node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js
|
|
196001
196497
|
var CANCEL_REQUEST_METHOD = "$/cancel_request";
|
|
196002
|
-
function
|
|
196498
|
+
function isRecord5(value) {
|
|
196003
196499
|
return typeof value === "object" && value !== null;
|
|
196004
196500
|
}
|
|
196005
196501
|
function isJsonRpcId(value) {
|
|
196006
196502
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
196007
196503
|
}
|
|
196008
196504
|
function cancelRequestId(params) {
|
|
196009
|
-
if (!
|
|
196505
|
+
if (!isRecord5(params) || !isJsonRpcId(params["requestId"])) {
|
|
196010
196506
|
return;
|
|
196011
196507
|
}
|
|
196012
196508
|
return params["requestId"];
|
|
@@ -196230,12 +196726,12 @@ class Connection {
|
|
|
196230
196726
|
}
|
|
196231
196727
|
const id = this.nextRequestId++;
|
|
196232
196728
|
let cancel = () => {};
|
|
196233
|
-
const responsePromise = new Promise((
|
|
196729
|
+
const responsePromise = new Promise((resolve4, reject) => {
|
|
196234
196730
|
const pendingResponse = {
|
|
196235
196731
|
resolve: (response) => {
|
|
196236
196732
|
try {
|
|
196237
196733
|
const value = mapResponse ? mapResponse(response) : response;
|
|
196238
|
-
|
|
196734
|
+
resolve4(value);
|
|
196239
196735
|
} catch (error51) {
|
|
196240
196736
|
reject(error51);
|
|
196241
196737
|
}
|
|
@@ -196300,8 +196796,8 @@ class Connection {
|
|
|
196300
196796
|
initialize(stream, handlers) {
|
|
196301
196797
|
this.stream = stream;
|
|
196302
196798
|
this.staticHandlers = handlers;
|
|
196303
|
-
this.closedPromise = new Promise((
|
|
196304
|
-
this.abortController.signal.addEventListener("abort", () =>
|
|
196799
|
+
this.closedPromise = new Promise((resolve4) => {
|
|
196800
|
+
this.abortController.signal.addEventListener("abort", () => resolve4());
|
|
196305
196801
|
});
|
|
196306
196802
|
this.receive();
|
|
196307
196803
|
}
|
|
@@ -196871,8 +197367,8 @@ class AsyncQueue {
|
|
|
196871
197367
|
if (this.failed) {
|
|
196872
197368
|
return Promise.reject(this.failure);
|
|
196873
197369
|
}
|
|
196874
|
-
return new Promise((
|
|
196875
|
-
this.waiters.push({ resolve:
|
|
197370
|
+
return new Promise((resolve4, reject) => {
|
|
197371
|
+
this.waiters.push({ resolve: resolve4, reject });
|
|
196876
197372
|
});
|
|
196877
197373
|
}
|
|
196878
197374
|
}
|
|
@@ -197558,7 +198054,7 @@ function isSeverity(value) {
|
|
|
197558
198054
|
return typeof value === "string" && severities.includes(value);
|
|
197559
198055
|
}
|
|
197560
198056
|
function normalizeCisaSecureByDesign(value) {
|
|
197561
|
-
if (!
|
|
198057
|
+
if (!isRecord6(value))
|
|
197562
198058
|
return;
|
|
197563
198059
|
const normalized = {};
|
|
197564
198060
|
const customerSecurityOutcomes = normalizeGateStatus(value.customerSecurityOutcomes);
|
|
@@ -197599,7 +198095,7 @@ function normalizeFindingFiles(value) {
|
|
|
197599
198095
|
if (!Array.isArray(value))
|
|
197600
198096
|
return;
|
|
197601
198097
|
const files = value.flatMap((item) => {
|
|
197602
|
-
if (!
|
|
198098
|
+
if (!isRecord6(item) || typeof item.path !== "string" || item.path.trim().length === 0) {
|
|
197603
198099
|
return [];
|
|
197604
198100
|
}
|
|
197605
198101
|
const file2 = {
|
|
@@ -197618,7 +198114,7 @@ function normalizeFindingFiles(value) {
|
|
|
197618
198114
|
function normalizeLineNumber(value) {
|
|
197619
198115
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
|
197620
198116
|
}
|
|
197621
|
-
function
|
|
198117
|
+
function isRecord6(value) {
|
|
197622
198118
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
197623
198119
|
}
|
|
197624
198120
|
|
|
@@ -197806,7 +198302,7 @@ async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
|
|
|
197806
198302
|
const readablePath = await resolveReadableFile(workspaceRoot, absolute);
|
|
197807
198303
|
if (!readablePath)
|
|
197808
198304
|
continue;
|
|
197809
|
-
content = await
|
|
198305
|
+
content = await readFile6(readablePath, "utf8").catch(() => {
|
|
197810
198306
|
return;
|
|
197811
198307
|
});
|
|
197812
198308
|
if (content !== undefined)
|
|
@@ -197823,7 +198319,7 @@ async function readWorkspaceFile(workspaceDir, requestedPath, line, limit) {
|
|
|
197823
198319
|
}
|
|
197824
198320
|
async function resolveReadableFile(workspaceRoot, absolute) {
|
|
197825
198321
|
const realPath = await realpath(absolute).catch((error51) => {
|
|
197826
|
-
if (
|
|
198322
|
+
if (isMissingPathError4(error51))
|
|
197827
198323
|
return;
|
|
197828
198324
|
throw error51;
|
|
197829
198325
|
});
|
|
@@ -197833,13 +198329,13 @@ async function resolveReadableFile(workspaceRoot, absolute) {
|
|
|
197833
198329
|
return realPath;
|
|
197834
198330
|
}
|
|
197835
198331
|
function resolveReadablePaths(workspaceRoot, requestedPath) {
|
|
197836
|
-
const primary =
|
|
198332
|
+
const primary = resolve4(workspaceRoot, requestedPath);
|
|
197837
198333
|
assertWithinWorkspace(workspaceRoot, primary);
|
|
197838
198334
|
const relativePath = relative(workspaceRoot, primary).replaceAll("\\", "/");
|
|
197839
198335
|
if (relativePath.startsWith("context/") || relativePath.startsWith("repo/")) {
|
|
197840
198336
|
return [primary];
|
|
197841
198337
|
}
|
|
197842
|
-
const repoPath =
|
|
198338
|
+
const repoPath = resolve4(workspaceRoot, "repo", relativePath);
|
|
197843
198339
|
assertWithinWorkspace(workspaceRoot, repoPath);
|
|
197844
198340
|
return isAbsolute(requestedPath) ? [primary, repoPath] : [repoPath, primary];
|
|
197845
198341
|
}
|
|
@@ -197861,7 +198357,7 @@ function terminateChild(child) {
|
|
|
197861
198357
|
}, 2000);
|
|
197862
198358
|
killTimer.unref();
|
|
197863
198359
|
}
|
|
197864
|
-
function
|
|
198360
|
+
function isMissingPathError4(error51) {
|
|
197865
198361
|
return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
|
|
197866
198362
|
}
|
|
197867
198363
|
function buildAgentFailure(rawDetail, fallbackMessage) {
|
|
@@ -198010,7 +198506,7 @@ function buildOpinion(agent, role, tool) {
|
|
|
198010
198506
|
}
|
|
198011
198507
|
|
|
198012
198508
|
// src/acp/prompts.ts
|
|
198013
|
-
function buildAgentPrompt(tool, request, agent) {
|
|
198509
|
+
function buildAgentPrompt(tool, request, agent, role) {
|
|
198014
198510
|
const shared = [
|
|
198015
198511
|
"You are running as a Kyoso child reviewer.",
|
|
198016
198512
|
"Do not edit files.",
|
|
@@ -198023,15 +198519,26 @@ function buildAgentPrompt(tool, request, agent) {
|
|
|
198023
198519
|
"Use empty arrays when no finding, test, risk, or question exists; do not copy the example finding."
|
|
198024
198520
|
].join(`
|
|
198025
198521
|
`);
|
|
198026
|
-
const
|
|
198027
|
-
|
|
198028
|
-
|
|
198029
|
-
|
|
198030
|
-
`
|
|
198031
|
-
|
|
198032
|
-
|
|
198033
|
-
|
|
198034
|
-
|
|
198522
|
+
const roleInstructions = {
|
|
198523
|
+
implementation_reviewer: [
|
|
198524
|
+
"You are the implementation reviewer role in Kyoso.",
|
|
198525
|
+
"Focus on feasibility, minimal change, existing code consistency, regression risk, tests, migration risk, and maintainability."
|
|
198526
|
+
].join(`
|
|
198527
|
+
`),
|
|
198528
|
+
architecture_security_reviewer: [
|
|
198529
|
+
"You are the architecture and security reviewer role in Kyoso.",
|
|
198530
|
+
"Focus on architecture, threat modeling, authn/authz, secrets, privacy, secure defaults, CISA Secure by Design, and edge cases."
|
|
198531
|
+
].join(`
|
|
198532
|
+
`),
|
|
198533
|
+
combined_reviewer: [
|
|
198534
|
+
"You are the combined reviewer role in Kyoso.",
|
|
198535
|
+
"Cover both implementation review and architecture/security review in one pass.",
|
|
198536
|
+
"First assess feasibility, minimal change, existing code consistency, regression risk, tests, migration risk, and maintainability.",
|
|
198537
|
+
"Then assess architecture, threat modeling, authn/authz, secrets, privacy, secure defaults, CISA Secure by Design, and edge cases.",
|
|
198538
|
+
"Use finding category values so readers can distinguish implementation, architecture, and security concerns."
|
|
198539
|
+
].join(`
|
|
198540
|
+
`)
|
|
198541
|
+
};
|
|
198035
198542
|
const cisaInstruction = tool === "security_review" ? [
|
|
198036
198543
|
"For security_review, include cisaMapping on each security-relevant finding when applicable.",
|
|
198037
198544
|
"Also include cisaSecureByDesign with all four gate dimensions."
|
|
@@ -198039,7 +198546,9 @@ function buildAgentPrompt(tool, request, agent) {
|
|
|
198039
198546
|
`) : "For plan_review and diff_review, include cisaMapping and cisaSecureByDesign only when relevant.";
|
|
198040
198547
|
return `${shared}
|
|
198041
198548
|
|
|
198042
|
-
${
|
|
198549
|
+
Agent: ${agent}
|
|
198550
|
+
Role: ${role}
|
|
198551
|
+
${roleInstructions[role]}
|
|
198043
198552
|
|
|
198044
198553
|
Tool: ${tool}
|
|
198045
198554
|
${cisaInstruction}
|
|
@@ -198382,8 +198891,8 @@ function normalizeTitle(value) {
|
|
|
198382
198891
|
}
|
|
198383
198892
|
|
|
198384
198893
|
// src/audit/trace.ts
|
|
198385
|
-
import { mkdir as
|
|
198386
|
-
import { dirname as
|
|
198894
|
+
import { mkdir as mkdir4, appendFile } from "node:fs/promises";
|
|
198895
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join4 } from "node:path";
|
|
198387
198896
|
|
|
198388
198897
|
// src/context/pathPolicy.ts
|
|
198389
198898
|
import { normalize, sep } from "node:path";
|
|
@@ -198477,13 +198986,13 @@ function createTraceWriter(options) {
|
|
|
198477
198986
|
}
|
|
198478
198987
|
const date5 = new Date().toISOString().slice(0, 10);
|
|
198479
198988
|
const directory = validateAuditDirectory(options.directory, warnings);
|
|
198480
|
-
const tracePath =
|
|
198989
|
+
const tracePath = join4(options.cwd, directory, date5, `${options.traceId}.jsonl`);
|
|
198481
198990
|
return {
|
|
198482
198991
|
tracePath,
|
|
198483
198992
|
warnings,
|
|
198484
198993
|
async write(event) {
|
|
198485
198994
|
try {
|
|
198486
|
-
await
|
|
198995
|
+
await mkdir4(dirname5(tracePath), { recursive: true });
|
|
198487
198996
|
await appendFile(tracePath, `${JSON.stringify(sanitizeForAudit(event, {
|
|
198488
198997
|
includeRawAgentOutput: options.includeRawAgentOutput
|
|
198489
198998
|
}))}
|
|
@@ -198626,6 +199135,7 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
198626
199135
|
`**Decision:** ${result.decision}`,
|
|
198627
199136
|
`**Mode:** ${tool}`,
|
|
198628
199137
|
`**Agents:** ${result.agentOpinions.map((opinion) => `${title(opinion.agent)} ${opinion.status}`).join(", ")}`,
|
|
199138
|
+
`**Review mode:** ${formatReviewMode(result)}`,
|
|
198629
199139
|
`**Degraded:** ${String(result.degraded)}`,
|
|
198630
199140
|
"",
|
|
198631
199141
|
"## Summary",
|
|
@@ -198652,7 +199162,11 @@ function renderMarkdownResult(tool, result, options = {}) {
|
|
|
198652
199162
|
lines.push(`### ${title(opinion.agent)}`, "", `${opinion.summary} (${opinion.status})`, "");
|
|
198653
199163
|
}
|
|
198654
199164
|
lines.push("", "## Disagreements", "");
|
|
198655
|
-
|
|
199165
|
+
if (result.reviewMode === "single_agent") {
|
|
199166
|
+
lines.push("- N/A - single-agent review.");
|
|
199167
|
+
} else {
|
|
199168
|
+
lines.push(...result.disagreements.length > 0 ? result.disagreements.map((item) => `- ${item.topic}: ${item.judgeComment}`) : ["- None."]);
|
|
199169
|
+
}
|
|
198656
199170
|
lines.push("", "## Notes", "", "Kyoso did not modify files. Review was performed on a temporary snapshot.");
|
|
198657
199171
|
if (result.audit.networkMode === "unrestricted") {
|
|
198658
199172
|
lines.push("Network mode was unrestricted. File modification policy remained denied.");
|
|
@@ -198666,6 +199180,14 @@ function defaultSummaryText(result) {
|
|
|
198666
199180
|
function title(value) {
|
|
198667
199181
|
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
198668
199182
|
}
|
|
199183
|
+
function formatReviewMode(result) {
|
|
199184
|
+
if (result.reviewMode !== "single_agent")
|
|
199185
|
+
return "multi-agent";
|
|
199186
|
+
const opinion = result.agentOpinions[0];
|
|
199187
|
+
const agent = opinion ? title(opinion.agent) : "single agent";
|
|
199188
|
+
const role = opinion?.role === "combined_reviewer" ? "combined role" : "configured role";
|
|
199189
|
+
return `single-agent (${agent}, ${role}; cross-model verification was not performed)`;
|
|
199190
|
+
}
|
|
198669
199191
|
function notes(items) {
|
|
198670
199192
|
return (items[0] ?? "No notes.").replaceAll("|", "\\|");
|
|
198671
199193
|
}
|
|
@@ -198866,15 +199388,15 @@ function decide(input2) {
|
|
|
198866
199388
|
}
|
|
198867
199389
|
|
|
198868
199390
|
// src/workspace/createSnapshot.ts
|
|
198869
|
-
import { chmod, mkdir as
|
|
198870
|
-
import { dirname as
|
|
199391
|
+
import { chmod, mkdir as mkdir5, mkdtemp, writeFile as writeFile5 } from "node:fs/promises";
|
|
199392
|
+
import { dirname as dirname6, join as join5 } from "node:path";
|
|
198871
199393
|
import { tmpdir } from "node:os";
|
|
198872
199394
|
async function createSnapshot(traceId, tool, request, options = {}) {
|
|
198873
|
-
const root = await mkdtemp(
|
|
198874
|
-
const repoDir =
|
|
198875
|
-
const contextDir =
|
|
198876
|
-
await
|
|
198877
|
-
await
|
|
199395
|
+
const root = await mkdtemp(join5(tmpdir(), `kyoso-${traceId}-`));
|
|
199396
|
+
const repoDir = join5(root, "repo");
|
|
199397
|
+
const contextDir = join5(root, "context");
|
|
199398
|
+
await mkdir5(repoDir, { recursive: true });
|
|
199399
|
+
await mkdir5(contextDir, { recursive: true });
|
|
198878
199400
|
let fileCount = 0;
|
|
198879
199401
|
for (const file2 of request.selectedFiles ?? []) {
|
|
198880
199402
|
const relative2 = normalizeRelativePath(file2.path);
|
|
@@ -198882,24 +199404,24 @@ async function createSnapshot(traceId, tool, request, options = {}) {
|
|
|
198882
199404
|
continue;
|
|
198883
199405
|
if (!isAllowedPath(relative2, options.allowPatterns ?? []))
|
|
198884
199406
|
continue;
|
|
198885
|
-
const dest =
|
|
198886
|
-
await
|
|
198887
|
-
await
|
|
199407
|
+
const dest = join5(repoDir, relative2);
|
|
199408
|
+
await mkdir5(dirname6(dest), { recursive: true });
|
|
199409
|
+
await writeFile5(dest, file2.content, "utf8");
|
|
198888
199410
|
await chmod(dest, 292).catch(() => {
|
|
198889
199411
|
return;
|
|
198890
199412
|
});
|
|
198891
199413
|
fileCount += 1;
|
|
198892
199414
|
}
|
|
198893
|
-
await
|
|
198894
|
-
await
|
|
198895
|
-
await
|
|
198896
|
-
await
|
|
199415
|
+
await writeFile5(join5(contextDir, "request.json"), JSON.stringify(stripContents(request), null, 2), "utf8");
|
|
199416
|
+
await writeFile5(join5(contextDir, "selected_files_manifest.json"), JSON.stringify(buildSelectedFilesManifest(request), null, 2), "utf8");
|
|
199417
|
+
await writeFile5(join5(contextDir, "instructions.codex.md"), buildAgentPrompt(tool, request, "codex", options.agentRoles?.codex ?? "implementation_reviewer"), "utf8");
|
|
199418
|
+
await writeFile5(join5(contextDir, "instructions.claude.md"), buildAgentPrompt(tool, request, "claude", options.agentRoles?.claude ?? "architecture_security_reviewer"), "utf8");
|
|
198897
199419
|
if (request.repoSummary)
|
|
198898
|
-
await
|
|
199420
|
+
await writeFile5(join5(contextDir, "repo_summary.md"), request.repoSummary, "utf8");
|
|
198899
199421
|
if (request.currentPlan)
|
|
198900
|
-
await
|
|
199422
|
+
await writeFile5(join5(contextDir, "current_plan.md"), request.currentPlan, "utf8");
|
|
198901
199423
|
if (request.diff?.unifiedDiff)
|
|
198902
|
-
await
|
|
199424
|
+
await writeFile5(join5(contextDir, "diff.patch"), request.diff.unifiedDiff, "utf8");
|
|
198903
199425
|
return { root, repoDir, contextDir, fileCount };
|
|
198904
199426
|
}
|
|
198905
199427
|
function stripContents(request) {
|
|
@@ -199055,9 +199577,11 @@ async function runReview(tool, request, options = {}) {
|
|
|
199055
199577
|
allowPatterns
|
|
199056
199578
|
});
|
|
199057
199579
|
warnings.push(...built.warnings);
|
|
199580
|
+
const agentRoles = resolveAgentRoles(loaded.config);
|
|
199058
199581
|
snapshot = await createSnapshot(traceId, tool, built.request, {
|
|
199059
199582
|
denyPatterns,
|
|
199060
|
-
allowPatterns
|
|
199583
|
+
allowPatterns,
|
|
199584
|
+
agentRoles
|
|
199061
199585
|
});
|
|
199062
199586
|
await trace.write({
|
|
199063
199587
|
type: "snapshot_created",
|
|
@@ -199077,6 +199601,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
199077
199601
|
trace
|
|
199078
199602
|
});
|
|
199079
199603
|
const normalizedAgentResults = agentResults.map(normalizeAgentRunResult);
|
|
199604
|
+
const agentsUsed = normalizedAgentResults.map((result2) => result2.agent);
|
|
199605
|
+
const reviewMode = agentsUsed.length === 1 ? "single_agent" : "multi_agent";
|
|
199080
199606
|
const completed = normalizedAgentResults.filter((result2) => result2.status === "completed");
|
|
199081
199607
|
const degraded = completed.length !== agentResults.length;
|
|
199082
199608
|
let aggregate = aggregateAgentResults(normalizedAgentResults);
|
|
@@ -199128,6 +199654,8 @@ async function runReview(tool, request, options = {}) {
|
|
|
199128
199654
|
const resultWithoutMarkdown = {
|
|
199129
199655
|
decision,
|
|
199130
199656
|
degraded,
|
|
199657
|
+
agentsUsed,
|
|
199658
|
+
reviewMode,
|
|
199131
199659
|
findings: aggregate.findings,
|
|
199132
199660
|
cisaSecureByDesign: cisa,
|
|
199133
199661
|
disagreements: aggregate.disagreements,
|
|
@@ -199140,7 +199668,7 @@ async function runReview(tool, request, options = {}) {
|
|
|
199140
199668
|
traceId,
|
|
199141
199669
|
startedAt,
|
|
199142
199670
|
completedAt,
|
|
199143
|
-
agentsUsed
|
|
199671
|
+
agentsUsed,
|
|
199144
199672
|
redactionsApplied: secretScan.redactions,
|
|
199145
199673
|
networkMode,
|
|
199146
199674
|
workspaceMode: "temp_snapshot",
|
|
@@ -199199,12 +199727,13 @@ async function runReview(tool, request, options = {}) {
|
|
|
199199
199727
|
}
|
|
199200
199728
|
}
|
|
199201
199729
|
async function runAgents(input2) {
|
|
199730
|
+
const agentRoles = resolveAgentRoles(input2.config);
|
|
199202
199731
|
const agentInputs = ["codex", "claude"].filter((agent) => input2.config.agents[agent].enabled).map((agent) => ({
|
|
199203
199732
|
traceId: input2.traceId,
|
|
199204
199733
|
agent,
|
|
199205
|
-
role: input2.config.agents[agent].role,
|
|
199734
|
+
role: agentRoles[agent] ?? input2.config.agents[agent].role,
|
|
199206
199735
|
tool: input2.tool,
|
|
199207
|
-
prompt: buildAgentPrompt(input2.tool, input2.request, agent),
|
|
199736
|
+
prompt: buildAgentPrompt(input2.tool, input2.request, agent, agentRoles[agent] ?? input2.config.agents[agent].role),
|
|
199208
199737
|
workspaceDir: input2.workspaceDir,
|
|
199209
199738
|
timeoutMs: input2.request.options?.maxAgentTimeoutMs ?? input2.config.agents[agent].timeoutMs ?? DEFAULT_AGENT_TIMEOUT_MS,
|
|
199210
199739
|
networkMode: input2.networkMode
|
|
@@ -199213,6 +199742,7 @@ async function runAgents(input2) {
|
|
|
199213
199742
|
type: "agent_started",
|
|
199214
199743
|
traceId: input2.traceId,
|
|
199215
199744
|
agent: agentInput.agent,
|
|
199745
|
+
role: agentInput.role,
|
|
199216
199746
|
timestamp: new Date().toISOString()
|
|
199217
199747
|
})));
|
|
199218
199748
|
const results = await input2.manager.runAll(agentInputs);
|
|
@@ -199221,6 +199751,7 @@ async function runAgents(input2) {
|
|
|
199221
199751
|
type: "agent_completed",
|
|
199222
199752
|
traceId: input2.traceId,
|
|
199223
199753
|
agent: result.agent,
|
|
199754
|
+
role: result.role,
|
|
199224
199755
|
status: result.status,
|
|
199225
199756
|
startedAt: result.startedAt,
|
|
199226
199757
|
completedAt: result.completedAt,
|
|
@@ -199237,6 +199768,15 @@ async function runAgents(input2) {
|
|
|
199237
199768
|
}));
|
|
199238
199769
|
return results;
|
|
199239
199770
|
}
|
|
199771
|
+
function resolveAgentRoles(config2) {
|
|
199772
|
+
const enabledAgents = ["codex", "claude"].filter((agent) => config2.agents[agent].enabled);
|
|
199773
|
+
const singleAgentMode = enabledAgents.length === 1;
|
|
199774
|
+
const roles = {};
|
|
199775
|
+
for (const agent of enabledAgents) {
|
|
199776
|
+
roles[agent] = singleAgentMode ? "combined_reviewer" : config2.agents[agent].role;
|
|
199777
|
+
}
|
|
199778
|
+
return roles;
|
|
199779
|
+
}
|
|
199240
199780
|
function defaultAgentManager(config2) {
|
|
199241
199781
|
if (process.env.KYOSO_TEST_FAKE_AGENTS === "1")
|
|
199242
199782
|
return new FakeAgentManager;
|
|
@@ -199274,6 +199814,8 @@ async function buildSecretBlockResult(input2) {
|
|
|
199274
199814
|
const resultWithoutMarkdown = {
|
|
199275
199815
|
decision: "block",
|
|
199276
199816
|
degraded: false,
|
|
199817
|
+
agentsUsed: [],
|
|
199818
|
+
reviewMode: "multi_agent",
|
|
199277
199819
|
findings: [finding],
|
|
199278
199820
|
cisaSecureByDesign: cisa,
|
|
199279
199821
|
disagreements: [],
|
|
@@ -199354,6 +199896,8 @@ async function buildPolicyBlockResult(input2) {
|
|
|
199354
199896
|
const resultWithoutMarkdown = {
|
|
199355
199897
|
decision: "block",
|
|
199356
199898
|
degraded: false,
|
|
199899
|
+
agentsUsed: [],
|
|
199900
|
+
reviewMode: "multi_agent",
|
|
199357
199901
|
findings: [input2.finding],
|
|
199358
199902
|
cisaSecureByDesign: input2.tool === "security_review" ? computeCisaGate([input2.finding], []) : undefined,
|
|
199359
199903
|
disagreements: [],
|
|
@@ -199395,7 +199939,7 @@ function mergeDenyPatterns(configDeny, requestDeny) {
|
|
|
199395
199939
|
function assertTrustedWorkspaceRoot(requestRoot, configRoot, cwd) {
|
|
199396
199940
|
if (!requestRoot)
|
|
199397
199941
|
return;
|
|
199398
|
-
if (
|
|
199942
|
+
if (resolve5(cwd, requestRoot) !== resolve5(cwd, configRoot)) {
|
|
199399
199943
|
throw new KyosoRequestError("workspace.root is not trusted by config", "UNTRUSTED_WORKSPACE_ROOT");
|
|
199400
199944
|
}
|
|
199401
199945
|
}
|
|
@@ -199503,6 +200047,17 @@ async function main() {
|
|
|
199503
200047
|
console.log(await runInit({ cwd, force: booleanFlag(parsed.flags, "force") }));
|
|
199504
200048
|
return;
|
|
199505
200049
|
}
|
|
200050
|
+
if (parsed.command === "setup") {
|
|
200051
|
+
console.log(await runSetup({
|
|
200052
|
+
cwd,
|
|
200053
|
+
client: parsed.positionals[0],
|
|
200054
|
+
write: booleanFlag(parsed.flags, "write"),
|
|
200055
|
+
global: booleanFlag(parsed.flags, "global"),
|
|
200056
|
+
runner: stringFlag(parsed.flags, "runner"),
|
|
200057
|
+
command: stringFlag(parsed.flags, "command")
|
|
200058
|
+
}));
|
|
200059
|
+
return;
|
|
200060
|
+
}
|
|
199506
200061
|
if (parsed.command === "plan" || parsed.command === "security" || parsed.command === "diff") {
|
|
199507
200062
|
const tool = commandToTool(parsed.command);
|
|
199508
200063
|
const request = await buildReviewRequest(tool, parsed.flags);
|
|
@@ -199554,7 +200109,7 @@ async function buildDiff(tool, flags) {
|
|
|
199554
200109
|
return;
|
|
199555
200110
|
const base = stringFlag(flags, "base") ?? "main";
|
|
199556
200111
|
const head = stringFlag(flags, "head") ?? "HEAD";
|
|
199557
|
-
const result =
|
|
200112
|
+
const result = spawnSync2("git", ["diff", base, head], { encoding: "utf8" });
|
|
199558
200113
|
if (result.status !== 0) {
|
|
199559
200114
|
throw new Error(result.stderr || `git diff failed for ${base}..${head}`);
|
|
199560
200115
|
}
|
|
@@ -199594,6 +200149,7 @@ var HELP = `Kyoso
|
|
|
199594
200149
|
|
|
199595
200150
|
Usage:
|
|
199596
200151
|
kyoso mcp [--config kyoso.config.ts] [--ignore-config] [--trust-config] [--network model_only|unrestricted]
|
|
200152
|
+
kyoso setup [codex|claude-code] [--write] [--runner npx|bunx] [--command <command>] [--global]
|
|
199597
200153
|
kyoso plan --goal <text> [--plan <path-or-text>] [--file <path>] [--json] [--trust-config]
|
|
199598
200154
|
kyoso security --goal <text> [--diff <path>] [--file <path>] [--allow-secret-redaction] [--trust-config]
|
|
199599
200155
|
kyoso diff --base main --head HEAD [--json] [--trust-config]
|