@kody-ade/kody-engine 0.4.231 → 0.4.233
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/kody.js +214 -4
- package/package.json +22 -23
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.233",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -5723,6 +5723,132 @@ var init_state2 = __esm({
|
|
|
5723
5723
|
}
|
|
5724
5724
|
});
|
|
5725
5725
|
|
|
5726
|
+
// src/goal/typeDefinitions.ts
|
|
5727
|
+
function cloneRoute(route) {
|
|
5728
|
+
return route.map((step) => ({
|
|
5729
|
+
stage: step.stage,
|
|
5730
|
+
evidence: step.evidence,
|
|
5731
|
+
duty: step.duty,
|
|
5732
|
+
...step.executable ? { executable: step.executable } : {},
|
|
5733
|
+
...step.args ? { args: structuredClone(step.args) } : {}
|
|
5734
|
+
}));
|
|
5735
|
+
}
|
|
5736
|
+
function stringArray(value) {
|
|
5737
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : null;
|
|
5738
|
+
}
|
|
5739
|
+
function routeArray(value) {
|
|
5740
|
+
if (!Array.isArray(value)) return null;
|
|
5741
|
+
const route = [];
|
|
5742
|
+
for (const item of value) {
|
|
5743
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return null;
|
|
5744
|
+
const raw = item;
|
|
5745
|
+
if (typeof raw.stage !== "string" || typeof raw.evidence !== "string" || typeof raw.duty !== "string") return null;
|
|
5746
|
+
route.push({
|
|
5747
|
+
stage: raw.stage,
|
|
5748
|
+
evidence: raw.evidence,
|
|
5749
|
+
duty: raw.duty,
|
|
5750
|
+
executable: typeof raw.executable === "string" ? raw.executable : void 0,
|
|
5751
|
+
args: raw.args && typeof raw.args === "object" && !Array.isArray(raw.args) ? { ...raw.args } : void 0
|
|
5752
|
+
});
|
|
5753
|
+
}
|
|
5754
|
+
return route;
|
|
5755
|
+
}
|
|
5756
|
+
function managedGoalTypeDefinition(type) {
|
|
5757
|
+
return Object.prototype.hasOwnProperty.call(GOAL_TYPE_DEFINITIONS, type) ? GOAL_TYPE_DEFINITIONS[type] : null;
|
|
5758
|
+
}
|
|
5759
|
+
function expandManagedGoalState(state) {
|
|
5760
|
+
const type = typeof state.extra.type === "string" ? state.extra.type : "";
|
|
5761
|
+
const definition = managedGoalTypeDefinition(type);
|
|
5762
|
+
if (!definition) return state;
|
|
5763
|
+
const destination = state.extra.destination && typeof state.extra.destination === "object" && !Array.isArray(state.extra.destination) ? { ...state.extra.destination } : {};
|
|
5764
|
+
const outcome = typeof destination.outcome === "string" ? destination.outcome : "";
|
|
5765
|
+
const evidence = stringArray(destination.evidence);
|
|
5766
|
+
const duties = stringArray(state.extra.duties);
|
|
5767
|
+
const route = routeArray(state.extra.route);
|
|
5768
|
+
const facts = state.extra.facts && typeof state.extra.facts === "object" && !Array.isArray(state.extra.facts) ? { ...state.extra.facts } : {};
|
|
5769
|
+
const blockers = stringArray(state.extra.blockers);
|
|
5770
|
+
return {
|
|
5771
|
+
...state,
|
|
5772
|
+
extra: {
|
|
5773
|
+
...state.extra,
|
|
5774
|
+
type: definition.type,
|
|
5775
|
+
destination: {
|
|
5776
|
+
...destination,
|
|
5777
|
+
outcome,
|
|
5778
|
+
evidence: evidence && evidence.length > 0 ? evidence : [...definition.evidence]
|
|
5779
|
+
},
|
|
5780
|
+
duties: duties && duties.length > 0 ? duties : [...definition.duties],
|
|
5781
|
+
route: route && route.length > 0 ? route : cloneRoute(definition.route),
|
|
5782
|
+
facts,
|
|
5783
|
+
blockers: blockers ?? []
|
|
5784
|
+
}
|
|
5785
|
+
};
|
|
5786
|
+
}
|
|
5787
|
+
var GOAL_TYPE_DEFINITIONS;
|
|
5788
|
+
var init_typeDefinitions = __esm({
|
|
5789
|
+
"src/goal/typeDefinitions.ts"() {
|
|
5790
|
+
"use strict";
|
|
5791
|
+
GOAL_TYPE_DEFINITIONS = {
|
|
5792
|
+
improve: {
|
|
5793
|
+
type: "improve",
|
|
5794
|
+
evidence: ["planReady", "changeImplemented", "changeVerified"],
|
|
5795
|
+
duties: ["plan", "fix", "review"],
|
|
5796
|
+
route: [
|
|
5797
|
+
{ stage: "plan", evidence: "planReady", duty: "plan", executable: "plan" },
|
|
5798
|
+
{ stage: "implement", evidence: "changeImplemented", duty: "fix", executable: "fix" },
|
|
5799
|
+
{ stage: "review", evidence: "changeVerified", duty: "review", executable: "review" }
|
|
5800
|
+
]
|
|
5801
|
+
},
|
|
5802
|
+
maintain: {
|
|
5803
|
+
type: "maintain",
|
|
5804
|
+
evidence: [],
|
|
5805
|
+
duties: ["cleanup", "code-health", "docs-health", "documentation-maintenance", "memory-compaction", "repo-graph", "skills-research"],
|
|
5806
|
+
route: []
|
|
5807
|
+
},
|
|
5808
|
+
monitor: {
|
|
5809
|
+
type: "monitor",
|
|
5810
|
+
evidence: [],
|
|
5811
|
+
duties: ["health-check", "pr-health-triage", "qa-sweep"],
|
|
5812
|
+
route: []
|
|
5813
|
+
},
|
|
5814
|
+
release: {
|
|
5815
|
+
type: "release",
|
|
5816
|
+
evidence: ["releasePrExists", "mainMerged", "productionDeployed"],
|
|
5817
|
+
duties: ["release", "release-merge", "vercel-production-deploy"],
|
|
5818
|
+
route: [
|
|
5819
|
+
{
|
|
5820
|
+
stage: "release",
|
|
5821
|
+
evidence: "releasePrExists",
|
|
5822
|
+
duty: "release",
|
|
5823
|
+
executable: "release-prepare",
|
|
5824
|
+
args: { issue: { fact: "issue" }, goal: { fact: "goalId" } }
|
|
5825
|
+
},
|
|
5826
|
+
{
|
|
5827
|
+
stage: "merge",
|
|
5828
|
+
evidence: "mainMerged",
|
|
5829
|
+
duty: "release-merge",
|
|
5830
|
+
executable: "release-merge",
|
|
5831
|
+
args: { pr: { fact: "releasePr" }, issue: { fact: "issue" }, goal: { fact: "goalId" } }
|
|
5832
|
+
},
|
|
5833
|
+
{
|
|
5834
|
+
stage: "publish",
|
|
5835
|
+
evidence: "productionDeployed",
|
|
5836
|
+
duty: "vercel-production-deploy",
|
|
5837
|
+
executable: "vercel-production-deploy",
|
|
5838
|
+
args: { goal: { fact: "goalId" } }
|
|
5839
|
+
}
|
|
5840
|
+
]
|
|
5841
|
+
},
|
|
5842
|
+
checklist: {
|
|
5843
|
+
type: "checklist",
|
|
5844
|
+
evidence: ["checklistComplete"],
|
|
5845
|
+
duties: ["task-verifier"],
|
|
5846
|
+
route: [{ stage: "verify", evidence: "checklistComplete", duty: "task-verifier", executable: "task-verifier" }]
|
|
5847
|
+
}
|
|
5848
|
+
};
|
|
5849
|
+
}
|
|
5850
|
+
});
|
|
5851
|
+
|
|
5726
5852
|
// src/stateBranch.ts
|
|
5727
5853
|
function is404(err) {
|
|
5728
5854
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6335,12 +6461,64 @@ function readSimpleGoalTaskSummary(goalId, cwd) {
|
|
|
6335
6461
|
const open = issues.filter((issue) => String(issue.state ?? "").toLowerCase() === "open").length;
|
|
6336
6462
|
return { total, open };
|
|
6337
6463
|
}
|
|
6464
|
+
function ensureIssueFactIfNeeded(goal, goalId, cwd) {
|
|
6465
|
+
if (!routeNeedsIssueFact(goal)) return;
|
|
6466
|
+
const existing = normalizeIssueNumber(goal.facts.issue);
|
|
6467
|
+
if (existing !== null) {
|
|
6468
|
+
goal.facts.issue = existing;
|
|
6469
|
+
return;
|
|
6470
|
+
}
|
|
6471
|
+
goal.facts.issue = findExistingGoalIssue(goalId, cwd) ?? createGoalIssue(goal, goalId, cwd);
|
|
6472
|
+
}
|
|
6473
|
+
function routeNeedsIssueFact(goal) {
|
|
6474
|
+
return goal.route.some(
|
|
6475
|
+
(step) => Object.values(step.args ?? {}).some((value) => {
|
|
6476
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
6477
|
+
const record = value;
|
|
6478
|
+
return Object.keys(record).length === 1 && record.fact === "issue";
|
|
6479
|
+
})
|
|
6480
|
+
);
|
|
6481
|
+
}
|
|
6482
|
+
function normalizeIssueNumber(value) {
|
|
6483
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
|
|
6484
|
+
if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
|
|
6485
|
+
return null;
|
|
6486
|
+
}
|
|
6487
|
+
function goalIssueMarker(goalId) {
|
|
6488
|
+
return `<!-- kody-managed-goal: ${goalId} -->`;
|
|
6489
|
+
}
|
|
6490
|
+
function findExistingGoalIssue(goalId, cwd) {
|
|
6491
|
+
const marker = goalIssueMarker(goalId);
|
|
6492
|
+
const raw = gh(["issue", "list", "--state", "all", "--limit", "100", "--json", "number,body"], { cwd });
|
|
6493
|
+
const issues = JSON.parse(raw);
|
|
6494
|
+
const match = issues.find((issue) => typeof issue.number === "number" && issue.body?.includes(marker));
|
|
6495
|
+
return match?.number ?? null;
|
|
6496
|
+
}
|
|
6497
|
+
function createGoalIssue(goal, goalId, cwd) {
|
|
6498
|
+
const prefix = goal.type === "release" ? "Release" : "Goal";
|
|
6499
|
+
const outcome = goal.destination.outcome.trim() || goalId;
|
|
6500
|
+
const title = `${prefix}: ${outcome}`.slice(0, 120);
|
|
6501
|
+
const body = [
|
|
6502
|
+
`Managed goal: \`${goalId}\``,
|
|
6503
|
+
"",
|
|
6504
|
+
`Finish line: ${outcome}`,
|
|
6505
|
+
"",
|
|
6506
|
+
"This issue was created by Kody so goal duties that require an issue can run end to end.",
|
|
6507
|
+
"",
|
|
6508
|
+
goalIssueMarker(goalId)
|
|
6509
|
+
].join("\n");
|
|
6510
|
+
const out = gh(["issue", "create", "--title", title, "--body-file", "-"], { input: body, cwd });
|
|
6511
|
+
const match = out.match(/\/issues\/(\d+)(?:[/?#]|$)/);
|
|
6512
|
+
if (!match) throw new Error(`gh issue create returned unexpected output: ${out}`);
|
|
6513
|
+
return Number(match[1]);
|
|
6514
|
+
}
|
|
6338
6515
|
var advanceManagedGoal;
|
|
6339
6516
|
var init_advanceManagedGoal = __esm({
|
|
6340
6517
|
"src/scripts/advanceManagedGoal.ts"() {
|
|
6341
6518
|
"use strict";
|
|
6342
6519
|
init_manager();
|
|
6343
6520
|
init_state2();
|
|
6521
|
+
init_typeDefinitions();
|
|
6344
6522
|
init_issue();
|
|
6345
6523
|
init_goalDutyScheduling();
|
|
6346
6524
|
advanceManagedGoal = async (ctx) => {
|
|
@@ -6352,11 +6530,29 @@ var init_advanceManagedGoal = __esm({
|
|
|
6352
6530
|
return;
|
|
6353
6531
|
}
|
|
6354
6532
|
ctx.data.goalOriginalStateText = serializeGoalState(goal.raw);
|
|
6533
|
+
goal.raw = expandManagedGoalState(goal.raw);
|
|
6355
6534
|
const managed = managedGoalFromState(goal.raw);
|
|
6356
6535
|
if (!managed) {
|
|
6357
6536
|
ctx.output.reason = "goal has no managed-goal contract; nothing to advance";
|
|
6358
6537
|
return;
|
|
6359
6538
|
}
|
|
6539
|
+
const previousGoalIdFact = managed.facts.goalId;
|
|
6540
|
+
managed.facts.goalId = goal.id;
|
|
6541
|
+
const restoreGoalIdFact = () => {
|
|
6542
|
+
if (previousGoalIdFact === void 0) delete managed.facts.goalId;
|
|
6543
|
+
else managed.facts.goalId = previousGoalIdFact;
|
|
6544
|
+
};
|
|
6545
|
+
try {
|
|
6546
|
+
ensureIssueFactIfNeeded(managed, goal.id, ctx.cwd);
|
|
6547
|
+
} catch (err) {
|
|
6548
|
+
const reason = `failed to prepare goal issue fact: ${err instanceof Error ? err.message : String(err)}`;
|
|
6549
|
+
managed.stage = "blocked";
|
|
6550
|
+
if (!managed.blockers.includes(reason)) managed.blockers.push(reason);
|
|
6551
|
+
restoreGoalIdFact();
|
|
6552
|
+
goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
|
|
6553
|
+
ctx.output.reason = reason;
|
|
6554
|
+
return;
|
|
6555
|
+
}
|
|
6360
6556
|
if (isDutyCadenceGoal(managed, goal.raw.extra)) {
|
|
6361
6557
|
const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
|
|
6362
6558
|
const decision2 = await planGoalDutySchedule({
|
|
@@ -6365,6 +6561,7 @@ var init_advanceManagedGoal = __esm({
|
|
|
6365
6561
|
config: ctx.config,
|
|
6366
6562
|
previousScheduleState
|
|
6367
6563
|
});
|
|
6564
|
+
restoreGoalIdFact();
|
|
6368
6565
|
goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
|
|
6369
6566
|
goal.raw.extra.scheduleState = decision2.scheduleState;
|
|
6370
6567
|
ctx.data.managedGoalDecision = decision2;
|
|
@@ -6382,6 +6579,7 @@ var init_advanceManagedGoal = __esm({
|
|
|
6382
6579
|
applySimpleGoalTaskSummary(managed, readSimpleGoalTaskSummary(goal.id, ctx.cwd));
|
|
6383
6580
|
}
|
|
6384
6581
|
const decision = planManagedGoalTick(managed);
|
|
6582
|
+
restoreGoalIdFact();
|
|
6385
6583
|
ctx.data.managedGoalDecision = decision;
|
|
6386
6584
|
if (decision.kind === "done") {
|
|
6387
6585
|
goal.state = "done";
|
|
@@ -16546,15 +16744,23 @@ async function runCi(argv) {
|
|
|
16546
16744
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
16547
16745
|
let manualWorkflowDispatch = false;
|
|
16548
16746
|
let forceRunAction = null;
|
|
16747
|
+
let forceRunCliArgs = {};
|
|
16549
16748
|
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs42.existsSync(dispatchEventPath)) {
|
|
16550
16749
|
try {
|
|
16551
16750
|
const evt = JSON.parse(fs42.readFileSync(dispatchEventPath, "utf-8"));
|
|
16552
16751
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
16553
16752
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
16554
16753
|
const dutyInput = String(evt?.inputs?.duty ?? evt?.inputs?.executable ?? "").trim();
|
|
16754
|
+
const messageInput = String(evt?.inputs?.message ?? "").trim();
|
|
16555
16755
|
const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
16556
|
-
if (noTarget && dutyInput)
|
|
16557
|
-
|
|
16756
|
+
if (noTarget && dutyInput) {
|
|
16757
|
+
forceRunAction = dutyInput;
|
|
16758
|
+
if (dutyInput === "goal-manager" && messageInput) {
|
|
16759
|
+
forceRunCliArgs = { goal: messageInput };
|
|
16760
|
+
}
|
|
16761
|
+
} else {
|
|
16762
|
+
manualWorkflowDispatch = noTarget;
|
|
16763
|
+
}
|
|
16558
16764
|
} catch {
|
|
16559
16765
|
manualWorkflowDispatch = false;
|
|
16560
16766
|
}
|
|
@@ -16567,6 +16773,10 @@ async function runCi(argv) {
|
|
|
16567
16773
|
`);
|
|
16568
16774
|
return 64;
|
|
16569
16775
|
}
|
|
16776
|
+
if (route.executable === "goal-manager" && typeof forceRunCliArgs.goal !== "string") {
|
|
16777
|
+
process.stderr.write("[kody] manual goal-manager run requires message goal id\n");
|
|
16778
|
+
return 64;
|
|
16779
|
+
}
|
|
16570
16780
|
process.stdout.write(`\u2192 kody: manual one-shot run of duty action ${route.action} (${route.duty})
|
|
16571
16781
|
|
|
16572
16782
|
`);
|
|
@@ -16603,7 +16813,7 @@ async function runCi(argv) {
|
|
|
16603
16813
|
action: route.action,
|
|
16604
16814
|
duty: route.duty,
|
|
16605
16815
|
executable: route.executable,
|
|
16606
|
-
cliArgs: route.cliArgs,
|
|
16816
|
+
cliArgs: { ...route.cliArgs, ...forceRunCliArgs },
|
|
16607
16817
|
flavor: "instant",
|
|
16608
16818
|
force: true
|
|
16609
16819
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.233",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,26 +12,6 @@
|
|
|
12
12
|
"templates",
|
|
13
13
|
"kody.config.schema.json"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"kody:run": "tsx bin/kody.ts",
|
|
17
|
-
"serve": "tsx bin/kody.ts serve",
|
|
18
|
-
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
19
|
-
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
20
|
-
"build": "tsup && node scripts/copy-assets.cjs",
|
|
21
|
-
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
22
|
-
"pretest": "pnpm check:modularity",
|
|
23
|
-
"test": "vitest run tests/unit tests/int --coverage",
|
|
24
|
-
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
25
|
-
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
26
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
27
|
-
"test:all": "vitest run tests --no-coverage",
|
|
28
|
-
"typecheck": "tsc --noEmit",
|
|
29
|
-
"lint": "biome check",
|
|
30
|
-
"lint:fix": "biome check --write",
|
|
31
|
-
"format": "biome format --write",
|
|
32
|
-
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
|
|
33
|
-
"prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build"
|
|
34
|
-
},
|
|
35
15
|
"dependencies": {
|
|
36
16
|
"@actions/cache": "^6.0.0",
|
|
37
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
@@ -55,5 +35,24 @@
|
|
|
55
35
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
56
36
|
},
|
|
57
37
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
58
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
59
|
-
|
|
38
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"kody:run": "tsx bin/kody.ts",
|
|
41
|
+
"serve": "tsx bin/kody.ts serve",
|
|
42
|
+
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
43
|
+
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
44
|
+
"build": "tsup && node scripts/copy-assets.cjs",
|
|
45
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
46
|
+
"pretest": "pnpm check:modularity",
|
|
47
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
48
|
+
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
49
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
50
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
51
|
+
"test:all": "vitest run tests --no-coverage",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"lint": "biome check",
|
|
54
|
+
"lint:fix": "biome check --write",
|
|
55
|
+
"format": "biome format --write",
|
|
56
|
+
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
|
|
57
|
+
}
|
|
58
|
+
}
|