@kody-ade/kody-engine 0.2.50 → 0.2.52
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.md +34 -9
- package/dist/bin/kody2.js +254 -88
- package/dist/executables/{orchestrator-plan-build-review → bug}/profile.json +18 -13
- package/dist/executables/fix/profile.json +8 -0
- package/dist/executables/fix-ci/profile.json +8 -0
- package/dist/executables/plan/profile.json +8 -0
- package/dist/executables/research/profile.json +8 -0
- package/dist/executables/resolve/profile.json +8 -0
- package/dist/executables/review/profile.json +8 -0
- package/dist/executables/run/profile.json +8 -0
- package/dist/executables/sync/profile.json +8 -0
- package/dist/executables/ui-review/profile.json +1 -0
- package/package.json +1 -1
- /package/dist/executables/{orchestrator-plan-build-review → bug}/prompt.md +0 -0
package/README.md
CHANGED
|
@@ -31,17 +31,42 @@ Every top-level command is its own auto-discovered executable (`run`, `fix`, `fi
|
|
|
31
31
|
## Commands
|
|
32
32
|
|
|
33
33
|
```
|
|
34
|
-
|
|
35
|
-
kody2
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
kody2
|
|
39
|
-
kody2
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
kody2
|
|
34
|
+
# issue-triggered, agent writes code
|
|
35
|
+
kody2 run --issue <N> # implement an issue end-to-end (branch, code, PR)
|
|
36
|
+
|
|
37
|
+
# issue-triggered, agent read-only (no commits)
|
|
38
|
+
kody2 plan --issue <N> # produce a plan artifact for run
|
|
39
|
+
kody2 research --issue <N> # map repo context, surface questions/gaps
|
|
40
|
+
|
|
41
|
+
# PR-triggered, agent writes code
|
|
42
|
+
kody2 fix --pr <N> [--feedback ...] # apply PR review feedback
|
|
43
|
+
kody2 fix-ci --pr <N> [--run-id <ID>] # fix failing CI
|
|
44
|
+
kody2 resolve --pr <N> # merge default branch in, resolve conflicts
|
|
45
|
+
|
|
46
|
+
# PR-triggered, agent read-only
|
|
47
|
+
kody2 review --pr <N> # structured diff review (fast, diff only)
|
|
48
|
+
kody2 ui-review --pr <N> [--preview-url <URL>] # UI/UX review — browses preview via Playwright
|
|
49
|
+
|
|
50
|
+
# no agent (deterministic)
|
|
51
|
+
kody2 sync --pr <N> # merge default branch into PR branch
|
|
52
|
+
kody2 release --mode prepare|finalize [--bump patch|minor|major] [--dry-run]
|
|
53
|
+
kody2 init [--force] # scaffold consumer repo
|
|
54
|
+
kody2 orchestrate --issue <N> [--flow plan-build-review] # chain plan → run → review → fix
|
|
55
|
+
|
|
56
|
+
# engine entrypoints
|
|
57
|
+
kody2 ci --issue <N> # CI preflight + auto-dispatch from GHA event
|
|
58
|
+
kody2 chat [--session <id>] # dashboard-driven chat session
|
|
43
59
|
```
|
|
44
60
|
|
|
61
|
+
### `ui-review`
|
|
62
|
+
|
|
63
|
+
`ui-review` adds UI/UX verification to the review surface. It runs the usual diff-based review AND drives the running preview deployment via the Playwright CLI — writing a throwaway spec under `.kody2/ui-review/`, running it, capturing screenshots, and folding the observed behavior into the review verdict.
|
|
64
|
+
|
|
65
|
+
- Preview URL resolution: `--preview-url` flag → `$PREVIEW_URL` → `http://localhost:3000`.
|
|
66
|
+
- Credentials: committed in `.kody2/qa-guide.md` (scaffolded by `kody2 init` when a UI is detected, with `CHANGE_ME` placeholders). The agent reads the guide and uses any credentials it finds.
|
|
67
|
+
- Auto-discovery: routes, roles, login page, admin path, Payload CMS collections, API routes, env vars — fed to the agent so it knows *what* to browse without you spelling it out.
|
|
68
|
+
- Falls back to a diff-only review when the preview URL is unreachable.
|
|
69
|
+
|
|
45
70
|
`kody2 chat` reads `.kody/sessions/<id>.jsonl`, runs one agent turn, appends
|
|
46
71
|
the reply, and writes `chat.message` + `chat.done` events to
|
|
47
72
|
`.kody/events/<id>.jsonl` (plus optional HTTP push to a dashboard ingest URL).
|
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.52",
|
|
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",
|
|
@@ -593,24 +593,12 @@ function autoDispatch(opts) {
|
|
|
593
593
|
if (!sub) {
|
|
594
594
|
return asDispatch(defaultExec, targetNum);
|
|
595
595
|
}
|
|
596
|
-
if (sub === "orchestrate" || sub === "orchestrator") {
|
|
597
|
-
const flow = extractFlowName(afterTag);
|
|
598
|
-
if (flow) {
|
|
599
|
-
return {
|
|
600
|
-
executable: `orchestrator-${flow}`,
|
|
601
|
-
cliArgs: { issue: targetNum, flow },
|
|
602
|
-
target: targetNum
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
return {
|
|
606
|
-
executable: "orchestrator-plan-build-review",
|
|
607
|
-
cliArgs: { issue: targetNum, flow: "plan-build-review" },
|
|
608
|
-
target: targetNum
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
596
|
if (sub === "build") {
|
|
612
597
|
return { executable: "run", cliArgs: { issue: targetNum }, target: targetNum };
|
|
613
598
|
}
|
|
599
|
+
if (sub === "orchestrate" || sub === "orchestrator") {
|
|
600
|
+
return { executable: "bug", cliArgs: { issue: targetNum }, target: targetNum };
|
|
601
|
+
}
|
|
614
602
|
return asDispatch(sub, targetNum);
|
|
615
603
|
}
|
|
616
604
|
function asDispatch(executable, target) {
|
|
@@ -626,10 +614,6 @@ function extractSubcommand(afterTag) {
|
|
|
626
614
|
if (!match) return null;
|
|
627
615
|
return match[1];
|
|
628
616
|
}
|
|
629
|
-
function extractFlowName(afterTag) {
|
|
630
|
-
const match = afterTag.match(/--flow[=\s]+([a-z][a-z0-9-]{0,60})/);
|
|
631
|
-
return match ? match[1] : null;
|
|
632
|
-
}
|
|
633
617
|
function extractFeedback(afterTag) {
|
|
634
618
|
const cleaned = afterTag.replace(/^(fix|please|kindly)(?:[\s:,.-]+|$)/i, "").trim();
|
|
635
619
|
return cleaned.length > 0 ? cleaned : void 0;
|
|
@@ -1209,7 +1193,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1209
1193
|
);
|
|
1210
1194
|
}
|
|
1211
1195
|
}
|
|
1212
|
-
const body =
|
|
1196
|
+
const body = `@kody2 ${flow.name}`;
|
|
1213
1197
|
try {
|
|
1214
1198
|
execFileSync3("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
|
|
1215
1199
|
timeout: API_TIMEOUT_MS2,
|
|
@@ -2651,6 +2635,188 @@ function computeFailureReason(ctx) {
|
|
|
2651
2635
|
|
|
2652
2636
|
// src/scripts/finishFlow.ts
|
|
2653
2637
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
2638
|
+
|
|
2639
|
+
// src/registry.ts
|
|
2640
|
+
import * as fs14 from "fs";
|
|
2641
|
+
import * as path13 from "path";
|
|
2642
|
+
function getExecutablesRoot() {
|
|
2643
|
+
const here = path13.dirname(new URL(import.meta.url).pathname);
|
|
2644
|
+
const candidates = [
|
|
2645
|
+
path13.join(here, "executables"),
|
|
2646
|
+
// dev: src/
|
|
2647
|
+
path13.join(here, "..", "executables"),
|
|
2648
|
+
// built: dist/bin → dist/executables
|
|
2649
|
+
path13.join(here, "..", "src", "executables")
|
|
2650
|
+
// fallback
|
|
2651
|
+
];
|
|
2652
|
+
for (const c of candidates) {
|
|
2653
|
+
if (fs14.existsSync(c) && fs14.statSync(c).isDirectory()) return c;
|
|
2654
|
+
}
|
|
2655
|
+
return candidates[0];
|
|
2656
|
+
}
|
|
2657
|
+
function listExecutables(root = getExecutablesRoot()) {
|
|
2658
|
+
if (!fs14.existsSync(root)) return [];
|
|
2659
|
+
const entries = fs14.readdirSync(root, { withFileTypes: true });
|
|
2660
|
+
const out = [];
|
|
2661
|
+
for (const ent of entries) {
|
|
2662
|
+
if (!ent.isDirectory()) continue;
|
|
2663
|
+
const profilePath = path13.join(root, ent.name, "profile.json");
|
|
2664
|
+
if (fs14.existsSync(profilePath) && fs14.statSync(profilePath).isFile()) {
|
|
2665
|
+
out.push({ name: ent.name, profilePath });
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
2669
|
+
}
|
|
2670
|
+
function hasExecutable(name, root = getExecutablesRoot()) {
|
|
2671
|
+
if (!isSafeName(name)) return false;
|
|
2672
|
+
const profilePath = path13.join(root, name, "profile.json");
|
|
2673
|
+
return fs14.existsSync(profilePath) && fs14.statSync(profilePath).isFile();
|
|
2674
|
+
}
|
|
2675
|
+
function isSafeName(name) {
|
|
2676
|
+
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2677
|
+
}
|
|
2678
|
+
function parseGenericFlags(argv) {
|
|
2679
|
+
const args = {};
|
|
2680
|
+
const positional = [];
|
|
2681
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2682
|
+
const arg = argv[i];
|
|
2683
|
+
if (!arg.startsWith("--")) {
|
|
2684
|
+
positional.push(arg);
|
|
2685
|
+
continue;
|
|
2686
|
+
}
|
|
2687
|
+
const key = arg.slice(2);
|
|
2688
|
+
const next = argv[i + 1];
|
|
2689
|
+
const value = next !== void 0 && !next.startsWith("--") ? (i++, next) : true;
|
|
2690
|
+
args[key] = value;
|
|
2691
|
+
if (key.includes("-")) {
|
|
2692
|
+
const camel = key.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2693
|
+
if (camel !== key && args[camel] === void 0) args[camel] = value;
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
if (positional.length > 0) args._ = positional;
|
|
2697
|
+
return args;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// src/lifecycleLabels.ts
|
|
2701
|
+
var KODY_LABEL_PREFIX = "kody:";
|
|
2702
|
+
function collectProfileLabels() {
|
|
2703
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
2704
|
+
for (const exe of listExecutables()) {
|
|
2705
|
+
let profile;
|
|
2706
|
+
try {
|
|
2707
|
+
profile = loadProfile(exe.profilePath);
|
|
2708
|
+
} catch {
|
|
2709
|
+
continue;
|
|
2710
|
+
}
|
|
2711
|
+
for (const entry of [...profile.scripts.preflight, ...profile.scripts.postflight]) {
|
|
2712
|
+
const spec = extractLabelSpec(entry);
|
|
2713
|
+
if (spec) byLabel.set(spec.label, spec);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
return [...byLabel.values()];
|
|
2717
|
+
}
|
|
2718
|
+
function extractLabelSpec(entry) {
|
|
2719
|
+
const w = entry.with;
|
|
2720
|
+
if (!w) return null;
|
|
2721
|
+
const label = typeof w.label === "string" ? w.label : null;
|
|
2722
|
+
if (!label || !label.startsWith(KODY_LABEL_PREFIX)) return null;
|
|
2723
|
+
return {
|
|
2724
|
+
label,
|
|
2725
|
+
color: typeof w.color === "string" ? w.color : void 0,
|
|
2726
|
+
description: typeof w.description === "string" ? w.description : void 0
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
function ensureLabels(cwd) {
|
|
2730
|
+
const result = { created: [], failed: [] };
|
|
2731
|
+
for (const spec of collectProfileLabels()) {
|
|
2732
|
+
try {
|
|
2733
|
+
createLabelInRepo(spec, cwd);
|
|
2734
|
+
result.created.push(spec.label);
|
|
2735
|
+
} catch (err) {
|
|
2736
|
+
result.failed.push({ label: spec.label, reason: errMsg(err) });
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
return result;
|
|
2740
|
+
}
|
|
2741
|
+
function getIssueLabels(issueNumber, cwd) {
|
|
2742
|
+
try {
|
|
2743
|
+
const output = gh2(
|
|
2744
|
+
["issue", "view", String(issueNumber), "--json", "labels", "--jq", ".labels[].name"],
|
|
2745
|
+
{ cwd }
|
|
2746
|
+
);
|
|
2747
|
+
return output.split("\n").filter(Boolean);
|
|
2748
|
+
} catch {
|
|
2749
|
+
return [];
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
function addLabel(issueNumber, label, cwd) {
|
|
2753
|
+
gh2(["issue", "edit", String(issueNumber), "--add-label", label], { cwd });
|
|
2754
|
+
}
|
|
2755
|
+
function removeLabel(issueNumber, label, cwd) {
|
|
2756
|
+
try {
|
|
2757
|
+
gh2(["issue", "edit", String(issueNumber), "--remove-label", label], { cwd });
|
|
2758
|
+
} catch {
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
function createLabelInRepo(spec, cwd) {
|
|
2762
|
+
const args = ["label", "create", spec.label, "--force"];
|
|
2763
|
+
if (spec.color) args.push("--color", spec.color);
|
|
2764
|
+
if (spec.description) args.push("--description", spec.description);
|
|
2765
|
+
gh2(args, { cwd });
|
|
2766
|
+
}
|
|
2767
|
+
function setKodyLabel(issueNumber, spec, cwd) {
|
|
2768
|
+
const target = spec.label;
|
|
2769
|
+
if (!target.startsWith(KODY_LABEL_PREFIX)) {
|
|
2770
|
+
process.stderr.write(
|
|
2771
|
+
`[kody2] setKodyLabel: refusing to set non-kody label "${target}"
|
|
2772
|
+
`
|
|
2773
|
+
);
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
const present = getIssueLabels(issueNumber, cwd);
|
|
2777
|
+
for (const label of present) {
|
|
2778
|
+
if (label !== target && label.startsWith(KODY_LABEL_PREFIX)) {
|
|
2779
|
+
removeLabel(issueNumber, label, cwd);
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
try {
|
|
2783
|
+
addLabel(issueNumber, target, cwd);
|
|
2784
|
+
} catch (err) {
|
|
2785
|
+
if (looksLikeMissingLabel(err)) {
|
|
2786
|
+
try {
|
|
2787
|
+
createLabelInRepo(spec, cwd);
|
|
2788
|
+
addLabel(issueNumber, target, cwd);
|
|
2789
|
+
return;
|
|
2790
|
+
} catch (retryErr) {
|
|
2791
|
+
process.stderr.write(
|
|
2792
|
+
`[kody2] setKodyLabel: create+retry failed for ${target} on #${issueNumber}: ${errMsg(retryErr)}
|
|
2793
|
+
`
|
|
2794
|
+
);
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
process.stderr.write(
|
|
2799
|
+
`[kody2] setKodyLabel: failed to add ${target} on #${issueNumber}: ${errMsg(err)}
|
|
2800
|
+
`
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
function looksLikeMissingLabel(err) {
|
|
2805
|
+
const msg = errMsg(err).toLowerCase();
|
|
2806
|
+
return msg.includes("not found") || msg.includes("could not add label") || msg.includes("could not resolve to a label");
|
|
2807
|
+
}
|
|
2808
|
+
function errMsg(err) {
|
|
2809
|
+
if (err instanceof Error) return err.message;
|
|
2810
|
+
if (typeof err === "object" && err !== null) {
|
|
2811
|
+
const e = err;
|
|
2812
|
+
const stderr = e.stderr?.toString().trim();
|
|
2813
|
+
if (stderr) return stderr;
|
|
2814
|
+
if (e.message) return e.message;
|
|
2815
|
+
}
|
|
2816
|
+
return String(err);
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
// src/scripts/finishFlow.ts
|
|
2654
2820
|
var API_TIMEOUT_MS5 = 3e4;
|
|
2655
2821
|
var STATUS_ICON = {
|
|
2656
2822
|
"review-passed": "\u2705",
|
|
@@ -2665,6 +2831,18 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
2665
2831
|
const flowName = state?.flow?.name ?? "(unknown flow)";
|
|
2666
2832
|
if (state) state.flow = void 0;
|
|
2667
2833
|
if (!issueNumber) return;
|
|
2834
|
+
const label = typeof args?.label === "string" ? args.label : void 0;
|
|
2835
|
+
if (label && label.startsWith(KODY_LABEL_PREFIX)) {
|
|
2836
|
+
setKodyLabel(
|
|
2837
|
+
issueNumber,
|
|
2838
|
+
{
|
|
2839
|
+
label,
|
|
2840
|
+
color: typeof args?.color === "string" ? args.color : void 0,
|
|
2841
|
+
description: typeof args?.description === "string" ? args.description : void 0
|
|
2842
|
+
},
|
|
2843
|
+
ctx.cwd
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2668
2846
|
const icon = STATUS_ICON[reason] ?? "\u2139\uFE0F";
|
|
2669
2847
|
const prSuffix = state?.core.prUrl ? `
|
|
2670
2848
|
|
|
@@ -2788,7 +2966,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
2788
2966
|
|
|
2789
2967
|
// src/gha.ts
|
|
2790
2968
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
2791
|
-
import * as
|
|
2969
|
+
import * as fs15 from "fs";
|
|
2792
2970
|
function getRunUrl() {
|
|
2793
2971
|
const server = process.env.GITHUB_SERVER_URL;
|
|
2794
2972
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -2799,10 +2977,10 @@ function getRunUrl() {
|
|
|
2799
2977
|
function reactToTriggerComment(cwd) {
|
|
2800
2978
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
2801
2979
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2802
|
-
if (!eventPath || !
|
|
2980
|
+
if (!eventPath || !fs15.existsSync(eventPath)) return;
|
|
2803
2981
|
let event = null;
|
|
2804
2982
|
try {
|
|
2805
|
-
event = JSON.parse(
|
|
2983
|
+
event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
|
|
2806
2984
|
} catch {
|
|
2807
2985
|
return;
|
|
2808
2986
|
}
|
|
@@ -3045,67 +3223,6 @@ import { execFileSync as execFileSync13 } from "child_process";
|
|
|
3045
3223
|
import * as fs17 from "fs";
|
|
3046
3224
|
import * as path15 from "path";
|
|
3047
3225
|
|
|
3048
|
-
// src/registry.ts
|
|
3049
|
-
import * as fs15 from "fs";
|
|
3050
|
-
import * as path13 from "path";
|
|
3051
|
-
function getExecutablesRoot() {
|
|
3052
|
-
const here = path13.dirname(new URL(import.meta.url).pathname);
|
|
3053
|
-
const candidates = [
|
|
3054
|
-
path13.join(here, "executables"),
|
|
3055
|
-
// dev: src/
|
|
3056
|
-
path13.join(here, "..", "executables"),
|
|
3057
|
-
// built: dist/bin → dist/executables
|
|
3058
|
-
path13.join(here, "..", "src", "executables")
|
|
3059
|
-
// fallback
|
|
3060
|
-
];
|
|
3061
|
-
for (const c of candidates) {
|
|
3062
|
-
if (fs15.existsSync(c) && fs15.statSync(c).isDirectory()) return c;
|
|
3063
|
-
}
|
|
3064
|
-
return candidates[0];
|
|
3065
|
-
}
|
|
3066
|
-
function listExecutables(root = getExecutablesRoot()) {
|
|
3067
|
-
if (!fs15.existsSync(root)) return [];
|
|
3068
|
-
const entries = fs15.readdirSync(root, { withFileTypes: true });
|
|
3069
|
-
const out = [];
|
|
3070
|
-
for (const ent of entries) {
|
|
3071
|
-
if (!ent.isDirectory()) continue;
|
|
3072
|
-
const profilePath = path13.join(root, ent.name, "profile.json");
|
|
3073
|
-
if (fs15.existsSync(profilePath) && fs15.statSync(profilePath).isFile()) {
|
|
3074
|
-
out.push({ name: ent.name, profilePath });
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
3078
|
-
}
|
|
3079
|
-
function hasExecutable(name, root = getExecutablesRoot()) {
|
|
3080
|
-
if (!isSafeName(name)) return false;
|
|
3081
|
-
const profilePath = path13.join(root, name, "profile.json");
|
|
3082
|
-
return fs15.existsSync(profilePath) && fs15.statSync(profilePath).isFile();
|
|
3083
|
-
}
|
|
3084
|
-
function isSafeName(name) {
|
|
3085
|
-
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
3086
|
-
}
|
|
3087
|
-
function parseGenericFlags(argv) {
|
|
3088
|
-
const args = {};
|
|
3089
|
-
const positional = [];
|
|
3090
|
-
for (let i = 0; i < argv.length; i++) {
|
|
3091
|
-
const arg = argv[i];
|
|
3092
|
-
if (!arg.startsWith("--")) {
|
|
3093
|
-
positional.push(arg);
|
|
3094
|
-
continue;
|
|
3095
|
-
}
|
|
3096
|
-
const key = arg.slice(2);
|
|
3097
|
-
const next = argv[i + 1];
|
|
3098
|
-
const value = next !== void 0 && !next.startsWith("--") ? (i++, next) : true;
|
|
3099
|
-
args[key] = value;
|
|
3100
|
-
if (key.includes("-")) {
|
|
3101
|
-
const camel = key.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
3102
|
-
if (camel !== key && args[camel] === void 0) args[camel] = value;
|
|
3103
|
-
}
|
|
3104
|
-
}
|
|
3105
|
-
if (positional.length > 0) args._ = positional;
|
|
3106
|
-
return args;
|
|
3107
|
-
}
|
|
3108
|
-
|
|
3109
3226
|
// src/scripts/loadQaGuide.ts
|
|
3110
3227
|
import * as fs16 from "fs";
|
|
3111
3228
|
import * as path14 from "path";
|
|
@@ -3297,7 +3414,13 @@ function performInit(cwd, force) {
|
|
|
3297
3414
|
fs17.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
3298
3415
|
wrote.push(`.github/workflows/kody2-${exe.name}.yml`);
|
|
3299
3416
|
}
|
|
3300
|
-
|
|
3417
|
+
let labels;
|
|
3418
|
+
try {
|
|
3419
|
+
labels = ensureLabels(cwd);
|
|
3420
|
+
} catch {
|
|
3421
|
+
labels = void 0;
|
|
3422
|
+
}
|
|
3423
|
+
return { wrote, skipped, labels };
|
|
3301
3424
|
}
|
|
3302
3425
|
function renderScheduledWorkflow(name, cron) {
|
|
3303
3426
|
return `# Scheduled kody2 executable: ${name}
|
|
@@ -3334,12 +3457,24 @@ jobs:
|
|
|
3334
3457
|
var initFlow = async (ctx) => {
|
|
3335
3458
|
const force = ctx.args.force === true;
|
|
3336
3459
|
const cwd = ctx.cwd;
|
|
3337
|
-
const { wrote, skipped } = performInit(cwd, force);
|
|
3460
|
+
const { wrote, skipped, labels } = performInit(cwd, force);
|
|
3338
3461
|
process.stdout.write("\u2192 kody2 init\n");
|
|
3339
3462
|
for (const f of wrote) process.stdout.write(` wrote ${f}
|
|
3340
3463
|
`);
|
|
3341
3464
|
for (const f of skipped) process.stdout.write(` skipped ${f} (already exists; pass --force to overwrite)
|
|
3342
3465
|
`);
|
|
3466
|
+
if (labels) {
|
|
3467
|
+
if (labels.created.length > 0) {
|
|
3468
|
+
process.stdout.write(` labels ensured ${labels.created.length} lifecycle label(s)
|
|
3469
|
+
`);
|
|
3470
|
+
}
|
|
3471
|
+
if (labels.failed.length > 0) {
|
|
3472
|
+
process.stdout.write(
|
|
3473
|
+
` labels ${labels.failed.length} failed (gh auth missing? will self-heal on first run)
|
|
3474
|
+
`
|
|
3475
|
+
);
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3343
3478
|
process.stdout.write(
|
|
3344
3479
|
wrote.length > 0 ? `
|
|
3345
3480
|
Done. Edit kody.config.json to pick your model, then push the workflow file.
|
|
@@ -3368,7 +3503,7 @@ var loadCoverageRules = async (ctx) => {
|
|
|
3368
3503
|
|
|
3369
3504
|
// src/scripts/loadIssueContext.ts
|
|
3370
3505
|
var DEFAULT_COMMENT_LIMIT = 12;
|
|
3371
|
-
var DEFAULT_COMMENT_MAX_BYTES =
|
|
3506
|
+
var DEFAULT_COMMENT_MAX_BYTES = 16e3;
|
|
3372
3507
|
var loadIssueContext = async (ctx) => {
|
|
3373
3508
|
const issueNumber = ctx.args.issue;
|
|
3374
3509
|
if (typeof issueNumber !== "number" || issueNumber <= 0) {
|
|
@@ -4261,6 +4396,36 @@ function synthesizeAction(ctx) {
|
|
|
4261
4396
|
};
|
|
4262
4397
|
}
|
|
4263
4398
|
|
|
4399
|
+
// src/scripts/setLifecycleLabel.ts
|
|
4400
|
+
var setLifecycleLabel = async (ctx, _profile, args) => {
|
|
4401
|
+
const label = args?.label;
|
|
4402
|
+
if (typeof label !== "string" || !label.startsWith(KODY_LABEL_PREFIX)) {
|
|
4403
|
+
process.stderr.write(
|
|
4404
|
+
`[kody2] setLifecycleLabel: missing or invalid "label" arg (must start with "${KODY_LABEL_PREFIX}"): ${String(label)}
|
|
4405
|
+
`
|
|
4406
|
+
);
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4409
|
+
const issueNumber = resolveTargetNumber(ctx.args);
|
|
4410
|
+
if (issueNumber === void 0) return;
|
|
4411
|
+
setKodyLabel(
|
|
4412
|
+
issueNumber,
|
|
4413
|
+
{
|
|
4414
|
+
label,
|
|
4415
|
+
color: typeof args?.color === "string" ? args.color : void 0,
|
|
4416
|
+
description: typeof args?.description === "string" ? args.description : void 0
|
|
4417
|
+
},
|
|
4418
|
+
ctx.cwd
|
|
4419
|
+
);
|
|
4420
|
+
};
|
|
4421
|
+
function resolveTargetNumber(args) {
|
|
4422
|
+
const issue = args.issue;
|
|
4423
|
+
if (typeof issue === "number" && Number.isFinite(issue)) return issue;
|
|
4424
|
+
const pr = args.pr;
|
|
4425
|
+
if (typeof pr === "number" && Number.isFinite(pr)) return pr;
|
|
4426
|
+
return void 0;
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4264
4429
|
// src/scripts/skipAgent.ts
|
|
4265
4430
|
var skipAgent = async (ctx) => {
|
|
4266
4431
|
ctx.skipAgent = true;
|
|
@@ -4607,6 +4772,7 @@ var preflightScripts = {
|
|
|
4607
4772
|
discoverQaContext,
|
|
4608
4773
|
resolvePreviewUrl,
|
|
4609
4774
|
composePrompt,
|
|
4775
|
+
setLifecycleLabel,
|
|
4610
4776
|
skipAgent
|
|
4611
4777
|
};
|
|
4612
4778
|
var postflightScripts = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"describe": "
|
|
2
|
+
"name": "bug",
|
|
3
|
+
"describe": "Sub-orchestrator for bug / enhancement issues — plan → run → review (→ fix on concerns/fail). No agent — the postflight entries ARE the transition table, evaluated top-to-bottom via runWhen.",
|
|
4
4
|
"inputs": [
|
|
5
5
|
{
|
|
6
6
|
"name": "issue",
|
|
@@ -8,13 +8,6 @@
|
|
|
8
8
|
"type": "int",
|
|
9
9
|
"required": true,
|
|
10
10
|
"describe": "GitHub issue number to drive the flow on."
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"name": "flow",
|
|
14
|
-
"flag": "--flow",
|
|
15
|
-
"type": "string",
|
|
16
|
-
"required": false,
|
|
17
|
-
"describe": "Flow name (cosmetic — recorded in state.flow.name)."
|
|
18
11
|
}
|
|
19
12
|
],
|
|
20
13
|
"claudeCode": {
|
|
@@ -34,6 +27,14 @@
|
|
|
34
27
|
"cliTools": [],
|
|
35
28
|
"scripts": {
|
|
36
29
|
"preflight": [
|
|
30
|
+
{
|
|
31
|
+
"script": "setLifecycleLabel",
|
|
32
|
+
"with": {
|
|
33
|
+
"label": "kody:orchestrating",
|
|
34
|
+
"color": "1d76db",
|
|
35
|
+
"description": "kody2: orchestrating a multi-stage flow"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
37
38
|
{ "script": "loadIssueContext" },
|
|
38
39
|
{ "script": "loadTaskState" },
|
|
39
40
|
{ "script": "skipAgent" }
|
|
@@ -48,19 +49,23 @@
|
|
|
48
49
|
{ "script": "dispatch", "with": { "next": "review", "target": "pr" },
|
|
49
50
|
"runWhen": { "data.taskState.core.lastOutcome.type": "RUN_COMPLETED" } },
|
|
50
51
|
|
|
51
|
-
{ "script": "finishFlow",
|
|
52
|
+
{ "script": "finishFlow",
|
|
53
|
+
"with": { "reason": "review-passed", "label": "kody:done", "color": "0e8a16", "description": "kody2: PR ready for human review/merge" },
|
|
52
54
|
"runWhen": { "data.taskState.core.lastOutcome.type": "REVIEW_PASS" } },
|
|
53
55
|
|
|
54
56
|
{ "script": "dispatch", "with": { "next": "fix", "target": "pr" },
|
|
55
57
|
"runWhen": { "data.taskState.core.lastOutcome.type": ["REVIEW_CONCERNS", "REVIEW_FAIL"] } },
|
|
56
58
|
|
|
57
|
-
{ "script": "finishFlow",
|
|
59
|
+
{ "script": "finishFlow",
|
|
60
|
+
"with": { "reason": "review-failed", "label": "kody:failed", "color": "e11d21", "description": "kody2: flow failed" },
|
|
58
61
|
"runWhen": { "data.taskState.core.lastOutcome.type": "REVIEW_FAILED" } },
|
|
59
62
|
|
|
60
|
-
{ "script": "finishFlow",
|
|
63
|
+
{ "script": "finishFlow",
|
|
64
|
+
"with": { "reason": "fix-applied", "label": "kody:done", "color": "0e8a16", "description": "kody2: PR ready for human review/merge" },
|
|
61
65
|
"runWhen": { "data.taskState.core.lastOutcome.type": "FIX_COMPLETED" } },
|
|
62
66
|
|
|
63
|
-
{ "script": "finishFlow",
|
|
67
|
+
{ "script": "finishFlow",
|
|
68
|
+
"with": { "reason": "aborted", "label": "kody:failed", "color": "e11d21", "description": "kody2: flow failed" },
|
|
64
69
|
"runWhen": { "data.taskState.core.lastOutcome.type": ["PLAN_FAILED", "RUN_FAILED", "FIX_FAILED", "AGENT_NOT_RUN"] } },
|
|
65
70
|
|
|
66
71
|
{ "script": "persistFlowState" }
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
"cliTools": [],
|
|
41
41
|
"scripts": {
|
|
42
42
|
"preflight": [
|
|
43
|
+
{
|
|
44
|
+
"script": "setLifecycleLabel",
|
|
45
|
+
"with": {
|
|
46
|
+
"label": "kody:fixing",
|
|
47
|
+
"color": "e99695",
|
|
48
|
+
"description": "kody2: applying review feedback"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
43
51
|
{
|
|
44
52
|
"script": "fixFlow"
|
|
45
53
|
},
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
"cliTools": [],
|
|
41
41
|
"scripts": {
|
|
42
42
|
"preflight": [
|
|
43
|
+
{
|
|
44
|
+
"script": "setLifecycleLabel",
|
|
45
|
+
"with": {
|
|
46
|
+
"label": "kody:fixing",
|
|
47
|
+
"color": "e99695",
|
|
48
|
+
"description": "kody2: applying review feedback"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
43
51
|
{
|
|
44
52
|
"script": "fixCiFlow"
|
|
45
53
|
},
|
|
@@ -31,6 +31,14 @@
|
|
|
31
31
|
"cliTools": [],
|
|
32
32
|
"scripts": {
|
|
33
33
|
"preflight": [
|
|
34
|
+
{
|
|
35
|
+
"script": "setLifecycleLabel",
|
|
36
|
+
"with": {
|
|
37
|
+
"label": "kody:planning",
|
|
38
|
+
"color": "5319e7",
|
|
39
|
+
"description": "kody2: producing an implementation plan"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
34
42
|
{
|
|
35
43
|
"script": "loadIssueContext"
|
|
36
44
|
},
|
|
@@ -31,6 +31,14 @@
|
|
|
31
31
|
"cliTools": [],
|
|
32
32
|
"scripts": {
|
|
33
33
|
"preflight": [
|
|
34
|
+
{
|
|
35
|
+
"script": "setLifecycleLabel",
|
|
36
|
+
"with": {
|
|
37
|
+
"label": "kody:researching",
|
|
38
|
+
"color": "1d76db",
|
|
39
|
+
"description": "kody2: researching the issue"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
34
42
|
{
|
|
35
43
|
"script": "loadIssueContext"
|
|
36
44
|
},
|
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
"cliTools": [],
|
|
34
34
|
"scripts": {
|
|
35
35
|
"preflight": [
|
|
36
|
+
{
|
|
37
|
+
"script": "setLifecycleLabel",
|
|
38
|
+
"with": {
|
|
39
|
+
"label": "kody:resolving",
|
|
40
|
+
"color": "b60205",
|
|
41
|
+
"description": "kody2: resolving merge conflicts"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
36
44
|
{
|
|
37
45
|
"script": "resolveFlow"
|
|
38
46
|
},
|
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
"cliTools": [],
|
|
34
34
|
"scripts": {
|
|
35
35
|
"preflight": [
|
|
36
|
+
{
|
|
37
|
+
"script": "setLifecycleLabel",
|
|
38
|
+
"with": {
|
|
39
|
+
"label": "kody:running",
|
|
40
|
+
"color": "fbca04",
|
|
41
|
+
"description": "kody2: implementing the change"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
36
44
|
{
|
|
37
45
|
"script": "runFlow"
|
|
38
46
|
},
|
|
@@ -26,6 +26,14 @@
|
|
|
26
26
|
"cliTools": [],
|
|
27
27
|
"scripts": {
|
|
28
28
|
"preflight": [
|
|
29
|
+
{
|
|
30
|
+
"script": "setLifecycleLabel",
|
|
31
|
+
"with": {
|
|
32
|
+
"label": "kody:syncing",
|
|
33
|
+
"color": "c5def5",
|
|
34
|
+
"description": "kody2: syncing PR with base"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
29
37
|
{
|
|
30
38
|
"script": "syncFlow"
|
|
31
39
|
}
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"outputArtifacts": [],
|
|
60
60
|
"scripts": {
|
|
61
61
|
"preflight": [
|
|
62
|
+
{ "script": "setLifecycleLabel", "with": { "label": "kody:reviewing", "color": "d93f0b", "description": "kody2: reviewing a PR" } },
|
|
62
63
|
{ "script": "reviewFlow" },
|
|
63
64
|
{ "script": "loadTaskState" },
|
|
64
65
|
{ "script": "loadConventions" },
|
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.52",
|
|
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",
|
|
File without changes
|