@kody-ade/kody-engine 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/kody2.js +237 -60
- package/dist/executables/init/profile.json +37 -0
- package/package.json +1 -1
package/dist/bin/kody2.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.2.
|
|
6
|
+
version: "0.2.4",
|
|
7
7
|
description: "kody2 \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -137,8 +137,8 @@ function getAnthropicApiKeyOrDummy() {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// src/executor.ts
|
|
140
|
-
import * as
|
|
141
|
-
import * as
|
|
140
|
+
import * as fs11 from "fs";
|
|
141
|
+
import * as path9 from "path";
|
|
142
142
|
|
|
143
143
|
// src/agent.ts
|
|
144
144
|
import * as fs2 from "fs";
|
|
@@ -519,9 +519,6 @@ function parseClaudeCode(p, raw) {
|
|
|
519
519
|
throw new ProfileError(p, `claudeCode.permissionMode must be one of default|acceptEdits|plan|bypassPermissions`);
|
|
520
520
|
}
|
|
521
521
|
const tools = Array.isArray(r.tools) ? r.tools : [];
|
|
522
|
-
if (tools.length === 0) {
|
|
523
|
-
throw new ProfileError(p, `claudeCode.tools must declare at least one SDK tool`);
|
|
524
|
-
}
|
|
525
522
|
const hooksRaw = r.hooks ?? {};
|
|
526
523
|
const hooks = {
|
|
527
524
|
PreToolUse: Array.isArray(hooksRaw.PreToolUse) ? hooksRaw.PreToolUse : [],
|
|
@@ -1651,6 +1648,174 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
1651
1648
|
}
|
|
1652
1649
|
}
|
|
1653
1650
|
|
|
1651
|
+
// src/scripts/initFlow.ts
|
|
1652
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
1653
|
+
import * as fs9 from "fs";
|
|
1654
|
+
import * as path8 from "path";
|
|
1655
|
+
function detectPackageManager(cwd) {
|
|
1656
|
+
if (fs9.existsSync(path8.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
1657
|
+
if (fs9.existsSync(path8.join(cwd, "yarn.lock"))) return "yarn";
|
|
1658
|
+
if (fs9.existsSync(path8.join(cwd, "bun.lockb"))) return "bun";
|
|
1659
|
+
return "npm";
|
|
1660
|
+
}
|
|
1661
|
+
function qualityCommandsFor(pm) {
|
|
1662
|
+
return {
|
|
1663
|
+
typecheck: `${pm} tsc --noEmit`,
|
|
1664
|
+
lint: "",
|
|
1665
|
+
testUnit: `${pm} test`
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
function detectOwnerRepo(cwd) {
|
|
1669
|
+
let url;
|
|
1670
|
+
try {
|
|
1671
|
+
url = execFileSync9("git", ["remote", "get-url", "origin"], {
|
|
1672
|
+
cwd,
|
|
1673
|
+
encoding: "utf-8",
|
|
1674
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1675
|
+
}).trim();
|
|
1676
|
+
} catch {
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
const m = url.match(/[:/]([^/:]+)\/([^/]+?)(?:\.git)?$/) ?? null;
|
|
1680
|
+
if (!m) return null;
|
|
1681
|
+
return { owner: m[1], repo: m[2] };
|
|
1682
|
+
}
|
|
1683
|
+
function makeConfig(pm, ownerRepo, defaultBranch) {
|
|
1684
|
+
return {
|
|
1685
|
+
$schema: "https://raw.githubusercontent.com/aharonyaircohen/kody-engine/main/kody.config.schema.json",
|
|
1686
|
+
quality: qualityCommandsFor(pm),
|
|
1687
|
+
git: { defaultBranch },
|
|
1688
|
+
github: {
|
|
1689
|
+
owner: ownerRepo?.owner ?? "OWNER",
|
|
1690
|
+
repo: ownerRepo?.repo ?? "REPO"
|
|
1691
|
+
},
|
|
1692
|
+
agent: {
|
|
1693
|
+
model: "minimax/MiniMax-M2.7-highspeed"
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
var WORKFLOW_TEMPLATE = `# Drop this file at .github/workflows/kody2.yml in your repo.
|
|
1698
|
+
#
|
|
1699
|
+
# Triggers: @kody2 comment on an issue or PR, or manual workflow_dispatch.
|
|
1700
|
+
# Everything else (install deps, set up LiteLLM, run the agent, open the PR)
|
|
1701
|
+
# is handled inside the @kody-ade/kody-engine package.
|
|
1702
|
+
#
|
|
1703
|
+
# Required repo secrets: at least one model provider key (e.g. MINIMAX_API_KEY,
|
|
1704
|
+
# ANTHROPIC_API_KEY). kody2 reads any *_API_KEY secret automatically via
|
|
1705
|
+
# toJSON(secrets) \u2014 no need to list them here.
|
|
1706
|
+
#
|
|
1707
|
+
# Recommended: KODY_TOKEN secret \u2014 a PAT or GitHub App token with repo
|
|
1708
|
+
# scope so kody2's pushes trigger downstream CI and PR-body edits succeed.
|
|
1709
|
+
|
|
1710
|
+
name: kody2
|
|
1711
|
+
|
|
1712
|
+
on:
|
|
1713
|
+
workflow_dispatch:
|
|
1714
|
+
inputs:
|
|
1715
|
+
issue_number:
|
|
1716
|
+
description: "GitHub issue number"
|
|
1717
|
+
required: true
|
|
1718
|
+
type: string
|
|
1719
|
+
issue_comment:
|
|
1720
|
+
types: [created]
|
|
1721
|
+
|
|
1722
|
+
jobs:
|
|
1723
|
+
run:
|
|
1724
|
+
if: >-
|
|
1725
|
+
\${{ github.event_name == 'workflow_dispatch' ||
|
|
1726
|
+
(github.event_name == 'issue_comment' &&
|
|
1727
|
+
!github.event.issue.pull_request &&
|
|
1728
|
+
contains(github.event.comment.body, '@kody2')) }}
|
|
1729
|
+
runs-on: ubuntu-latest
|
|
1730
|
+
timeout-minutes: 60
|
|
1731
|
+
permissions:
|
|
1732
|
+
issues: write
|
|
1733
|
+
pull-requests: write
|
|
1734
|
+
contents: write
|
|
1735
|
+
steps:
|
|
1736
|
+
- uses: actions/checkout@v4
|
|
1737
|
+
with:
|
|
1738
|
+
fetch-depth: 0
|
|
1739
|
+
token: \${{ secrets.KODY_TOKEN || github.token }}
|
|
1740
|
+
|
|
1741
|
+
- uses: actions/setup-node@v4
|
|
1742
|
+
with:
|
|
1743
|
+
node-version: 22
|
|
1744
|
+
|
|
1745
|
+
- uses: actions/setup-python@v5
|
|
1746
|
+
with:
|
|
1747
|
+
python-version: "3.12"
|
|
1748
|
+
|
|
1749
|
+
- env:
|
|
1750
|
+
ALL_SECRETS: \${{ toJSON(secrets) }}
|
|
1751
|
+
run: npx -y -p @kody-ade/kody-engine@latest kody2 ci --issue \${{ github.event.inputs.issue_number || github.event.issue.number }}
|
|
1752
|
+
`;
|
|
1753
|
+
function defaultBranchFromGit(cwd) {
|
|
1754
|
+
try {
|
|
1755
|
+
const ref = execFileSync9("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
1756
|
+
cwd,
|
|
1757
|
+
encoding: "utf-8",
|
|
1758
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1759
|
+
}).trim();
|
|
1760
|
+
return ref.replace("refs/remotes/origin/", "");
|
|
1761
|
+
} catch {
|
|
1762
|
+
try {
|
|
1763
|
+
return execFileSync9("git", ["branch", "--show-current"], {
|
|
1764
|
+
cwd,
|
|
1765
|
+
encoding: "utf-8",
|
|
1766
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1767
|
+
}).trim() || "main";
|
|
1768
|
+
} catch {
|
|
1769
|
+
return "main";
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
function performInit(cwd, force) {
|
|
1774
|
+
const wrote = [];
|
|
1775
|
+
const skipped = [];
|
|
1776
|
+
const pm = detectPackageManager(cwd);
|
|
1777
|
+
const ownerRepo = detectOwnerRepo(cwd);
|
|
1778
|
+
const defaultBranch = defaultBranchFromGit(cwd);
|
|
1779
|
+
const configPath = path8.join(cwd, "kody.config.json");
|
|
1780
|
+
if (fs9.existsSync(configPath) && !force) {
|
|
1781
|
+
skipped.push("kody.config.json");
|
|
1782
|
+
} else {
|
|
1783
|
+
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
1784
|
+
fs9.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
1785
|
+
`);
|
|
1786
|
+
wrote.push("kody.config.json");
|
|
1787
|
+
}
|
|
1788
|
+
const workflowDir = path8.join(cwd, ".github", "workflows");
|
|
1789
|
+
const workflowPath = path8.join(workflowDir, "kody2.yml");
|
|
1790
|
+
if (fs9.existsSync(workflowPath) && !force) {
|
|
1791
|
+
skipped.push(".github/workflows/kody2.yml");
|
|
1792
|
+
} else {
|
|
1793
|
+
fs9.mkdirSync(workflowDir, { recursive: true });
|
|
1794
|
+
fs9.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
1795
|
+
wrote.push(".github/workflows/kody2.yml");
|
|
1796
|
+
}
|
|
1797
|
+
return { wrote, skipped };
|
|
1798
|
+
}
|
|
1799
|
+
var initFlow = async (ctx) => {
|
|
1800
|
+
const force = ctx.args.force === true;
|
|
1801
|
+
const cwd = ctx.cwd;
|
|
1802
|
+
const { wrote, skipped } = performInit(cwd, force);
|
|
1803
|
+
process.stdout.write("\u2192 kody2 init\n");
|
|
1804
|
+
for (const f of wrote) process.stdout.write(` wrote ${f}
|
|
1805
|
+
`);
|
|
1806
|
+
for (const f of skipped) process.stdout.write(` skipped ${f} (already exists; pass --force to overwrite)
|
|
1807
|
+
`);
|
|
1808
|
+
process.stdout.write(
|
|
1809
|
+
wrote.length > 0 ? `
|
|
1810
|
+
Done. Edit kody.config.json to pick your model, then push the workflow file.
|
|
1811
|
+
` : `
|
|
1812
|
+
Nothing to do. All files already present. (Use --force to overwrite.)
|
|
1813
|
+
`
|
|
1814
|
+
);
|
|
1815
|
+
ctx.skipAgent = true;
|
|
1816
|
+
ctx.output.exitCode = 0;
|
|
1817
|
+
};
|
|
1818
|
+
|
|
1654
1819
|
// src/scripts/loadConventions.ts
|
|
1655
1820
|
var loadConventions = async (ctx) => {
|
|
1656
1821
|
const conventions = loadProjectConventions(ctx.cwd);
|
|
@@ -1734,7 +1899,7 @@ function postWith(type, n, body, cwd) {
|
|
|
1734
1899
|
}
|
|
1735
1900
|
|
|
1736
1901
|
// src/scripts/resolveFlow.ts
|
|
1737
|
-
import { execFileSync as
|
|
1902
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
1738
1903
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
1739
1904
|
var resolveFlow = async (ctx) => {
|
|
1740
1905
|
const prNumber = ctx.args.pr;
|
|
@@ -1786,7 +1951,7 @@ var resolveFlow = async (ctx) => {
|
|
|
1786
1951
|
};
|
|
1787
1952
|
function getConflictedFiles(cwd) {
|
|
1788
1953
|
try {
|
|
1789
|
-
const out =
|
|
1954
|
+
const out = execFileSync10("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
1790
1955
|
encoding: "utf-8",
|
|
1791
1956
|
cwd,
|
|
1792
1957
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -1801,7 +1966,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
1801
1966
|
let total = 0;
|
|
1802
1967
|
for (const f of files) {
|
|
1803
1968
|
try {
|
|
1804
|
-
const content =
|
|
1969
|
+
const content = execFileSync10("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
1805
1970
|
const snippet = `### ${f}
|
|
1806
1971
|
|
|
1807
1972
|
\`\`\`
|
|
@@ -1939,7 +2104,7 @@ var verify = async (ctx) => {
|
|
|
1939
2104
|
};
|
|
1940
2105
|
|
|
1941
2106
|
// src/scripts/writeRunSummary.ts
|
|
1942
|
-
import * as
|
|
2107
|
+
import * as fs10 from "fs";
|
|
1943
2108
|
var writeRunSummary = async (ctx) => {
|
|
1944
2109
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
1945
2110
|
if (!summaryPath) return;
|
|
@@ -1961,7 +2126,7 @@ var writeRunSummary = async (ctx) => {
|
|
|
1961
2126
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
1962
2127
|
lines.push("");
|
|
1963
2128
|
try {
|
|
1964
|
-
|
|
2129
|
+
fs10.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
1965
2130
|
`);
|
|
1966
2131
|
} catch {
|
|
1967
2132
|
}
|
|
@@ -1973,6 +2138,7 @@ var preflightScripts = {
|
|
|
1973
2138
|
fixFlow,
|
|
1974
2139
|
fixCiFlow,
|
|
1975
2140
|
resolveFlow,
|
|
2141
|
+
initFlow,
|
|
1976
2142
|
loadConventions,
|
|
1977
2143
|
loadCoverageRules,
|
|
1978
2144
|
composePrompt
|
|
@@ -1992,7 +2158,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
1992
2158
|
]);
|
|
1993
2159
|
|
|
1994
2160
|
// src/tools.ts
|
|
1995
|
-
import { execFileSync as
|
|
2161
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
1996
2162
|
function verifyCliTools(tools, cwd) {
|
|
1997
2163
|
const out = [];
|
|
1998
2164
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -2025,7 +2191,7 @@ function verifyOne(tool, cwd) {
|
|
|
2025
2191
|
}
|
|
2026
2192
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
2027
2193
|
try {
|
|
2028
|
-
|
|
2194
|
+
execFileSync11("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
2029
2195
|
return true;
|
|
2030
2196
|
} catch {
|
|
2031
2197
|
return false;
|
|
@@ -2076,7 +2242,7 @@ async function runExecutable(profileName, input) {
|
|
|
2076
2242
|
data: {},
|
|
2077
2243
|
output: { exitCode: 0 }
|
|
2078
2244
|
};
|
|
2079
|
-
const ndjsonDir =
|
|
2245
|
+
const ndjsonDir = path9.join(input.cwd, ".kody2");
|
|
2080
2246
|
const invokeAgent = async (prompt) => runAgent({
|
|
2081
2247
|
prompt,
|
|
2082
2248
|
model,
|
|
@@ -2135,17 +2301,17 @@ async function runExecutable(profileName, input) {
|
|
|
2135
2301
|
}
|
|
2136
2302
|
}
|
|
2137
2303
|
function resolveProfilePath(profileName) {
|
|
2138
|
-
const here =
|
|
2304
|
+
const here = path9.dirname(new URL(import.meta.url).pathname);
|
|
2139
2305
|
const candidates = [
|
|
2140
|
-
|
|
2306
|
+
path9.join(here, "executables", profileName, "profile.json"),
|
|
2141
2307
|
// same-dir sibling (dev)
|
|
2142
|
-
|
|
2308
|
+
path9.join(here, "..", "executables", profileName, "profile.json"),
|
|
2143
2309
|
// up one (prod: dist/bin → dist/executables)
|
|
2144
|
-
|
|
2310
|
+
path9.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
2145
2311
|
// fallback
|
|
2146
2312
|
];
|
|
2147
2313
|
for (const c of candidates) {
|
|
2148
|
-
if (
|
|
2314
|
+
if (fs11.existsSync(c)) return c;
|
|
2149
2315
|
}
|
|
2150
2316
|
return candidates[0];
|
|
2151
2317
|
}
|
|
@@ -2223,12 +2389,12 @@ function finish(out) {
|
|
|
2223
2389
|
}
|
|
2224
2390
|
|
|
2225
2391
|
// src/kody2-cli.ts
|
|
2226
|
-
import { execFileSync as
|
|
2227
|
-
import * as
|
|
2228
|
-
import * as
|
|
2392
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
2393
|
+
import * as fs13 from "fs";
|
|
2394
|
+
import * as path10 from "path";
|
|
2229
2395
|
|
|
2230
2396
|
// src/dispatch.ts
|
|
2231
|
-
import * as
|
|
2397
|
+
import * as fs12 from "fs";
|
|
2232
2398
|
function autoDispatch(explicit) {
|
|
2233
2399
|
if (explicit?.mode && explicit.target) {
|
|
2234
2400
|
return {
|
|
@@ -2238,10 +2404,10 @@ function autoDispatch(explicit) {
|
|
|
2238
2404
|
}
|
|
2239
2405
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2240
2406
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2241
|
-
if (!eventName || !eventPath || !
|
|
2407
|
+
if (!eventName || !eventPath || !fs12.existsSync(eventPath)) return null;
|
|
2242
2408
|
let event = {};
|
|
2243
2409
|
try {
|
|
2244
|
-
event = JSON.parse(
|
|
2410
|
+
event = JSON.parse(fs12.readFileSync(eventPath, "utf-8"));
|
|
2245
2411
|
} catch {
|
|
2246
2412
|
return null;
|
|
2247
2413
|
}
|
|
@@ -2360,15 +2526,15 @@ function resolveAuthToken(env = process.env) {
|
|
|
2360
2526
|
if (token && !env.GH_TOKEN) env.GH_TOKEN = token;
|
|
2361
2527
|
return token;
|
|
2362
2528
|
}
|
|
2363
|
-
function
|
|
2364
|
-
if (
|
|
2365
|
-
if (
|
|
2366
|
-
if (
|
|
2529
|
+
function detectPackageManager2(cwd) {
|
|
2530
|
+
if (fs13.existsSync(path10.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
2531
|
+
if (fs13.existsSync(path10.join(cwd, "yarn.lock"))) return "yarn";
|
|
2532
|
+
if (fs13.existsSync(path10.join(cwd, "bun.lockb"))) return "bun";
|
|
2367
2533
|
return "npm";
|
|
2368
2534
|
}
|
|
2369
2535
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
2370
2536
|
try {
|
|
2371
|
-
|
|
2537
|
+
execFileSync12(cmd, args, {
|
|
2372
2538
|
cwd,
|
|
2373
2539
|
stdio: stream ? "inherit" : "pipe",
|
|
2374
2540
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -2381,7 +2547,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
2381
2547
|
}
|
|
2382
2548
|
function isOnPath(bin) {
|
|
2383
2549
|
try {
|
|
2384
|
-
|
|
2550
|
+
execFileSync12("which", [bin], { stdio: "pipe" });
|
|
2385
2551
|
return true;
|
|
2386
2552
|
} catch {
|
|
2387
2553
|
return false;
|
|
@@ -2415,7 +2581,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
2415
2581
|
} catch {
|
|
2416
2582
|
}
|
|
2417
2583
|
try {
|
|
2418
|
-
|
|
2584
|
+
execFileSync12("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
2419
2585
|
process.stdout.write("\u2192 kody2: litellm already installed\n");
|
|
2420
2586
|
return 0;
|
|
2421
2587
|
} catch {
|
|
@@ -2425,26 +2591,26 @@ function installLitellmIfNeeded(cwd) {
|
|
|
2425
2591
|
}
|
|
2426
2592
|
function configureGitIdentity(cwd) {
|
|
2427
2593
|
try {
|
|
2428
|
-
const name =
|
|
2594
|
+
const name = execFileSync12("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
2429
2595
|
if (name) return;
|
|
2430
2596
|
} catch {
|
|
2431
2597
|
}
|
|
2432
2598
|
try {
|
|
2433
|
-
|
|
2599
|
+
execFileSync12("git", ["config", "user.name", "kody2-bot"], { cwd, stdio: "pipe" });
|
|
2434
2600
|
} catch {
|
|
2435
2601
|
}
|
|
2436
2602
|
try {
|
|
2437
|
-
|
|
2603
|
+
execFileSync12("git", ["config", "user.email", "kody2-bot@users.noreply.github.com"], { cwd, stdio: "pipe" });
|
|
2438
2604
|
} catch {
|
|
2439
2605
|
}
|
|
2440
2606
|
}
|
|
2441
2607
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
2442
2608
|
if (!issueNumber) return;
|
|
2443
|
-
const logPath =
|
|
2609
|
+
const logPath = path10.join(cwd, ".kody2", "last-run.jsonl");
|
|
2444
2610
|
let tail = "";
|
|
2445
2611
|
try {
|
|
2446
|
-
if (
|
|
2447
|
-
const content =
|
|
2612
|
+
if (fs13.existsSync(logPath)) {
|
|
2613
|
+
const content = fs13.readFileSync(logPath, "utf-8");
|
|
2448
2614
|
tail = content.slice(-3e3);
|
|
2449
2615
|
}
|
|
2450
2616
|
} catch {
|
|
@@ -2481,7 +2647,7 @@ async function runCi(argv) {
|
|
|
2481
2647
|
${CI_HELP}`);
|
|
2482
2648
|
return 64;
|
|
2483
2649
|
}
|
|
2484
|
-
const cwd = args.cwd ?
|
|
2650
|
+
const cwd = args.cwd ? path10.resolve(args.cwd) : process.cwd();
|
|
2485
2651
|
const dispatch = autoFallback ?? {
|
|
2486
2652
|
mode: "run",
|
|
2487
2653
|
target: args.issueNumber,
|
|
@@ -2496,7 +2662,7 @@ ${CI_HELP}`);
|
|
|
2496
2662
|
`);
|
|
2497
2663
|
resolveAuthToken();
|
|
2498
2664
|
reactToTriggerComment(cwd);
|
|
2499
|
-
const pm = args.packageManager ??
|
|
2665
|
+
const pm = args.packageManager ?? detectPackageManager2(cwd);
|
|
2500
2666
|
process.stdout.write(`\u2192 kody2: package manager = ${pm}
|
|
2501
2667
|
`);
|
|
2502
2668
|
if (!args.skipInstall) {
|
|
@@ -2557,31 +2723,31 @@ ${CI_HELP}`);
|
|
|
2557
2723
|
}
|
|
2558
2724
|
|
|
2559
2725
|
// src/registry.ts
|
|
2560
|
-
import * as
|
|
2561
|
-
import * as
|
|
2726
|
+
import * as fs14 from "fs";
|
|
2727
|
+
import * as path11 from "path";
|
|
2562
2728
|
function getExecutablesRoot() {
|
|
2563
|
-
const here =
|
|
2729
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2564
2730
|
const candidates = [
|
|
2565
|
-
|
|
2731
|
+
path11.join(here, "executables"),
|
|
2566
2732
|
// dev: src/
|
|
2567
|
-
|
|
2733
|
+
path11.join(here, "..", "executables"),
|
|
2568
2734
|
// built: dist/bin → dist/executables
|
|
2569
|
-
|
|
2735
|
+
path11.join(here, "..", "src", "executables")
|
|
2570
2736
|
// fallback
|
|
2571
2737
|
];
|
|
2572
2738
|
for (const c of candidates) {
|
|
2573
|
-
if (
|
|
2739
|
+
if (fs14.existsSync(c) && fs14.statSync(c).isDirectory()) return c;
|
|
2574
2740
|
}
|
|
2575
2741
|
return candidates[0];
|
|
2576
2742
|
}
|
|
2577
2743
|
function listExecutables(root = getExecutablesRoot()) {
|
|
2578
|
-
if (!
|
|
2579
|
-
const entries =
|
|
2744
|
+
if (!fs14.existsSync(root)) return [];
|
|
2745
|
+
const entries = fs14.readdirSync(root, { withFileTypes: true });
|
|
2580
2746
|
const out = [];
|
|
2581
2747
|
for (const ent of entries) {
|
|
2582
2748
|
if (!ent.isDirectory()) continue;
|
|
2583
|
-
const profilePath =
|
|
2584
|
-
if (
|
|
2749
|
+
const profilePath = path11.join(root, ent.name, "profile.json");
|
|
2750
|
+
if (fs14.existsSync(profilePath) && fs14.statSync(profilePath).isFile()) {
|
|
2585
2751
|
out.push({ name: ent.name, profilePath });
|
|
2586
2752
|
}
|
|
2587
2753
|
}
|
|
@@ -2589,8 +2755,8 @@ function listExecutables(root = getExecutablesRoot()) {
|
|
|
2589
2755
|
}
|
|
2590
2756
|
function hasExecutable(name, root = getExecutablesRoot()) {
|
|
2591
2757
|
if (!isSafeName(name)) return false;
|
|
2592
|
-
const profilePath =
|
|
2593
|
-
return
|
|
2758
|
+
const profilePath = path11.join(root, name, "profile.json");
|
|
2759
|
+
return fs14.existsSync(profilePath) && fs14.statSync(profilePath).isFile();
|
|
2594
2760
|
}
|
|
2595
2761
|
function isSafeName(name) {
|
|
2596
2762
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
@@ -2728,16 +2894,27 @@ ${HELP_TEXT}`);
|
|
|
2728
2894
|
}
|
|
2729
2895
|
}
|
|
2730
2896
|
const cwd = args.cwd ?? process.cwd();
|
|
2897
|
+
const configlessCommands = /* @__PURE__ */ new Set(["init"]);
|
|
2898
|
+
const needsConfig = !(args.command === "__executable__" && configlessCommands.has(args.executableName ?? ""));
|
|
2731
2899
|
let config;
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2900
|
+
if (needsConfig) {
|
|
2901
|
+
try {
|
|
2902
|
+
config = loadConfig(cwd);
|
|
2903
|
+
} catch (err) {
|
|
2904
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2905
|
+
process.stderr.write(`[kody2] config error: ${msg}
|
|
2737
2906
|
`);
|
|
2738
|
-
|
|
2907
|
+
process.stdout.write(`PR_URL=FAILED: config error: ${msg}
|
|
2739
2908
|
`);
|
|
2740
|
-
|
|
2909
|
+
return 99;
|
|
2910
|
+
}
|
|
2911
|
+
} else {
|
|
2912
|
+
config = {
|
|
2913
|
+
quality: { typecheck: "", lint: "", testUnit: "" },
|
|
2914
|
+
git: { defaultBranch: "main" },
|
|
2915
|
+
github: { owner: "", repo: "" },
|
|
2916
|
+
agent: { model: "claude/claude-haiku-4-5-20251001" }
|
|
2917
|
+
};
|
|
2741
2918
|
}
|
|
2742
2919
|
if (args.command === "__executable__") {
|
|
2743
2920
|
try {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "init",
|
|
3
|
+
"describe": "Scaffold a consumer repo with kody.config.json and the @kody2 workflow. No agent.",
|
|
4
|
+
|
|
5
|
+
"inputs": [
|
|
6
|
+
{
|
|
7
|
+
"name": "force",
|
|
8
|
+
"flag": "--force",
|
|
9
|
+
"type": "bool",
|
|
10
|
+
"required": false,
|
|
11
|
+
"describe": "Overwrite existing files instead of skipping them."
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
|
|
15
|
+
"claudeCode": {
|
|
16
|
+
"model": "inherit",
|
|
17
|
+
"permissionMode": "acceptEdits",
|
|
18
|
+
"maxTurns": null,
|
|
19
|
+
"systemPromptAppend": null,
|
|
20
|
+
"tools": [],
|
|
21
|
+
"hooks": { "PreToolUse": [], "PostToolUse": [], "Stop": [] },
|
|
22
|
+
"skills": [],
|
|
23
|
+
"commands": [],
|
|
24
|
+
"subagents": [],
|
|
25
|
+
"plugins": [],
|
|
26
|
+
"mcpServers": []
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"cliTools": [],
|
|
30
|
+
|
|
31
|
+
"scripts": {
|
|
32
|
+
"preflight": [
|
|
33
|
+
{ "script": "initFlow" }
|
|
34
|
+
],
|
|
35
|
+
"postflight": []
|
|
36
|
+
}
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "kody2 — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|