@kody-ade/kody-engine 0.2.52 → 0.2.54

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/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.52",
6
+ version: "0.2.54",
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",
@@ -738,6 +738,7 @@ import * as fs7 from "fs";
738
738
  import * as path6 from "path";
739
739
  var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
740
740
  var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
741
+ var VALID_ROLES = /* @__PURE__ */ new Set(["primitive", "orchestrator", "watch", "utility"]);
741
742
  var ProfileError = class extends Error {
742
743
  constructor(profilePath, message) {
743
744
  super(`Invalid profile at ${profilePath}:
@@ -765,9 +766,17 @@ function loadProfile(profilePath) {
765
766
  if (kind === "scheduled" && typeof r.schedule !== "string") {
766
767
  throw new ProfileError(profilePath, `kind: "scheduled" requires a "schedule" cron string`);
767
768
  }
769
+ if (typeof r.role !== "string" || !VALID_ROLES.has(r.role)) {
770
+ throw new ProfileError(
771
+ profilePath,
772
+ `"role" is required and must be one of: ${[...VALID_ROLES].join(" | ")}`
773
+ );
774
+ }
775
+ const role = r.role;
768
776
  const profile = {
769
777
  name: requireString(profilePath, r, "name"),
770
778
  describe: typeof r.describe === "string" ? r.describe : "",
779
+ role,
771
780
  kind,
772
781
  schedule: typeof r.schedule === "string" ? r.schedule : void 0,
773
782
  inputs: parseInputs(profilePath, r.inputs),
@@ -2373,9 +2382,12 @@ function getIssue(issueNumber, cwd) {
2373
2382
  }))
2374
2383
  };
2375
2384
  }
2385
+ function stripKody2Mentions(body) {
2386
+ return body.replace(/(@)(kody2)/gi, "$1\u200B$2");
2387
+ }
2376
2388
  function postIssueComment(issueNumber, body, cwd) {
2377
2389
  try {
2378
- gh2(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: body, cwd });
2390
+ gh2(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
2379
2391
  } catch (err) {
2380
2392
  process.stderr.write(
2381
2393
  `[kody2] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
@@ -2460,7 +2472,7 @@ function getPrLatestReviewBody(prNumber, cwd) {
2460
2472
  }
2461
2473
  function postPrReviewComment(prNumber, body, cwd) {
2462
2474
  try {
2463
- gh2(["pr", "comment", String(prNumber), "--body-file", "-"], { input: body, cwd });
2475
+ gh2(["pr", "comment", String(prNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
2464
2476
  } catch (err) {
2465
2477
  process.stderr.write(
2466
2478
  `[kody2] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
@@ -2698,7 +2710,11 @@ function parseGenericFlags(argv) {
2698
2710
  }
2699
2711
 
2700
2712
  // src/lifecycleLabels.ts
2701
- var KODY_LABEL_PREFIX = "kody:";
2713
+ var KODY_NAMESPACE = "kody";
2714
+ function groupOf(label) {
2715
+ const idx = label.indexOf(":");
2716
+ return idx === -1 ? label : label.slice(0, idx + 1);
2717
+ }
2702
2718
  function collectProfileLabels() {
2703
2719
  const byLabel = /* @__PURE__ */ new Map();
2704
2720
  for (const exe of listExecutables()) {
@@ -2719,7 +2735,7 @@ function extractLabelSpec(entry) {
2719
2735
  const w = entry.with;
2720
2736
  if (!w) return null;
2721
2737
  const label = typeof w.label === "string" ? w.label : null;
2722
- if (!label || !label.startsWith(KODY_LABEL_PREFIX)) return null;
2738
+ if (!label || !label.startsWith(KODY_NAMESPACE)) return null;
2723
2739
  return {
2724
2740
  label,
2725
2741
  color: typeof w.color === "string" ? w.color : void 0,
@@ -2766,16 +2782,17 @@ function createLabelInRepo(spec, cwd) {
2766
2782
  }
2767
2783
  function setKodyLabel(issueNumber, spec, cwd) {
2768
2784
  const target = spec.label;
2769
- if (!target.startsWith(KODY_LABEL_PREFIX)) {
2785
+ if (!target.startsWith(KODY_NAMESPACE)) {
2770
2786
  process.stderr.write(
2771
2787
  `[kody2] setKodyLabel: refusing to set non-kody label "${target}"
2772
2788
  `
2773
2789
  );
2774
2790
  return;
2775
2791
  }
2792
+ const targetGroup = groupOf(target);
2776
2793
  const present = getIssueLabels(issueNumber, cwd);
2777
2794
  for (const label of present) {
2778
- if (label !== target && label.startsWith(KODY_LABEL_PREFIX)) {
2795
+ if (label !== target && label.startsWith(KODY_NAMESPACE) && groupOf(label) === targetGroup) {
2779
2796
  removeLabel(issueNumber, label, cwd);
2780
2797
  }
2781
2798
  }
@@ -2832,7 +2849,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
2832
2849
  if (state) state.flow = void 0;
2833
2850
  if (!issueNumber) return;
2834
2851
  const label = typeof args?.label === "string" ? args.label : void 0;
2835
- if (label && label.startsWith(KODY_LABEL_PREFIX)) {
2852
+ if (label && label.startsWith(KODY_NAMESPACE)) {
2836
2853
  setKodyLabel(
2837
2854
  issueNumber,
2838
2855
  {
@@ -4399,9 +4416,9 @@ function synthesizeAction(ctx) {
4399
4416
  // src/scripts/setLifecycleLabel.ts
4400
4417
  var setLifecycleLabel = async (ctx, _profile, args) => {
4401
4418
  const label = args?.label;
4402
- if (typeof label !== "string" || !label.startsWith(KODY_LABEL_PREFIX)) {
4419
+ if (typeof label !== "string" || !label.startsWith(KODY_NAMESPACE)) {
4403
4420
  process.stderr.write(
4404
- `[kody2] setLifecycleLabel: missing or invalid "label" arg (must start with "${KODY_LABEL_PREFIX}"): ${String(label)}
4421
+ `[kody2] setLifecycleLabel: missing or invalid "label" arg (must start with "${KODY_NAMESPACE}"): ${String(label)}
4405
4422
  `
4406
4423
  );
4407
4424
  return;
@@ -4434,14 +4451,14 @@ var skipAgent = async (ctx) => {
4434
4451
  // src/scripts/startFlow.ts
4435
4452
  import { execFileSync as execFileSync16 } from "child_process";
4436
4453
  var API_TIMEOUT_MS6 = 3e4;
4437
- var startFlow = async (ctx, _profile, _agentResult, args) => {
4454
+ var startFlow = async (ctx, profile, _agentResult, args) => {
4438
4455
  const entry = args?.entry;
4439
4456
  if (!entry) {
4440
4457
  process.stderr.write("[kody2 startFlow] missing `with.entry` \u2014 skipping\n");
4441
4458
  return;
4442
4459
  }
4443
4460
  const target = args?.target ?? "issue";
4444
- const flowName = ctx.args.flow ?? "default";
4461
+ const flowName = profile.name;
4445
4462
  const issueNumber = ctx.args.issue;
4446
4463
  if (!issueNumber) {
4447
4464
  process.stderr.write("[kody2 startFlow] no --issue arg \u2014 skipping\n");
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "bug",
3
+ "role": "orchestrator",
3
4
  "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
5
  "inputs": [
5
6
  {
@@ -27,6 +28,14 @@
27
28
  "cliTools": [],
28
29
  "scripts": {
29
30
  "preflight": [
31
+ {
32
+ "script": "setLifecycleLabel",
33
+ "with": {
34
+ "label": "kody-flow:bug",
35
+ "color": "d73a4a",
36
+ "description": "kody2 flow: bug / enhancement"
37
+ }
38
+ },
30
39
  {
31
40
  "script": "setLifecycleLabel",
32
41
  "with": {
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "chore",
3
+ "role": "orchestrator",
4
+ "describe": "Sub-orchestrator for chore / docs / dep-bump issues — run → review (→ fix on concerns/fail). Skips planning entirely. No agent.",
5
+ "inputs": [
6
+ {
7
+ "name": "issue",
8
+ "flag": "--issue",
9
+ "type": "int",
10
+ "required": true,
11
+ "describe": "GitHub issue number to drive the flow on."
12
+ }
13
+ ],
14
+ "claudeCode": {
15
+ "model": "inherit",
16
+ "permissionMode": "default",
17
+ "maxTurns": 0,
18
+ "maxThinkingTokens": null,
19
+ "systemPromptAppend": null,
20
+ "tools": [],
21
+ "hooks": [],
22
+ "skills": [],
23
+ "commands": [],
24
+ "subagents": [],
25
+ "plugins": [],
26
+ "mcpServers": []
27
+ },
28
+ "cliTools": [],
29
+ "scripts": {
30
+ "preflight": [
31
+ {
32
+ "script": "setLifecycleLabel",
33
+ "with": {
34
+ "label": "kody-flow:chore",
35
+ "color": "c5def5",
36
+ "description": "kody2 flow: chore / docs / dep-bump"
37
+ }
38
+ },
39
+ {
40
+ "script": "setLifecycleLabel",
41
+ "with": {
42
+ "label": "kody:orchestrating",
43
+ "color": "1d76db",
44
+ "description": "kody2: orchestrating a multi-stage flow"
45
+ }
46
+ },
47
+ { "script": "loadIssueContext" },
48
+ { "script": "loadTaskState" },
49
+ { "script": "skipAgent" }
50
+ ],
51
+ "postflight": [
52
+ { "script": "startFlow", "with": { "entry": "run", "target": "issue" },
53
+ "runWhen": { "data.taskState.core.lastOutcome": null } },
54
+
55
+ { "script": "dispatch", "with": { "next": "review", "target": "pr" },
56
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RUN_COMPLETED" } },
57
+
58
+ { "script": "finishFlow",
59
+ "with": { "reason": "review-passed", "label": "kody:done", "color": "0e8a16", "description": "kody2: PR ready for human review/merge" },
60
+ "runWhen": { "data.taskState.core.lastOutcome.type": "REVIEW_PASS" } },
61
+
62
+ { "script": "dispatch", "with": { "next": "fix", "target": "pr" },
63
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["REVIEW_CONCERNS", "REVIEW_FAIL"] } },
64
+
65
+ { "script": "finishFlow",
66
+ "with": { "reason": "review-failed", "label": "kody:failed", "color": "e11d21", "description": "kody2: flow failed" },
67
+ "runWhen": { "data.taskState.core.lastOutcome.type": "REVIEW_FAILED" } },
68
+
69
+ { "script": "finishFlow",
70
+ "with": { "reason": "fix-applied", "label": "kody:done", "color": "0e8a16", "description": "kody2: PR ready for human review/merge" },
71
+ "runWhen": { "data.taskState.core.lastOutcome.type": "FIX_COMPLETED" } },
72
+
73
+ { "script": "finishFlow",
74
+ "with": { "reason": "aborted", "label": "kody:failed", "color": "e11d21", "description": "kody2: flow failed" },
75
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["RUN_FAILED", "FIX_FAILED", "AGENT_NOT_RUN"] } },
76
+
77
+ { "script": "persistFlowState" }
78
+ ]
79
+ },
80
+ "output": {
81
+ "actionTypes": [
82
+ "FLOW_STARTED",
83
+ "FLOW_COMPLETED",
84
+ "FLOW_ABORTED"
85
+ ]
86
+ }
87
+ }
@@ -0,0 +1,5 @@
1
+ <!--
2
+ Placeholder. The chore sub-orchestrator runs with maxTurns: 0 and a
3
+ `skipAgent` preflight, so this prompt is never sent to Claude. The
4
+ transition logic lives entirely in profile.json's postflight entries.
5
+ -->
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "feature",
3
+ "role": "orchestrator",
4
+ "describe": "Sub-orchestrator for feature / refactor issues — research → plan → run → review (→ fix on concerns/fail). No agent — the postflight entries ARE the transition table, evaluated top-to-bottom via runWhen.",
5
+ "inputs": [
6
+ {
7
+ "name": "issue",
8
+ "flag": "--issue",
9
+ "type": "int",
10
+ "required": true,
11
+ "describe": "GitHub issue number to drive the flow on."
12
+ }
13
+ ],
14
+ "claudeCode": {
15
+ "model": "inherit",
16
+ "permissionMode": "default",
17
+ "maxTurns": 0,
18
+ "maxThinkingTokens": null,
19
+ "systemPromptAppend": null,
20
+ "tools": [],
21
+ "hooks": [],
22
+ "skills": [],
23
+ "commands": [],
24
+ "subagents": [],
25
+ "plugins": [],
26
+ "mcpServers": []
27
+ },
28
+ "cliTools": [],
29
+ "scripts": {
30
+ "preflight": [
31
+ {
32
+ "script": "setLifecycleLabel",
33
+ "with": {
34
+ "label": "kody-flow:feature",
35
+ "color": "a2eeef",
36
+ "description": "kody2 flow: feature / refactor"
37
+ }
38
+ },
39
+ {
40
+ "script": "setLifecycleLabel",
41
+ "with": {
42
+ "label": "kody:orchestrating",
43
+ "color": "1d76db",
44
+ "description": "kody2: orchestrating a multi-stage flow"
45
+ }
46
+ },
47
+ { "script": "loadIssueContext" },
48
+ { "script": "loadTaskState" },
49
+ { "script": "skipAgent" }
50
+ ],
51
+ "postflight": [
52
+ { "script": "startFlow", "with": { "entry": "research", "target": "issue" },
53
+ "runWhen": { "data.taskState.core.lastOutcome": null } },
54
+
55
+ { "script": "dispatch", "with": { "next": "plan", "target": "issue" },
56
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RESEARCH_COMPLETED" } },
57
+
58
+ { "script": "dispatch", "with": { "next": "run", "target": "issue" },
59
+ "runWhen": { "data.taskState.core.lastOutcome.type": "PLAN_COMPLETED" } },
60
+
61
+ { "script": "dispatch", "with": { "next": "review", "target": "pr" },
62
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RUN_COMPLETED" } },
63
+
64
+ { "script": "finishFlow",
65
+ "with": { "reason": "review-passed", "label": "kody:done", "color": "0e8a16", "description": "kody2: PR ready for human review/merge" },
66
+ "runWhen": { "data.taskState.core.lastOutcome.type": "REVIEW_PASS" } },
67
+
68
+ { "script": "dispatch", "with": { "next": "fix", "target": "pr" },
69
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["REVIEW_CONCERNS", "REVIEW_FAIL"] } },
70
+
71
+ { "script": "finishFlow",
72
+ "with": { "reason": "review-failed", "label": "kody:failed", "color": "e11d21", "description": "kody2: flow failed" },
73
+ "runWhen": { "data.taskState.core.lastOutcome.type": "REVIEW_FAILED" } },
74
+
75
+ { "script": "finishFlow",
76
+ "with": { "reason": "fix-applied", "label": "kody:done", "color": "0e8a16", "description": "kody2: PR ready for human review/merge" },
77
+ "runWhen": { "data.taskState.core.lastOutcome.type": "FIX_COMPLETED" } },
78
+
79
+ { "script": "finishFlow",
80
+ "with": { "reason": "aborted", "label": "kody:failed", "color": "e11d21", "description": "kody2: flow failed" },
81
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["RESEARCH_FAILED", "PLAN_FAILED", "RUN_FAILED", "FIX_FAILED", "AGENT_NOT_RUN"] } },
82
+
83
+ { "script": "persistFlowState" }
84
+ ]
85
+ },
86
+ "output": {
87
+ "actionTypes": [
88
+ "FLOW_STARTED",
89
+ "FLOW_COMPLETED",
90
+ "FLOW_ABORTED"
91
+ ]
92
+ }
93
+ }
@@ -0,0 +1,5 @@
1
+ <!--
2
+ Placeholder. The feature sub-orchestrator runs with maxTurns: 0 and a
3
+ `skipAgent` preflight, so this prompt is never sent to Claude. The
4
+ transition logic lives entirely in profile.json's postflight entries.
5
+ -->
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "fix",
3
+ "role": "primitive",
3
4
  "describe": "Apply review feedback to an existing PR branch.",
4
5
  "inputs": [
5
6
  {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "fix-ci",
3
+ "role": "primitive",
3
4
  "describe": "Fix a failing CI workflow on an existing PR.",
4
5
  "inputs": [
5
6
  {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "init",
3
+ "role": "utility",
3
4
  "describe": "Scaffold a consumer repo with kody.config.json and the @kody2 workflow. No agent.",
4
5
  "inputs": [
5
6
  {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "plan",
3
+ "role": "primitive",
3
4
  "describe": "Research an issue and produce a concrete implementation plan as a comment. Read-only \u2014 no branches, no commits.",
4
5
  "inputs": [
5
6
  {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "plan-verify",
3
+ "role": "utility",
3
4
  "describe": "Live-test executable. Loads kody-live-marker skill, /kody-live-probe command, kody-live-trace hook, and the bundled test-plugin. Asks the agent to emit confirmation tokens for each feature so Kody2 can validate plugin wiring end-to-end.",
4
5
  "inputs": [
5
6
  { "name": "issue", "flag": "--issue", "type": "int", "required": true, "describe": "GitHub issue number to verify against." }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "release",
3
+ "role": "utility",
3
4
  "describe": "Version bump + changelog + release PR (prepare), or tag + publish + GH release (finalize). No agent.",
4
5
  "inputs": [
5
6
  {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "research",
3
+ "role": "primitive",
3
4
  "describe": "Research an issue: understand the ask, map relevant repo context, and surface clarifying questions + gaps. Read-only — no branches, no commits, no prescribed next steps.",
4
5
  "inputs": [
5
6
  {
@@ -67,6 +68,9 @@
67
68
  },
68
69
  {
69
70
  "script": "saveTaskState"
71
+ },
72
+ {
73
+ "script": "advanceFlow"
70
74
  }
71
75
  ]
72
76
  },
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "resolve",
3
+ "role": "primitive",
3
4
  "describe": "Resolve merge conflicts between a PR branch and the default branch.",
4
5
  "inputs": [
5
6
  {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "review",
3
+ "role": "primitive",
3
4
  "describe": "Read-only structured review of an open PR. Posts one comment, never commits.",
4
5
  "inputs": [
5
6
  {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "run",
3
+ "role": "primitive",
3
4
  "describe": "Implement a GitHub issue end-to-end: branch, code, commit, open PR.",
4
5
  "inputs": [
5
6
  {
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "spec",
3
+ "role": "orchestrator",
4
+ "describe": "Sub-orchestrator for spec / RFC / design-doc issues — research → plan (stop). Terminates at the plan artifact; no run, no PR. No agent.",
5
+ "inputs": [
6
+ {
7
+ "name": "issue",
8
+ "flag": "--issue",
9
+ "type": "int",
10
+ "required": true,
11
+ "describe": "GitHub issue number to drive the flow on."
12
+ }
13
+ ],
14
+ "claudeCode": {
15
+ "model": "inherit",
16
+ "permissionMode": "default",
17
+ "maxTurns": 0,
18
+ "maxThinkingTokens": null,
19
+ "systemPromptAppend": null,
20
+ "tools": [],
21
+ "hooks": [],
22
+ "skills": [],
23
+ "commands": [],
24
+ "subagents": [],
25
+ "plugins": [],
26
+ "mcpServers": []
27
+ },
28
+ "cliTools": [],
29
+ "scripts": {
30
+ "preflight": [
31
+ {
32
+ "script": "setLifecycleLabel",
33
+ "with": {
34
+ "label": "kody-flow:spec",
35
+ "color": "7057ff",
36
+ "description": "kody2 flow: spec / RFC / design-doc"
37
+ }
38
+ },
39
+ {
40
+ "script": "setLifecycleLabel",
41
+ "with": {
42
+ "label": "kody:orchestrating",
43
+ "color": "1d76db",
44
+ "description": "kody2: orchestrating a multi-stage flow"
45
+ }
46
+ },
47
+ { "script": "loadIssueContext" },
48
+ { "script": "loadTaskState" },
49
+ { "script": "skipAgent" }
50
+ ],
51
+ "postflight": [
52
+ { "script": "startFlow", "with": { "entry": "research", "target": "issue" },
53
+ "runWhen": { "data.taskState.core.lastOutcome": null } },
54
+
55
+ { "script": "dispatch", "with": { "next": "plan", "target": "issue" },
56
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RESEARCH_COMPLETED" } },
57
+
58
+ { "script": "finishFlow",
59
+ "with": { "reason": "spec-ready", "label": "kody:done", "color": "0e8a16", "description": "kody2: spec/plan artifact ready" },
60
+ "runWhen": { "data.taskState.core.lastOutcome.type": "PLAN_COMPLETED" } },
61
+
62
+ { "script": "finishFlow",
63
+ "with": { "reason": "aborted", "label": "kody:failed", "color": "e11d21", "description": "kody2: flow failed" },
64
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["RESEARCH_FAILED", "PLAN_FAILED", "AGENT_NOT_RUN"] } },
65
+
66
+ { "script": "persistFlowState" }
67
+ ]
68
+ },
69
+ "output": {
70
+ "actionTypes": [
71
+ "FLOW_STARTED",
72
+ "FLOW_COMPLETED",
73
+ "FLOW_ABORTED"
74
+ ]
75
+ }
76
+ }
@@ -0,0 +1,5 @@
1
+ <!--
2
+ Placeholder. The spec sub-orchestrator runs with maxTurns: 0 and a
3
+ `skipAgent` preflight, so this prompt is never sent to Claude. The
4
+ transition logic lives entirely in profile.json's postflight entries.
5
+ -->
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "sync",
3
+ "role": "primitive",
3
4
  "describe": "Merge the default (base) branch into a PR branch and push. No agent.",
4
5
  "inputs": [
5
6
  {
@@ -18,7 +18,19 @@ export interface Profile {
18
18
  name: string
19
19
  describe: string
20
20
  /**
21
- * Execution model. `oneshot` (default): single invocation on demand.
21
+ * Semantic role what this executable IS, not when it runs.
22
+ * - primitive: single-step agent executor (flow → agent → verify → commit → PR).
23
+ * - orchestrator: no-agent, drives primitives via a postflight transition table.
24
+ * - watch: scheduled observer that inspects repo state and may trigger other executables.
25
+ * - utility: no-agent, one-off administrative work (scaffolding, release, etc.).
26
+ *
27
+ * Roles enforce shape at profile-load time and let help/dispatch treat
28
+ * executables differently by category.
29
+ */
30
+ role: "primitive" | "orchestrator" | "watch" | "utility"
31
+ /**
32
+ * Execution model — orthogonal to `role`.
33
+ * `oneshot` (default): single invocation on demand.
22
34
  * `scheduled`: fires periodically via an external cron (typically GHA
23
35
  * `schedule:`). Scheduled profiles must declare a `schedule` cron string.
24
36
  */
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "ui-review",
3
+ "role": "primitive",
3
4
  "describe": "UI/UX review of an open PR: browses the running preview with Playwright, compares behavior to diff intent, posts one structured review comment. Read-only on the repo (no commits); writes a throwaway Playwright spec under .kody2/.",
4
5
  "kind": "oneshot",
5
6
  "inputs": [
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "watch-stale-prs",
3
+ "role": "watch",
3
4
  "describe": "Scheduled: list open PRs untouched for N days and report. No agent invocation.",
4
5
  "kind": "scheduled",
5
6
  "schedule": "0 8 * * MON",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.2.52",
3
+ "version": "0.2.54",
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",