@kody-ade/kody-engine 0.4.631 → 0.4.633

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 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.631",
18
+ version: "0.4.633",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
20
20
  license: "MIT",
21
21
  repository: {
@@ -3390,6 +3390,12 @@ function createStateBackendFromEnv(env = process.env, client) {
3390
3390
  updatedAt
3391
3391
  });
3392
3392
  },
3393
+ async listReports(tenantId2) {
3394
+ const result = await transport.query(anyApi.reports.list, {
3395
+ tenantId: requireTenant(tenantId2)
3396
+ });
3397
+ return Array.isArray(result) ? result : [];
3398
+ },
3393
3399
  async listIntents(tenantId2) {
3394
3400
  const result = await transport.query(anyApi.intents.list, { tenantId: requireTenant(tenantId2) });
3395
3401
  return Array.isArray(result) ? result : [];
@@ -3991,6 +3997,105 @@ function capabilityToolDefinitions(opts) {
3991
3997
  return { content: [{ type: "text", text: text2 }] };
3992
3998
  }
3993
3999
  };
4000
+ const readLatestReportTool = {
4001
+ name: "read_latest_report",
4002
+ description: "Read the newest persisted Kody Report for this repository. Optionally restrict to one stable report slug or reports newer than an ISO timestamp. Returns the Report body and metadata; use it as evidence before deciding whether work is needed.",
4003
+ inputSchema: {
4004
+ slug: z3.string().regex(/^[a-z0-9][a-z0-9_-]{0,79}$/).optional(),
4005
+ since: z3.string().datetime().optional()
4006
+ },
4007
+ handler: async (args) => {
4008
+ const slug = typeof args.slug === "string" ? args.slug : void 0;
4009
+ const since = typeof args.since === "string" ? args.since : void 0;
4010
+ const reports = await createStateBackendFromEnv().listReports(opts.repoSlug);
4011
+ const report = reports.filter((candidate) => (!slug || candidate.slug === slug) && (!since || candidate.updatedAt > since)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
4012
+ return {
4013
+ content: [
4014
+ {
4015
+ type: "text",
4016
+ text: JSON.stringify(
4017
+ report ? {
4018
+ found: true,
4019
+ slug: report.slug,
4020
+ runId: report.runId,
4021
+ title: report.title,
4022
+ body: report.body,
4023
+ meta: report.meta,
4024
+ updatedAt: report.updatedAt
4025
+ } : { found: false },
4026
+ null,
4027
+ 2
4028
+ )
4029
+ }
4030
+ ]
4031
+ };
4032
+ }
4033
+ };
4034
+ const reconcileTodoTool = {
4035
+ name: "reconcile_todo",
4036
+ description: "Idempotently create, update, close, or reopen one canonical repository Todo for a recurring problem. The stable slug and item id prevent duplicates. Repeating the same state is a no-op; unrelated items in an existing Todo are preserved.",
4037
+ inputSchema: {
4038
+ slug: z3.string().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/),
4039
+ itemId: z3.string().regex(/^[a-z0-9][a-z0-9_-]{0,79}$/).optional(),
4040
+ title: z3.string().min(1).max(160),
4041
+ description: z3.string().max(2e4).optional(),
4042
+ status: z3.enum(["open", "resolved"]),
4043
+ reportSlug: z3.string().regex(/^[a-z0-9][a-z0-9_-]{0,79}$/),
4044
+ reportRunId: z3.string().max(160).optional(),
4045
+ evidence: z3.string().max(2e4).optional()
4046
+ },
4047
+ handler: async (args) => {
4048
+ const slug = String(args.slug);
4049
+ const itemId = typeof args.itemId === "string" ? args.itemId : "finding";
4050
+ const title = String(args.title).trim();
4051
+ const description = typeof args.description === "string" ? args.description.trim() : "";
4052
+ const status = args.status === "resolved" ? "resolved" : "open";
4053
+ const reportSlug = String(args.reportSlug);
4054
+ const reportRunId = typeof args.reportRunId === "string" ? args.reportRunId : void 0;
4055
+ const evidence = typeof args.evidence === "string" ? args.evidence.trim() : "";
4056
+ const backend = createStateBackendFromEnv();
4057
+ const existing = await backend.getRepoDoc(opts.repoSlug, `todo:${slug}`);
4058
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4059
+ const current = existing?.doc && typeof existing.doc === "object" && !Array.isArray(existing.doc) ? existing.doc : {};
4060
+ const currentItems = Array.isArray(current.items) ? current.items.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
4061
+ const previous = currentItems.find((item) => item.id === itemId);
4062
+ const completed = status === "resolved";
4063
+ const previousMeta = previous?.meta && typeof previous.meta === "object" && !Array.isArray(previous.meta) ? previous.meta : {};
4064
+ const unchanged = Boolean(
4065
+ previous && previous.completed === completed && previous.title === title && String(previous.body ?? "") === evidence && previousMeta.reportSlug === reportSlug
4066
+ );
4067
+ if (unchanged) {
4068
+ return { content: [{ type: "text", text: JSON.stringify({ changed: false, slug, status }) }] };
4069
+ }
4070
+ const nextItem = {
4071
+ id: itemId,
4072
+ title,
4073
+ body: evidence,
4074
+ assignee: null,
4075
+ completed,
4076
+ createdAt: typeof previous?.createdAt === "string" ? previous.createdAt : now,
4077
+ completedAt: completed ? now : null,
4078
+ meta: {
4079
+ ...previousMeta,
4080
+ source: "live-agent",
4081
+ reportSlug,
4082
+ ...reportRunId ? { reportRunId } : {},
4083
+ status
4084
+ }
4085
+ };
4086
+ const items = previous ? currentItems.map((item) => item.id === itemId ? nextItem : item) : [...currentItems, nextItem];
4087
+ const doc = {
4088
+ ...current,
4089
+ version: 1,
4090
+ title,
4091
+ description,
4092
+ createdAt: typeof current.createdAt === "string" ? current.createdAt : now,
4093
+ items
4094
+ };
4095
+ await backend.saveRepoDoc(opts.repoSlug, `todo:${slug}`, doc, existing?.updatedAt);
4096
+ return { content: [{ type: "text", text: JSON.stringify({ changed: true, slug, status }) }] };
4097
+ }
4098
+ };
3994
4099
  const cmsTools = dashboardCmsToolDefinitions({
3995
4100
  repoSlug: opts.repoSlug,
3996
4101
  assertWriteAllowed: () => assertCmsWriteAllowed(opts)
@@ -4007,6 +4112,8 @@ function capabilityToolDefinitions(opts) {
4007
4112
  ensureIssueTool,
4008
4113
  ensureCommentTool,
4009
4114
  startCapabilityTool,
4115
+ readLatestReportTool,
4116
+ reconcileTodoTool,
4010
4117
  ...cmsTools
4011
4118
  ];
4012
4119
  }
@@ -4035,6 +4142,7 @@ var init_capabilityMcp = __esm({
4035
4142
  init_issue();
4036
4143
  init_registry();
4037
4144
  init_trustPolicy();
4145
+ init_state_backend();
4038
4146
  FAIL_CONCLUSIONS = /* @__PURE__ */ new Set(["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "STARTUP_FAILURE", "CANCELLED"]);
4039
4147
  RUNNING_STATUSES = /* @__PURE__ */ new Set(["IN_PROGRESS", "QUEUED", "PENDING", "WAITING", "REQUESTED"]);
4040
4148
  THREAD_BODY_MAX = 4e3;
@@ -4054,6 +4162,8 @@ var init_capabilityMcp = __esm({
4054
4162
  "ensure_issue",
4055
4163
  "ensure_comment",
4056
4164
  "start_capability",
4165
+ "read_latest_report",
4166
+ "reconcile_todo",
4057
4167
  ...DASHBOARD_CMS_MCP_TOOL_NAMES
4058
4168
  ];
4059
4169
  }
@@ -15966,13 +16076,15 @@ function defaultBranchFromGit(cwd) {
15966
16076
  }
15967
16077
  }
15968
16078
  }
15969
- function performInit(cwd, force) {
16079
+ function performInit(cwd, force, workflowOnly = false) {
15970
16080
  const wrote = [];
15971
16081
  const skipped = [];
15972
16082
  const ownerRepo = detectOwnerRepo(cwd);
15973
16083
  const defaultBranch = defaultBranchFromGit(cwd);
15974
16084
  const configPath = path39.join(cwd, "kody.config.json");
15975
- if (fs40.existsSync(configPath) && !force) {
16085
+ if (workflowOnly) {
16086
+ skipped.push("kody.config.json");
16087
+ } else if (fs40.existsSync(configPath) && !force) {
15976
16088
  skipped.push("kody.config.json");
15977
16089
  } else {
15978
16090
  const cfg = makeConfig(cwd, ownerRepo, defaultBranch);
@@ -16007,8 +16119,9 @@ var init_initFlow = __esm({
16007
16119
  init_workflow_template();
16008
16120
  initFlow = async (ctx) => {
16009
16121
  const force = ctx.args.force === true;
16122
+ const workflowOnly = ctx.args.workflowOnly === true;
16010
16123
  const cwd = ctx.cwd;
16011
- const { wrote, skipped, labels } = performInit(cwd, force);
16124
+ const { wrote, skipped, labels } = performInit(cwd, force || workflowOnly, workflowOnly);
16012
16125
  process.stdout.write("\u2192 kody-engine init\n");
16013
16126
  for (const f of wrote) process.stdout.write(` wrote ${f}
16014
16127
  `);
@@ -16695,7 +16808,7 @@ var init_loadLiveAgent = __esm({
16695
16808
  }
16696
16809
  };
16697
16810
  ctx.data.jobStateJson = JSON.stringify(state, null, 2);
16698
- ctx.data.capabilityTools = ["start_capability"];
16811
+ ctx.data.capabilityTools = ["start_capability", "read_latest_report", "reconcile_todo"];
16699
16812
  ctx.data.capabilityToolMode = "lock";
16700
16813
  profile.claudeCode.enableSubmitTool = true;
16701
16814
  };
@@ -9,6 +9,13 @@
9
9
  "type": "bool",
10
10
  "required": false,
11
11
  "describe": "Overwrite existing generated files instead of skipping them."
12
+ },
13
+ {
14
+ "name": "workflowOnly",
15
+ "flag": "--workflow-only",
16
+ "type": "bool",
17
+ "required": false,
18
+ "describe": "Refresh only the generated GitHub Actions launcher and preserve kody.config.json."
12
19
  }
13
20
  ],
14
21
  "claudeCode": {
@@ -13,7 +13,12 @@
13
13
  "maxThinkingTokens": null,
14
14
  "systemPromptAppend": null,
15
15
  "enableSubmitTool": true,
16
- "tools": ["mcp__kody-capability__start_capability", "mcp__kody-submit__submit_state"],
16
+ "tools": [
17
+ "mcp__kody-capability__start_capability",
18
+ "mcp__kody-capability__read_latest_report",
19
+ "mcp__kody-capability__reconcile_todo",
20
+ "mcp__kody-submit__submit_state"
21
+ ],
17
22
  "hooks": [], "skills": [], "commands": [], "subagents": [], "plugins": [], "mcpServers": []
18
23
  },
19
24
  "cliTools": [],
@@ -30,7 +30,9 @@ You are a persistent Kody Agent completing one scheduled cycle.
30
30
  {{jobStateJson}}
31
31
  ```
32
32
 
33
- Inspect current conditions, decide the best next action toward the Intent, and use only assigned capabilities. Do not create work merely to appear active. If waiting is correct, record what is being awaited.
33
+ Inspect current conditions, decide the best next action toward the Intent, and use only assigned capabilities. A dispatched capability may finish after this cycle: record its run, then inspect its Report on a later cycle. Treat the Report as evidence; you make the decision.
34
+
35
+ For an actionable recurring problem, reconcile one stable Todo whose slug identifies the problem. Reuse and update that Todo while the problem remains open, close it when a later Report proves recovery, and reopen the same Todo if the problem returns. Do not create work merely to appear active. If the newest Report was already handled or waiting is correct, make no change and record what is being awaited.
34
36
 
35
37
  As your final action, call `submit_state` exactly once with:
36
38
  - `cursor`: the next continuation cursor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.631",
3
+ "version": "0.4.633",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "repository": {