@kody-ade/kody-engine 0.2.54 → 0.2.55
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
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.55",
|
|
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",
|
|
@@ -50,7 +50,7 @@ var package_default = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
// src/chat-cli.ts
|
|
53
|
-
import { execFileSync as
|
|
53
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
54
54
|
import * as fs22 from "fs";
|
|
55
55
|
import * as path19 from "path";
|
|
56
56
|
|
|
@@ -182,9 +182,24 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
182
182
|
issueContext: parseIssueContext(raw.issueContext),
|
|
183
183
|
testRequirements: parseTestRequirements(raw.testRequirements),
|
|
184
184
|
defaultExecutable: typeof raw.defaultExecutable === "string" && raw.defaultExecutable.length > 0 ? raw.defaultExecutable : void 0,
|
|
185
|
+
classify: parseClassifyConfig(raw.classify),
|
|
185
186
|
release: parseReleaseConfig(raw.release)
|
|
186
187
|
};
|
|
187
188
|
}
|
|
189
|
+
function parseClassifyConfig(raw) {
|
|
190
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
191
|
+
const r = raw;
|
|
192
|
+
const out = {};
|
|
193
|
+
if (r.labelMap && typeof r.labelMap === "object") {
|
|
194
|
+
const entries = Object.entries(r.labelMap).filter(
|
|
195
|
+
([, v]) => typeof v === "string" && v.length > 0
|
|
196
|
+
);
|
|
197
|
+
if (entries.length > 0) {
|
|
198
|
+
out.labelMap = Object.fromEntries(entries.map(([k, v]) => [k.toLowerCase(), String(v)]));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
202
|
+
}
|
|
188
203
|
function parseReleaseConfig(raw) {
|
|
189
204
|
if (!raw || typeof raw !== "object") return void 0;
|
|
190
205
|
const r = raw;
|
|
@@ -528,7 +543,7 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
528
543
|
}
|
|
529
544
|
|
|
530
545
|
// src/kody2-cli.ts
|
|
531
|
-
import { execFileSync as
|
|
546
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
532
547
|
import * as fs21 from "fs";
|
|
533
548
|
import * as path18 from "path";
|
|
534
549
|
|
|
@@ -1499,6 +1514,41 @@ ${formatMissesForFeedback(misses)}`;
|
|
|
1499
1514
|
ctx.data.coverageMisses = finalMisses;
|
|
1500
1515
|
};
|
|
1501
1516
|
|
|
1517
|
+
// src/scripts/classifyByLabel.ts
|
|
1518
|
+
var VALID_CLASSES = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
1519
|
+
var classifyByLabel = async (ctx) => {
|
|
1520
|
+
const issue = ctx.data.issue;
|
|
1521
|
+
const labels = issue?.labels;
|
|
1522
|
+
if (!labels || labels.length === 0) return;
|
|
1523
|
+
const cfgMap = ctx.config.classify?.labelMap;
|
|
1524
|
+
const map = cfgMap ?? defaultLabelMap();
|
|
1525
|
+
for (const label of labels) {
|
|
1526
|
+
const candidate = map[label.toLowerCase()];
|
|
1527
|
+
if (candidate && VALID_CLASSES.has(candidate)) {
|
|
1528
|
+
ctx.data.classification = candidate;
|
|
1529
|
+
ctx.data.classificationSource = "label";
|
|
1530
|
+
ctx.data.classificationReason = `label \`${label}\` \u2192 ${candidate}`;
|
|
1531
|
+
ctx.skipAgent = true;
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
function defaultLabelMap() {
|
|
1537
|
+
return {
|
|
1538
|
+
bug: "bug",
|
|
1539
|
+
enhancement: "bug",
|
|
1540
|
+
refactor: "feature",
|
|
1541
|
+
feature: "feature",
|
|
1542
|
+
performance: "feature",
|
|
1543
|
+
rfc: "spec",
|
|
1544
|
+
design: "spec",
|
|
1545
|
+
spec: "spec",
|
|
1546
|
+
docs: "chore",
|
|
1547
|
+
chore: "chore",
|
|
1548
|
+
dependencies: "chore"
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1502
1552
|
// src/scripts/commitAndPush.ts
|
|
1503
1553
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
1504
1554
|
|
|
@@ -2366,7 +2416,7 @@ function gh2(args, options) {
|
|
|
2366
2416
|
}).trim();
|
|
2367
2417
|
}
|
|
2368
2418
|
function getIssue(issueNumber, cwd) {
|
|
2369
|
-
const output = gh2(["issue", "view", String(issueNumber), "--json", "number,title,body,comments"], { cwd });
|
|
2419
|
+
const output = gh2(["issue", "view", String(issueNumber), "--json", "number,title,body,comments,labels"], { cwd });
|
|
2370
2420
|
const parsed = JSON.parse(output);
|
|
2371
2421
|
if (typeof parsed?.title !== "string") {
|
|
2372
2422
|
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
@@ -2379,7 +2429,8 @@ function getIssue(issueNumber, cwd) {
|
|
|
2379
2429
|
body: c.body ?? "",
|
|
2380
2430
|
author: c.author?.login ?? "unknown",
|
|
2381
2431
|
createdAt: c.createdAt ?? ""
|
|
2382
|
-
}))
|
|
2432
|
+
})),
|
|
2433
|
+
labels: Array.isArray(parsed.labels) ? parsed.labels.map((l) => l.name ?? "").filter((n) => n.length > 0) : []
|
|
2383
2434
|
};
|
|
2384
2435
|
}
|
|
2385
2436
|
function stripKody2Mentions(body) {
|
|
@@ -3534,7 +3585,9 @@ var loadIssueContext = async (ctx) => {
|
|
|
3534
3585
|
const kept = sorted.slice(0, limit);
|
|
3535
3586
|
const commentsFormatted = kept.length === 0 ? "(no comments yet)" : kept.map((c) => `- **${c.author}** (${c.createdAt}):
|
|
3536
3587
|
${truncate2(c.body, maxBytes).replace(/\n/g, "\n ")}`).join("\n\n");
|
|
3537
|
-
|
|
3588
|
+
const labels = issue.labels ?? [];
|
|
3589
|
+
const labelsFormatted = labels.length === 0 ? "(no labels)" : labels.map((l) => `\`${l}\``).join(", ");
|
|
3590
|
+
ctx.data.issue = { ...issue, commentsFormatted, labelsFormatted };
|
|
3538
3591
|
ctx.data.commentTargetType = "issue";
|
|
3539
3592
|
ctx.data.commentTargetNumber = issueNumber;
|
|
3540
3593
|
};
|
|
@@ -3659,6 +3712,86 @@ var persistFlowState = async (ctx) => {
|
|
|
3659
3712
|
}
|
|
3660
3713
|
};
|
|
3661
3714
|
|
|
3715
|
+
// src/scripts/postClassification.ts
|
|
3716
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
3717
|
+
var API_TIMEOUT_MS6 = 3e4;
|
|
3718
|
+
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
3719
|
+
var postClassification = async (ctx) => {
|
|
3720
|
+
const issueNumber = ctx.args.issue;
|
|
3721
|
+
if (!issueNumber) return;
|
|
3722
|
+
const presetClassification = ctx.data.classification;
|
|
3723
|
+
let classification = null;
|
|
3724
|
+
let reason = null;
|
|
3725
|
+
if (presetClassification && VALID_CLASSES2.has(presetClassification)) {
|
|
3726
|
+
classification = presetClassification;
|
|
3727
|
+
reason = ctx.data.classificationReason ?? "label-based match";
|
|
3728
|
+
} else {
|
|
3729
|
+
const parsed = parseClassification(ctx.data.prSummary ?? "");
|
|
3730
|
+
classification = parsed?.classification ?? null;
|
|
3731
|
+
reason = parsed?.reason ?? null;
|
|
3732
|
+
}
|
|
3733
|
+
if (!classification) {
|
|
3734
|
+
ctx.data.action = failedAction("classification missing or invalid");
|
|
3735
|
+
tryAuditComment(issueNumber, "\u26A0\uFE0F kody2 classifier could not decide \u2014 please re-run with an explicit `@kody2 <type>`.", ctx.cwd);
|
|
3736
|
+
ctx.output.exitCode = 1;
|
|
3737
|
+
ctx.output.reason = "classify: no decision";
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3740
|
+
tryAuditComment(
|
|
3741
|
+
issueNumber,
|
|
3742
|
+
`\u{1F50E} kody2 classified as \`${classification}\`${reason ? ` \u2014 ${reason}` : ""}`,
|
|
3743
|
+
ctx.cwd
|
|
3744
|
+
);
|
|
3745
|
+
try {
|
|
3746
|
+
execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", `@kody2 ${classification}`], {
|
|
3747
|
+
cwd: ctx.cwd,
|
|
3748
|
+
timeout: API_TIMEOUT_MS6,
|
|
3749
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3750
|
+
});
|
|
3751
|
+
} catch (err) {
|
|
3752
|
+
process.stderr.write(
|
|
3753
|
+
`[kody2 postClassification] failed to dispatch @kody2 ${classification}: ${err instanceof Error ? err.message : String(err)}
|
|
3754
|
+
`
|
|
3755
|
+
);
|
|
3756
|
+
ctx.data.action = failedAction("dispatch post failed");
|
|
3757
|
+
ctx.output.exitCode = 1;
|
|
3758
|
+
ctx.output.reason = "classify: dispatch failed";
|
|
3759
|
+
return;
|
|
3760
|
+
}
|
|
3761
|
+
ctx.data.action = makeAction2(`CLASSIFIED_AS_${classification.toUpperCase()}`, {
|
|
3762
|
+
classification,
|
|
3763
|
+
reason: reason ?? "",
|
|
3764
|
+
source: ctx.data.classificationSource ?? "agent"
|
|
3765
|
+
});
|
|
3766
|
+
ctx.data.classification = classification;
|
|
3767
|
+
ctx.data.classificationReason = reason ?? "";
|
|
3768
|
+
};
|
|
3769
|
+
function parseClassification(prSummary) {
|
|
3770
|
+
if (!prSummary) return null;
|
|
3771
|
+
const classMatch = prSummary.match(/classification:\s*(feature|bug|spec|chore)\b/i);
|
|
3772
|
+
if (!classMatch) return null;
|
|
3773
|
+
const classification = classMatch[1].toLowerCase();
|
|
3774
|
+
const reasonMatch = prSummary.match(/reason:\s*(.+)$/im);
|
|
3775
|
+
const reason = reasonMatch ? reasonMatch[1].trim() : "";
|
|
3776
|
+
return { classification, reason };
|
|
3777
|
+
}
|
|
3778
|
+
function tryAuditComment(issueNumber, body, cwd) {
|
|
3779
|
+
try {
|
|
3780
|
+
execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3781
|
+
cwd,
|
|
3782
|
+
timeout: API_TIMEOUT_MS6,
|
|
3783
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3784
|
+
});
|
|
3785
|
+
} catch {
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
function makeAction2(type, payload) {
|
|
3789
|
+
return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3790
|
+
}
|
|
3791
|
+
function failedAction(reason) {
|
|
3792
|
+
return { type: "CLASSIFY_FAILED", payload: { reason }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3662
3795
|
// src/scripts/postIssueComment.ts
|
|
3663
3796
|
var postIssueComment2 = async (ctx) => {
|
|
3664
3797
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0) return;
|
|
@@ -3773,7 +3906,7 @@ function reviewAction(verdict, payload) {
|
|
|
3773
3906
|
const type = verdict === "PASS" ? "REVIEW_PASS" : verdict === "CONCERNS" ? "REVIEW_CONCERNS" : verdict === "FAIL" ? "REVIEW_FAIL" : "REVIEW_COMPLETED";
|
|
3774
3907
|
return { type, payload: { verdict, ...payload }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3775
3908
|
}
|
|
3776
|
-
function
|
|
3909
|
+
function failedAction2(reason) {
|
|
3777
3910
|
return { type: "REVIEW_FAILED", payload: { reason }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3778
3911
|
}
|
|
3779
3912
|
var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
@@ -3781,7 +3914,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
|
3781
3914
|
if (!prNumber) {
|
|
3782
3915
|
ctx.output.exitCode = 99;
|
|
3783
3916
|
ctx.output.reason = "review postflight: no PR number in context";
|
|
3784
|
-
ctx.data.action =
|
|
3917
|
+
ctx.data.action = failedAction2(ctx.output.reason);
|
|
3785
3918
|
return;
|
|
3786
3919
|
}
|
|
3787
3920
|
if (!agentResult || agentResult.outcome !== "completed") {
|
|
@@ -3792,7 +3925,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
|
3792
3925
|
}
|
|
3793
3926
|
ctx.output.exitCode = 1;
|
|
3794
3927
|
ctx.output.reason = reason;
|
|
3795
|
-
ctx.data.action =
|
|
3928
|
+
ctx.data.action = failedAction2(reason);
|
|
3796
3929
|
return;
|
|
3797
3930
|
}
|
|
3798
3931
|
const reviewBody = agentResult.finalText.trim();
|
|
@@ -3803,7 +3936,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
|
3803
3936
|
}
|
|
3804
3937
|
ctx.output.exitCode = 1;
|
|
3805
3938
|
ctx.output.reason = "empty review body";
|
|
3806
|
-
ctx.data.action =
|
|
3939
|
+
ctx.data.action = failedAction2("empty review body");
|
|
3807
3940
|
return;
|
|
3808
3941
|
}
|
|
3809
3942
|
try {
|
|
@@ -3812,7 +3945,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
|
3812
3945
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3813
3946
|
ctx.output.exitCode = 4;
|
|
3814
3947
|
ctx.output.reason = `failed to post review comment: ${msg}`;
|
|
3815
|
-
ctx.data.action =
|
|
3948
|
+
ctx.data.action = failedAction2(ctx.output.reason);
|
|
3816
3949
|
return;
|
|
3817
3950
|
}
|
|
3818
3951
|
const verdict = detectVerdict(reviewBody);
|
|
@@ -3828,7 +3961,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
3828
3961
|
};
|
|
3829
3962
|
|
|
3830
3963
|
// src/scripts/releaseFlow.ts
|
|
3831
|
-
import { execFileSync as
|
|
3964
|
+
import { execFileSync as execFileSync15, spawnSync } from "child_process";
|
|
3832
3965
|
import * as fs18 from "fs";
|
|
3833
3966
|
import * as path16 from "path";
|
|
3834
3967
|
function bumpVersion(current, bump) {
|
|
@@ -3858,7 +3991,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
3858
3991
|
const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
|
|
3859
3992
|
let log = "";
|
|
3860
3993
|
try {
|
|
3861
|
-
log =
|
|
3994
|
+
log = execFileSync15("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
|
|
3862
3995
|
cwd,
|
|
3863
3996
|
encoding: "utf-8",
|
|
3864
3997
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3915,7 +4048,7 @@ ${entry}${prior.slice(idx + 1)}`);
|
|
|
3915
4048
|
}
|
|
3916
4049
|
}
|
|
3917
4050
|
function git3(args, cwd, timeout = 6e4) {
|
|
3918
|
-
return
|
|
4051
|
+
return execFileSync15("git", args, {
|
|
3919
4052
|
encoding: "utf-8",
|
|
3920
4053
|
timeout,
|
|
3921
4054
|
cwd,
|
|
@@ -4142,12 +4275,12 @@ function fail(ctx, profile, reason) {
|
|
|
4142
4275
|
ctx.data.agentDone = false;
|
|
4143
4276
|
ctx.data.agentFailureReason = reason;
|
|
4144
4277
|
const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
|
|
4145
|
-
const
|
|
4278
|
+
const failedAction3 = {
|
|
4146
4279
|
type: `${modeSeg}_FAILED`,
|
|
4147
4280
|
payload: { reason },
|
|
4148
4281
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4149
4282
|
};
|
|
4150
|
-
ctx.data.action =
|
|
4283
|
+
ctx.data.action = failedAction3;
|
|
4151
4284
|
}
|
|
4152
4285
|
function countActionItems(block) {
|
|
4153
4286
|
if (!block.trim()) return 0;
|
|
@@ -4186,12 +4319,12 @@ function fail2(ctx, profile, reason) {
|
|
|
4186
4319
|
ctx.data.agentDone = false;
|
|
4187
4320
|
ctx.data.agentFailureReason = reason;
|
|
4188
4321
|
const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
|
|
4189
|
-
const
|
|
4322
|
+
const failedAction3 = {
|
|
4190
4323
|
type: `${modeSeg}_FAILED`,
|
|
4191
4324
|
payload: { reason },
|
|
4192
4325
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4193
4326
|
};
|
|
4194
|
-
ctx.data.action =
|
|
4327
|
+
ctx.data.action = failedAction3;
|
|
4195
4328
|
}
|
|
4196
4329
|
|
|
4197
4330
|
// src/scripts/resolveArtifacts.ts
|
|
@@ -4218,7 +4351,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
4218
4351
|
};
|
|
4219
4352
|
|
|
4220
4353
|
// src/scripts/resolveFlow.ts
|
|
4221
|
-
import { execFileSync as
|
|
4354
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
4222
4355
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
4223
4356
|
var resolveFlow = async (ctx) => {
|
|
4224
4357
|
const prNumber = ctx.args.pr;
|
|
@@ -4270,7 +4403,7 @@ var resolveFlow = async (ctx) => {
|
|
|
4270
4403
|
};
|
|
4271
4404
|
function getConflictedFiles(cwd) {
|
|
4272
4405
|
try {
|
|
4273
|
-
const out =
|
|
4406
|
+
const out = execFileSync16("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
4274
4407
|
encoding: "utf-8",
|
|
4275
4408
|
cwd,
|
|
4276
4409
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -4285,7 +4418,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
4285
4418
|
let total = 0;
|
|
4286
4419
|
for (const f of files) {
|
|
4287
4420
|
try {
|
|
4288
|
-
const content =
|
|
4421
|
+
const content = execFileSync16("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
4289
4422
|
const snippet = `### ${f}
|
|
4290
4423
|
|
|
4291
4424
|
\`\`\`
|
|
@@ -4449,8 +4582,8 @@ var skipAgent = async (ctx) => {
|
|
|
4449
4582
|
};
|
|
4450
4583
|
|
|
4451
4584
|
// src/scripts/startFlow.ts
|
|
4452
|
-
import { execFileSync as
|
|
4453
|
-
var
|
|
4585
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
4586
|
+
var API_TIMEOUT_MS7 = 3e4;
|
|
4454
4587
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
4455
4588
|
const entry = args?.entry;
|
|
4456
4589
|
if (!entry) {
|
|
@@ -4483,8 +4616,8 @@ function postKody2Comment(target, issueNumber, state, next, cwd) {
|
|
|
4483
4616
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
4484
4617
|
const body = `@kody2 ${next}`;
|
|
4485
4618
|
try {
|
|
4486
|
-
|
|
4487
|
-
timeout:
|
|
4619
|
+
execFileSync17("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
4620
|
+
timeout: API_TIMEOUT_MS7,
|
|
4488
4621
|
cwd,
|
|
4489
4622
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4490
4623
|
});
|
|
@@ -4503,7 +4636,7 @@ function parsePr2(url) {
|
|
|
4503
4636
|
}
|
|
4504
4637
|
|
|
4505
4638
|
// src/scripts/syncFlow.ts
|
|
4506
|
-
import { execFileSync as
|
|
4639
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
4507
4640
|
var syncFlow = async (ctx) => {
|
|
4508
4641
|
ctx.skipAgent = true;
|
|
4509
4642
|
const prNumber = ctx.args.pr;
|
|
@@ -4562,7 +4695,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
4562
4695
|
}
|
|
4563
4696
|
function revParseHead(cwd) {
|
|
4564
4697
|
try {
|
|
4565
|
-
return
|
|
4698
|
+
return execFileSync18("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
4566
4699
|
} catch {
|
|
4567
4700
|
return "";
|
|
4568
4701
|
}
|
|
@@ -4570,9 +4703,9 @@ function revParseHead(cwd) {
|
|
|
4570
4703
|
function pushBranch(branch, cwd) {
|
|
4571
4704
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
4572
4705
|
try {
|
|
4573
|
-
|
|
4706
|
+
execFileSync18("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
4574
4707
|
} catch {
|
|
4575
|
-
|
|
4708
|
+
execFileSync18("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
4576
4709
|
cwd,
|
|
4577
4710
|
env,
|
|
4578
4711
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4790,7 +4923,8 @@ var preflightScripts = {
|
|
|
4790
4923
|
resolvePreviewUrl,
|
|
4791
4924
|
composePrompt,
|
|
4792
4925
|
setLifecycleLabel,
|
|
4793
|
-
skipAgent
|
|
4926
|
+
skipAgent,
|
|
4927
|
+
classifyByLabel
|
|
4794
4928
|
};
|
|
4795
4929
|
var postflightScripts = {
|
|
4796
4930
|
parseAgentResult: parseAgentResult2,
|
|
@@ -4812,7 +4946,8 @@ var postflightScripts = {
|
|
|
4812
4946
|
dispatch,
|
|
4813
4947
|
finishFlow,
|
|
4814
4948
|
advanceFlow,
|
|
4815
|
-
persistFlowState
|
|
4949
|
+
persistFlowState,
|
|
4950
|
+
postClassification
|
|
4816
4951
|
};
|
|
4817
4952
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
4818
4953
|
...Object.keys(preflightScripts),
|
|
@@ -4820,7 +4955,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
4820
4955
|
]);
|
|
4821
4956
|
|
|
4822
4957
|
// src/tools.ts
|
|
4823
|
-
import { execFileSync as
|
|
4958
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
4824
4959
|
function verifyCliTools(tools, cwd) {
|
|
4825
4960
|
const out = [];
|
|
4826
4961
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -4853,7 +4988,7 @@ function verifyOne(tool, cwd) {
|
|
|
4853
4988
|
}
|
|
4854
4989
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
4855
4990
|
try {
|
|
4856
|
-
|
|
4991
|
+
execFileSync19("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
4857
4992
|
return true;
|
|
4858
4993
|
} catch {
|
|
4859
4994
|
return false;
|
|
@@ -5183,7 +5318,7 @@ function detectPackageManager2(cwd) {
|
|
|
5183
5318
|
}
|
|
5184
5319
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
5185
5320
|
try {
|
|
5186
|
-
|
|
5321
|
+
execFileSync20(cmd, args, {
|
|
5187
5322
|
cwd,
|
|
5188
5323
|
stdio: stream ? "inherit" : "pipe",
|
|
5189
5324
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -5196,7 +5331,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
5196
5331
|
}
|
|
5197
5332
|
function isOnPath(bin) {
|
|
5198
5333
|
try {
|
|
5199
|
-
|
|
5334
|
+
execFileSync20("which", [bin], { stdio: "pipe" });
|
|
5200
5335
|
return true;
|
|
5201
5336
|
} catch {
|
|
5202
5337
|
return false;
|
|
@@ -5230,7 +5365,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5230
5365
|
} catch {
|
|
5231
5366
|
}
|
|
5232
5367
|
try {
|
|
5233
|
-
|
|
5368
|
+
execFileSync20("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
5234
5369
|
process.stdout.write("\u2192 kody2: litellm already installed\n");
|
|
5235
5370
|
return 0;
|
|
5236
5371
|
} catch {
|
|
@@ -5240,16 +5375,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5240
5375
|
}
|
|
5241
5376
|
function configureGitIdentity(cwd) {
|
|
5242
5377
|
try {
|
|
5243
|
-
const name =
|
|
5378
|
+
const name = execFileSync20("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
5244
5379
|
if (name) return;
|
|
5245
5380
|
} catch {
|
|
5246
5381
|
}
|
|
5247
5382
|
try {
|
|
5248
|
-
|
|
5383
|
+
execFileSync20("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
5249
5384
|
} catch {
|
|
5250
5385
|
}
|
|
5251
5386
|
try {
|
|
5252
|
-
|
|
5387
|
+
execFileSync20("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
5253
5388
|
cwd,
|
|
5254
5389
|
stdio: "pipe"
|
|
5255
5390
|
});
|
|
@@ -5426,9 +5561,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
5426
5561
|
if (paths.length === 0) return;
|
|
5427
5562
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
5428
5563
|
try {
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5564
|
+
execFileSync21("git", ["add", ...paths], opts);
|
|
5565
|
+
execFileSync21("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
5566
|
+
execFileSync21("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
5432
5567
|
} catch (err) {
|
|
5433
5568
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5434
5569
|
process.stderr.write(`[kody2:chat] commit/push skipped: ${msg}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "classify",
|
|
3
|
+
"role": "primitive",
|
|
4
|
+
"describe": "Classify an issue into one of {feature, bug, spec, chore} and dispatch the matching sub-orchestrator. Label-first fast path; LLM fallback when labels don't decide.",
|
|
5
|
+
"inputs": [
|
|
6
|
+
{
|
|
7
|
+
"name": "issue",
|
|
8
|
+
"flag": "--issue",
|
|
9
|
+
"type": "int",
|
|
10
|
+
"required": true,
|
|
11
|
+
"describe": "GitHub issue number to classify."
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"claudeCode": {
|
|
15
|
+
"model": "inherit",
|
|
16
|
+
"permissionMode": "default",
|
|
17
|
+
"maxTurns": null,
|
|
18
|
+
"maxThinkingTokens": null,
|
|
19
|
+
"systemPromptAppend": null,
|
|
20
|
+
"tools": [
|
|
21
|
+
"Read",
|
|
22
|
+
"Bash"
|
|
23
|
+
],
|
|
24
|
+
"hooks": [],
|
|
25
|
+
"skills": [],
|
|
26
|
+
"commands": [],
|
|
27
|
+
"subagents": [],
|
|
28
|
+
"plugins": [],
|
|
29
|
+
"mcpServers": []
|
|
30
|
+
},
|
|
31
|
+
"cliTools": [],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"preflight": [
|
|
34
|
+
{
|
|
35
|
+
"script": "setLifecycleLabel",
|
|
36
|
+
"with": {
|
|
37
|
+
"label": "kody:classifying",
|
|
38
|
+
"color": "0e8a16",
|
|
39
|
+
"description": "kody2: classifying the issue"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{ "script": "loadIssueContext" },
|
|
43
|
+
{ "script": "loadTaskState" },
|
|
44
|
+
{ "script": "classifyByLabel" },
|
|
45
|
+
{ "script": "loadConventions" },
|
|
46
|
+
{ "script": "composePrompt" }
|
|
47
|
+
],
|
|
48
|
+
"postflight": [
|
|
49
|
+
{ "script": "parseAgentResult" },
|
|
50
|
+
{ "script": "postClassification" },
|
|
51
|
+
{ "script": "writeRunSummary" },
|
|
52
|
+
{ "script": "saveTaskState" }
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"output": {
|
|
56
|
+
"actionTypes": [
|
|
57
|
+
"CLASSIFIED_AS_FEATURE",
|
|
58
|
+
"CLASSIFIED_AS_BUG",
|
|
59
|
+
"CLASSIFIED_AS_SPEC",
|
|
60
|
+
"CLASSIFIED_AS_CHORE",
|
|
61
|
+
"CLASSIFY_FAILED"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
You are Kody's issue-triage classifier. Your only job: read the issue below and pick ONE of the four flow types.
|
|
2
|
+
|
|
3
|
+
# Repo
|
|
4
|
+
{{repoOwner}}/{{repoName}}, default branch `{{defaultBranch}}`
|
|
5
|
+
|
|
6
|
+
# Issue #{{issue.number}}: {{issue.title}}
|
|
7
|
+
|
|
8
|
+
Labels: {{issue.labelsFormatted}}
|
|
9
|
+
|
|
10
|
+
{{issue.body}}
|
|
11
|
+
|
|
12
|
+
Recent comments (most recent first, truncated):
|
|
13
|
+
{{issue.commentsFormatted}}
|
|
14
|
+
|
|
15
|
+
{{conventionsBlock}}
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Classification rubric
|
|
20
|
+
|
|
21
|
+
Pick **exactly one** of:
|
|
22
|
+
|
|
23
|
+
- **feature** — new user-facing capability, refactor, performance work, or anything where scope is not fully known up front. Multi-file change likely. Use when the issue opens a design space (even if small).
|
|
24
|
+
- **bug** — fix broken behavior, enhancement to existing feature, or any targeted change where the scope is localized and well understood. Skip research; go straight to plan.
|
|
25
|
+
- **spec** — produce a design doc, RFC, architecture proposal, or exploration artifact. No code changes. Terminates at the plan artifact.
|
|
26
|
+
- **chore** — trivial maintenance: docs tweak, dep bump, lint fix, README update. No planning needed.
|
|
27
|
+
|
|
28
|
+
**If the issue ASKS for an RFC / design doc / spec / analysis with no implementation → `spec`.** Beats everything else.
|
|
29
|
+
**If the issue is plainly "fix X" or "add tiny Y to existing Z" with clear boundaries → `bug`.**
|
|
30
|
+
**If the issue is "tweak config / bump dep / fix typo" with no real design choice → `chore`.**
|
|
31
|
+
**Otherwise → `feature`.**
|
|
32
|
+
|
|
33
|
+
# Required output
|
|
34
|
+
|
|
35
|
+
Your FINAL message must be exactly this shape (no extra text before or after):
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
DONE
|
|
39
|
+
COMMIT_MSG: classify: <classification>
|
|
40
|
+
PR_SUMMARY:
|
|
41
|
+
classification: <feature|bug|spec|chore>
|
|
42
|
+
reason: <one sentence explaining the pick, grounded in the issue text>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
# Rules
|
|
46
|
+
|
|
47
|
+
- Read-only. Do NOT modify any file. Do NOT run git or gh.
|
|
48
|
+
- Output `FAILED: <reason>` if the issue is incoherent or ambiguous beyond the rubric.
|
|
49
|
+
- Do not over-think. This is triage, not analysis.
|
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.55",
|
|
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",
|