@kody-ade/kody-engine 0.4.270 → 0.4.272

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 +223 -16
  2. package/package.json +1 -1
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.270",
18
+ version: "0.4.272",
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",
@@ -2146,15 +2146,7 @@ function dispatchWorkflow(workflowFile, capability, issueNumber, repoSlug) {
2146
2146
  }
2147
2147
  }
2148
2148
  try {
2149
- gh([
2150
- "workflow",
2151
- "run",
2152
- workflowFile,
2153
- "-f",
2154
- `capability=${capability}`,
2155
- "-f",
2156
- `issue_number=${issueNumber}`
2157
- ]);
2149
+ gh(["workflow", "run", workflowFile, "-f", `capability=${capability}`, "-f", `issue_number=${issueNumber}`]);
2158
2150
  return { ok: true };
2159
2151
  } catch (err) {
2160
2152
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
@@ -2189,6 +2181,132 @@ function isDispatchGated(capability, mode) {
2189
2181
  function trustRefusal(capabilitySlug) {
2190
2182
  return `Not dispatched: capability \`${capabilitySlug ?? "?"}\` is in ASK mode (not trusted for autonomy). Do NOT retry the dispatch. Instead notify the operator (use recommend_to_operator, or rely on the tracking issue that already @-mentions them), then submit_state. To let this capability act on its own, grant it Auto on the dashboard Trust page.`;
2191
2183
  }
2184
+ function dashboardBaseUrl() {
2185
+ const raw = process.env.KODY_CMS_DASHBOARD_URL?.trim() || process.env.KODY_DASHBOARD_URL?.trim() || process.env.DASHBOARD_URL?.trim() || "";
2186
+ if (!raw) return null;
2187
+ try {
2188
+ const parsed = new URL(raw);
2189
+ return parsed.origin;
2190
+ } catch {
2191
+ return null;
2192
+ }
2193
+ }
2194
+ function dashboardCmsToken() {
2195
+ return process.env.KODY_CMS_TOKEN?.trim() || process.env.KODY_DASHBOARD_TOKEN?.trim() || process.env.KODY_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || process.env.GITHUB_TOKEN?.trim() || process.env.GH_PAT?.trim() || null;
2196
+ }
2197
+ function cmsHeaders(opts) {
2198
+ const token = dashboardCmsToken();
2199
+ const [owner, repo] = opts.repoSlug.split("/");
2200
+ if (!token) {
2201
+ return {
2202
+ ok: false,
2203
+ error: "missing_cms_token",
2204
+ message: "Set KODY_CMS_TOKEN, KODY_DASHBOARD_TOKEN, KODY_TOKEN, GH_TOKEN, GITHUB_TOKEN, or GH_PAT."
2205
+ };
2206
+ }
2207
+ if (!owner || !repo) {
2208
+ return { ok: false, error: "invalid_repo", message: `Invalid repo slug: ${opts.repoSlug}` };
2209
+ }
2210
+ return {
2211
+ ok: true,
2212
+ headers: {
2213
+ "Content-Type": "application/json",
2214
+ "x-kody-token": token,
2215
+ "x-kody-owner": owner,
2216
+ "x-kody-repo": repo
2217
+ }
2218
+ };
2219
+ }
2220
+ async function callDashboardCms(opts, path49, init = {}) {
2221
+ const baseUrl = dashboardBaseUrl();
2222
+ if (!baseUrl) {
2223
+ return {
2224
+ ok: false,
2225
+ error: "missing_dashboard_url",
2226
+ message: "Set KODY_CMS_DASHBOARD_URL or KODY_DASHBOARD_URL to the Dashboard origin."
2227
+ };
2228
+ }
2229
+ const headerResult = cmsHeaders(opts);
2230
+ if (!headerResult.ok) return headerResult;
2231
+ try {
2232
+ const res = await fetch(`${baseUrl}${path49}`, {
2233
+ ...init,
2234
+ headers: {
2235
+ ...headerResult.headers,
2236
+ ...init.headers
2237
+ }
2238
+ });
2239
+ const contentType = res.headers.get("content-type") ?? "";
2240
+ const data = contentType.includes("application/json") ? await res.json().catch(() => null) : await res.text().catch(() => "");
2241
+ if (!res.ok) {
2242
+ return {
2243
+ ok: false,
2244
+ status: res.status,
2245
+ error: stringFieldFromRecord(data, "error") ?? "cms_request_failed",
2246
+ message: stringFieldFromRecord(data, "message") ?? `Dashboard CMS request failed with ${res.status}.`,
2247
+ data
2248
+ };
2249
+ }
2250
+ return { ok: true, status: res.status, data };
2251
+ } catch (err) {
2252
+ return {
2253
+ ok: false,
2254
+ error: "cms_request_error",
2255
+ message: err instanceof Error ? err.message : String(err)
2256
+ };
2257
+ }
2258
+ }
2259
+ function cmsToolResponse(result) {
2260
+ return {
2261
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
2262
+ ...result.ok ? {} : { isError: true }
2263
+ };
2264
+ }
2265
+ function assertCmsWriteAllowed(opts) {
2266
+ if (isDispatchGated(opts.capabilitySlug, readCapabilityTrustMode(opts.state, opts.repoSlug, opts.capabilitySlug))) {
2267
+ return trustRefusal(opts.capabilitySlug);
2268
+ }
2269
+ return null;
2270
+ }
2271
+ function cmsQuery(args) {
2272
+ const params = new URLSearchParams();
2273
+ const q = stringArg(args.q);
2274
+ if (q) params.set("q", q);
2275
+ const limit = numberArg(args.limit);
2276
+ if (limit !== void 0) params.set("limit", String(limit));
2277
+ const offset = numberArg(args.offset);
2278
+ if (offset !== void 0) params.set("offset", String(offset));
2279
+ if (args.filters && typeof args.filters === "object" && !Array.isArray(args.filters)) {
2280
+ params.set("filters", JSON.stringify(args.filters));
2281
+ }
2282
+ if (Array.isArray(args.sort)) {
2283
+ const sort = args.sort.flatMap((entry) => {
2284
+ if (!entry || typeof entry !== "object") return [];
2285
+ const field = stringArg(entry.field);
2286
+ if (!field) return [];
2287
+ const direction = entry.direction === "asc" ? "asc" : "desc";
2288
+ return [`${field}:${direction}`];
2289
+ }).join(",");
2290
+ if (sort) params.set("sort", sort);
2291
+ }
2292
+ const value = params.toString();
2293
+ return value ? `?${value}` : "";
2294
+ }
2295
+ function stringArg(value) {
2296
+ return typeof value === "string" ? value.trim() : "";
2297
+ }
2298
+ function numberArg(value) {
2299
+ if (value === void 0 || value === null || value === "") return void 0;
2300
+ const n = Number(value);
2301
+ return Number.isFinite(n) ? n : void 0;
2302
+ }
2303
+ function documentArg(value) {
2304
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2305
+ return value;
2306
+ }
2307
+ function stringFieldFromRecord(value, field) {
2308
+ return value && typeof value === "object" && !Array.isArray(value) && typeof value[field] === "string" ? String(value[field]) : void 0;
2309
+ }
2192
2310
  function capabilityToolDefinitions(opts) {
2193
2311
  const workflowFile = opts.workflowFile ?? "kody.yml";
2194
2312
  const listTool = {
@@ -2337,10 +2455,7 @@ function capabilityToolDefinitions(opts) {
2337
2455
  handler: async (args) => {
2338
2456
  const capability = String(args.capability ?? args.executable ?? "");
2339
2457
  const issueNumber = Number(args.issueNumber);
2340
- if (isDispatchGated(
2341
- capability,
2342
- readCapabilityTrustMode(opts.state, opts.repoSlug, opts.capabilitySlug)
2343
- )) {
2458
+ if (isDispatchGated(capability, readCapabilityTrustMode(opts.state, opts.repoSlug, opts.capabilitySlug))) {
2344
2459
  return { content: [{ type: "text", text: trustRefusal(opts.capabilitySlug) }] };
2345
2460
  }
2346
2461
  const result = dispatchWorkflow(workflowFile, capability, issueNumber, opts.repoSlug);
@@ -2348,6 +2463,86 @@ function capabilityToolDefinitions(opts) {
2348
2463
  return { content: [{ type: "text", text }] };
2349
2464
  }
2350
2465
  };
2466
+ const cmsListCollectionsTool = {
2467
+ name: "cms_list_collections",
2468
+ description: "List configured Dashboard CMS collections and their supported operations. Read-only.",
2469
+ inputSchema: {},
2470
+ handler: async () => cmsToolResponse(await callDashboardCms(opts, "/api/kody/cms"))
2471
+ };
2472
+ const cmsListDocumentsTool = {
2473
+ name: "cms_list_documents",
2474
+ description: "List or search Dashboard CMS documents from one configured collection. Read-only.",
2475
+ inputSchema: {
2476
+ collection: z2.string().min(1).describe("CMS collection name."),
2477
+ q: z2.string().optional().describe("Optional search query."),
2478
+ filters: z2.record(z2.string(), z2.unknown()).optional().describe("Optional filter object keyed by field."),
2479
+ sort: z2.array(
2480
+ z2.object({
2481
+ field: z2.string().min(1),
2482
+ direction: z2.enum(["asc", "desc"]).default("desc")
2483
+ })
2484
+ ).optional(),
2485
+ limit: z2.number().int().min(1).max(100).optional(),
2486
+ offset: z2.number().int().min(0).optional()
2487
+ },
2488
+ handler: async (args) => {
2489
+ const collection = encodeURIComponent(stringArg(args.collection));
2490
+ return cmsToolResponse(await callDashboardCms(opts, `/api/kody/cms/${collection}${cmsQuery(args)}`));
2491
+ }
2492
+ };
2493
+ const cmsGetDocumentTool = {
2494
+ name: "cms_get_document",
2495
+ description: "Get one Dashboard CMS document by collection and id. Read-only.",
2496
+ inputSchema: {
2497
+ collection: z2.string().min(1).describe("CMS collection name."),
2498
+ id: z2.string().min(1).describe("Document id.")
2499
+ },
2500
+ handler: async (args) => {
2501
+ const collection = encodeURIComponent(stringArg(args.collection));
2502
+ const id = encodeURIComponent(stringArg(args.id));
2503
+ return cmsToolResponse(await callDashboardCms(opts, `/api/kody/cms/${collection}/${id}`));
2504
+ }
2505
+ };
2506
+ const cmsCreateDocumentTool = {
2507
+ name: "cms_create_document",
2508
+ description: "Create one Dashboard CMS document when the capability is trusted and the CMS collection allows create.",
2509
+ inputSchema: {
2510
+ collection: z2.string().min(1).describe("CMS collection name."),
2511
+ data: z2.record(z2.string(), z2.unknown()).describe("Document fields to create.")
2512
+ },
2513
+ handler: async (args) => {
2514
+ const refusal = assertCmsWriteAllowed(opts);
2515
+ if (refusal) return { content: [{ type: "text", text: refusal }] };
2516
+ const collection = encodeURIComponent(stringArg(args.collection));
2517
+ return cmsToolResponse(
2518
+ await callDashboardCms(opts, `/api/kody/cms/${collection}`, {
2519
+ method: "POST",
2520
+ body: JSON.stringify(documentArg(args.data))
2521
+ })
2522
+ );
2523
+ }
2524
+ };
2525
+ const cmsUpdateDocumentTool = {
2526
+ name: "cms_update_document",
2527
+ description: "Update one Dashboard CMS document when the capability is trusted and the CMS collection allows update.",
2528
+ inputSchema: {
2529
+ collection: z2.string().min(1).describe("CMS collection name."),
2530
+ id: z2.string().min(1).describe("Document id."),
2531
+ data: z2.record(z2.string(), z2.unknown()).describe("Partial document fields to update.")
2532
+ },
2533
+ handler: async (args) => {
2534
+ const refusal = assertCmsWriteAllowed(opts);
2535
+ if (refusal) return { content: [{ type: "text", text: refusal }] };
2536
+ const collection = encodeURIComponent(stringArg(args.collection));
2537
+ const id = encodeURIComponent(stringArg(args.id));
2538
+ return cmsToolResponse(
2539
+ await callDashboardCms(opts, `/api/kody/cms/${collection}/${id}`, {
2540
+ method: "PATCH",
2541
+ body: JSON.stringify(documentArg(args.data))
2542
+ })
2543
+ );
2544
+ }
2545
+ };
2351
2546
  return [
2352
2547
  listTool,
2353
2548
  syncTool,
@@ -2359,7 +2554,12 @@ function capabilityToolDefinitions(opts) {
2359
2554
  readThreadTool,
2360
2555
  ensureIssueTool,
2361
2556
  ensureCommentTool,
2362
- dispatchTool
2557
+ dispatchTool,
2558
+ cmsListCollectionsTool,
2559
+ cmsListDocumentsTool,
2560
+ cmsGetDocumentTool,
2561
+ cmsCreateDocumentTool,
2562
+ cmsUpdateDocumentTool
2363
2563
  ];
2364
2564
  }
2365
2565
  function buildCapabilityMcpServer(opts) {
@@ -2406,7 +2606,12 @@ var init_capabilityMcp = __esm({
2406
2606
  "read_thread",
2407
2607
  "ensure_issue",
2408
2608
  "ensure_comment",
2409
- "dispatch_workflow"
2609
+ "dispatch_workflow",
2610
+ "cms_list_collections",
2611
+ "cms_list_documents",
2612
+ "cms_get_document",
2613
+ "cms_create_document",
2614
+ "cms_update_document"
2410
2615
  ];
2411
2616
  }
2412
2617
  });
@@ -9428,6 +9633,8 @@ function inferVerdictFromReviewText(body) {
9428
9633
  if (/\b(blocking|blocker|must fix|should not merge|regression|breaks|security risk)\b/i.test(body)) return "FAIL";
9429
9634
  if (/\b(actionable item|suggestions?|worth clarifying|worth checking|minor note|non-blocking|deserves a comment)\b/i.test(
9430
9635
  body
9636
+ ) || /\b(improvement opportunit(?:y|ies)|more useful if|would be more useful|would be better|incomplete as standalone|leaving a gap)\b/i.test(
9637
+ body
9431
9638
  )) {
9432
9639
  return "CONCERNS";
9433
9640
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.270",
3
+ "version": "0.4.272",
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",