@kody-ade/kody-engine 0.4.232 → 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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +206 -4
  2. 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.232",
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,6 +6530,7 @@ 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";
@@ -6363,6 +6542,17 @@ var init_advanceManagedGoal = __esm({
6363
6542
  if (previousGoalIdFact === void 0) delete managed.facts.goalId;
6364
6543
  else managed.facts.goalId = previousGoalIdFact;
6365
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
+ }
6366
6556
  if (isDutyCadenceGoal(managed, goal.raw.extra)) {
6367
6557
  const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
6368
6558
  const decision2 = await planGoalDutySchedule({
@@ -16554,15 +16744,23 @@ async function runCi(argv) {
16554
16744
  const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
16555
16745
  let manualWorkflowDispatch = false;
16556
16746
  let forceRunAction = null;
16747
+ let forceRunCliArgs = {};
16557
16748
  if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs42.existsSync(dispatchEventPath)) {
16558
16749
  try {
16559
16750
  const evt = JSON.parse(fs42.readFileSync(dispatchEventPath, "utf-8"));
16560
16751
  const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
16561
16752
  const sessionInput = String(evt?.inputs?.sessionId ?? "");
16562
16753
  const dutyInput = String(evt?.inputs?.duty ?? evt?.inputs?.executable ?? "").trim();
16754
+ const messageInput = String(evt?.inputs?.message ?? "").trim();
16563
16755
  const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
16564
- if (noTarget && dutyInput) forceRunAction = dutyInput;
16565
- else manualWorkflowDispatch = noTarget;
16756
+ if (noTarget && dutyInput) {
16757
+ forceRunAction = dutyInput;
16758
+ if (dutyInput === "goal-manager" && messageInput) {
16759
+ forceRunCliArgs = { goal: messageInput };
16760
+ }
16761
+ } else {
16762
+ manualWorkflowDispatch = noTarget;
16763
+ }
16566
16764
  } catch {
16567
16765
  manualWorkflowDispatch = false;
16568
16766
  }
@@ -16575,6 +16773,10 @@ async function runCi(argv) {
16575
16773
  `);
16576
16774
  return 64;
16577
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
+ }
16578
16780
  process.stdout.write(`\u2192 kody: manual one-shot run of duty action ${route.action} (${route.duty})
16579
16781
 
16580
16782
  `);
@@ -16611,7 +16813,7 @@ async function runCi(argv) {
16611
16813
  action: route.action,
16612
16814
  duty: route.duty,
16613
16815
  executable: route.executable,
16614
- cliArgs: route.cliArgs,
16816
+ cliArgs: { ...route.cliArgs, ...forceRunCliArgs },
16615
16817
  flavor: "instant",
16616
16818
  force: true
16617
16819
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.232",
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
+ }