@kody-ade/kody-engine 0.4.64 → 0.4.65

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
@@ -315,7 +315,7 @@ var init_verifyMcp = __esm({
315
315
  // package.json
316
316
  var package_default = {
317
317
  name: "@kody-ade/kody-engine",
318
- version: "0.4.64",
318
+ version: "0.4.65",
319
319
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
320
320
  license: "MIT",
321
321
  type: "module",
@@ -1925,6 +1925,141 @@ init_events();
1925
1925
  // src/profile.ts
1926
1926
  import * as fs9 from "fs";
1927
1927
  import * as path8 from "path";
1928
+
1929
+ // src/profile-error.ts
1930
+ var ProfileError = class extends Error {
1931
+ constructor(profilePath, message) {
1932
+ super(`Invalid profile at ${profilePath}:
1933
+ ${message}`);
1934
+ this.profilePath = profilePath;
1935
+ this.name = "ProfileError";
1936
+ }
1937
+ profilePath;
1938
+ };
1939
+
1940
+ // src/lifecycles/prBranch.ts
1941
+ var VALID_CONTEXTS = /* @__PURE__ */ new Set(["task", "ci-fix", "minimal"]);
1942
+ var CONTEXT_BUNDLES = {
1943
+ task: ["loadTaskState", "loadConventions", "loadPriorArt", "loadMemoryContext", "loadCoverageRules"],
1944
+ "ci-fix": ["loadTaskState", "loadConventions", "loadCoverageRules"],
1945
+ minimal: []
1946
+ };
1947
+ function prBranchLifecycle(profile, profilePath) {
1948
+ const cfg = validateConfig(profile.lifecycleConfig, profilePath);
1949
+ const before = [];
1950
+ if (cfg.sync) before.push({ script: "syncFlow" });
1951
+ before.push({
1952
+ script: "setLifecycleLabel",
1953
+ with: {
1954
+ label: cfg.label.name,
1955
+ color: cfg.label.color,
1956
+ description: cfg.label.description
1957
+ }
1958
+ });
1959
+ const contextBundle = buildContextBundle(cfg.context, cfg.contextExtras);
1960
+ const afterPreflight = cfg.context === "minimal" && cfg.contextExtras.length === 0 ? [{ script: "composePrompt" }] : [...contextBundle, { script: "composePrompt" }];
1961
+ profile.scripts.preflight = [...before, ...profile.scripts.preflight, ...afterPreflight];
1962
+ const beforePostflight = [{ script: "parseAgentResult" }];
1963
+ const verifyChain = cfg.verify ? [{ script: "verify" }, { script: "checkCoverageWithRetry" }, { script: "abortUnfinishedGitOps" }] : [];
1964
+ const tail = [
1965
+ ...verifyChain,
1966
+ { script: "commitAndPush" },
1967
+ { script: "ensurePr" },
1968
+ { script: "postIssueComment" },
1969
+ { script: "writeRunSummary" },
1970
+ { script: "saveTaskState" }
1971
+ ];
1972
+ if (cfg.mirrorState) tail.push({ script: "mirrorStateToPr" });
1973
+ if (cfg.advance) tail.push({ script: "advanceFlow" });
1974
+ profile.scripts.postflight = [...beforePostflight, ...profile.scripts.postflight, ...tail];
1975
+ }
1976
+ function buildContextBundle(context, extras) {
1977
+ const base = CONTEXT_BUNDLES[context] ?? [];
1978
+ if (base.length === 0 && extras.length === 0) return [];
1979
+ const out = [];
1980
+ let extrasInserted = false;
1981
+ for (const name of base) {
1982
+ out.push({ script: name });
1983
+ if (name === "loadTaskState" && extras.length > 0) {
1984
+ for (const e of extras) out.push({ script: e });
1985
+ extrasInserted = true;
1986
+ }
1987
+ }
1988
+ if (!extrasInserted && extras.length > 0) {
1989
+ out.unshift(...extras.map((e) => ({ script: e })));
1990
+ }
1991
+ return out;
1992
+ }
1993
+ function validateConfig(raw, profilePath) {
1994
+ if (!raw) {
1995
+ throw new ProfileError(profilePath, `lifecycle "pr-branch" requires "lifecycleConfig" with a "label" object`);
1996
+ }
1997
+ const label = raw.label;
1998
+ if (!label || typeof label !== "object" || Array.isArray(label)) {
1999
+ throw new ProfileError(profilePath, `lifecycle "pr-branch": lifecycleConfig.label must be an object`);
2000
+ }
2001
+ const lbl = label;
2002
+ for (const k of ["name", "color", "description"]) {
2003
+ if (typeof lbl[k] !== "string" || lbl[k].length === 0) {
2004
+ throw new ProfileError(profilePath, `lifecycle "pr-branch": lifecycleConfig.label.${k} must be a non-empty string`);
2005
+ }
2006
+ }
2007
+ const context = raw.context === void 0 ? "task" : raw.context;
2008
+ if (typeof context !== "string" || !VALID_CONTEXTS.has(context)) {
2009
+ throw new ProfileError(
2010
+ profilePath,
2011
+ `lifecycle "pr-branch": lifecycleConfig.context must be one of: ${[...VALID_CONTEXTS].join(" | ")}`
2012
+ );
2013
+ }
2014
+ let contextExtras = [];
2015
+ if (raw.contextExtras !== void 0) {
2016
+ if (!Array.isArray(raw.contextExtras) || raw.contextExtras.some((s) => typeof s !== "string" || !s)) {
2017
+ throw new ProfileError(
2018
+ profilePath,
2019
+ `lifecycle "pr-branch": lifecycleConfig.contextExtras must be an array of non-empty strings`
2020
+ );
2021
+ }
2022
+ contextExtras = raw.contextExtras;
2023
+ }
2024
+ return {
2025
+ label: {
2026
+ name: lbl.name,
2027
+ color: lbl.color,
2028
+ description: lbl.description
2029
+ },
2030
+ context,
2031
+ contextExtras,
2032
+ sync: parseBool(raw, profilePath, "sync", true),
2033
+ verify: parseBool(raw, profilePath, "verify", true),
2034
+ advance: parseBool(raw, profilePath, "advance", true),
2035
+ mirrorState: parseBool(raw, profilePath, "mirrorState", false)
2036
+ };
2037
+ }
2038
+ function parseBool(raw, profilePath, key, def) {
2039
+ if (raw[key] === void 0) return def;
2040
+ if (typeof raw[key] !== "boolean") {
2041
+ throw new ProfileError(profilePath, `lifecycle "pr-branch": lifecycleConfig.${key} must be a boolean`);
2042
+ }
2043
+ return raw[key];
2044
+ }
2045
+
2046
+ // src/lifecycles/index.ts
2047
+ var REGISTRY = {
2048
+ "pr-branch": prBranchLifecycle
2049
+ };
2050
+ function applyLifecycle(profile, profilePath) {
2051
+ if (!profile.lifecycle) return;
2052
+ const expander = REGISTRY[profile.lifecycle];
2053
+ if (!expander) {
2054
+ throw new ProfileError(
2055
+ profilePath,
2056
+ `unknown "lifecycle": "${profile.lifecycle}". Registered: ${Object.keys(REGISTRY).sort().join(", ")}`
2057
+ );
2058
+ }
2059
+ expander(profile, profilePath);
2060
+ }
2061
+
2062
+ // src/profile.ts
1928
2063
  var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
1929
2064
  var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
1930
2065
  var VALID_ROLES = /* @__PURE__ */ new Set(["primitive", "orchestrator", "container", "watch", "utility"]);
@@ -1940,6 +2075,8 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
1940
2075
  "inputs",
1941
2076
  "claudeCode",
1942
2077
  "cliTools",
2078
+ "lifecycle",
2079
+ "lifecycleConfig",
1943
2080
  "scripts",
1944
2081
  "outputContract",
1945
2082
  "inputArtifacts",
@@ -1951,15 +2088,6 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
1951
2088
  "children",
1952
2089
  "resetBetweenChildren"
1953
2090
  ]);
1954
- var ProfileError = class extends Error {
1955
- constructor(profilePath, message) {
1956
- super(`Invalid profile at ${profilePath}:
1957
- ${message}`);
1958
- this.profilePath = profilePath;
1959
- this.name = "ProfileError";
1960
- }
1961
- profilePath;
1962
- };
1963
2091
  function loadProfile(profilePath) {
1964
2092
  if (!fs9.existsSync(profilePath)) {
1965
2093
  throw new ProfileError(profilePath, "file not found");
@@ -1997,6 +2125,23 @@ function loadProfile(profilePath) {
1997
2125
  phase = r.phase;
1998
2126
  }
1999
2127
  const children = parseContainerChildren(profilePath, role, r.children);
2128
+ let lifecycle;
2129
+ if (r.lifecycle !== void 0) {
2130
+ if (typeof r.lifecycle !== "string" || r.lifecycle.length === 0) {
2131
+ throw new ProfileError(profilePath, `"lifecycle" must be a non-empty string`);
2132
+ }
2133
+ lifecycle = r.lifecycle;
2134
+ }
2135
+ let lifecycleConfig;
2136
+ if (r.lifecycleConfig !== void 0) {
2137
+ if (!r.lifecycleConfig || typeof r.lifecycleConfig !== "object" || Array.isArray(r.lifecycleConfig)) {
2138
+ throw new ProfileError(profilePath, `"lifecycleConfig" must be an object`);
2139
+ }
2140
+ lifecycleConfig = r.lifecycleConfig;
2141
+ }
2142
+ if (lifecycleConfig && !lifecycle) {
2143
+ throw new ProfileError(profilePath, `"lifecycleConfig" is only meaningful when "lifecycle" is set`);
2144
+ }
2000
2145
  const profile = {
2001
2146
  name: requireString(profilePath, r, "name"),
2002
2147
  describe: typeof r.describe === "string" ? r.describe : "",
@@ -2007,6 +2152,8 @@ function loadProfile(profilePath) {
2007
2152
  inputs: parseInputs(profilePath, r.inputs),
2008
2153
  claudeCode: parseClaudeCode(profilePath, r.claudeCode),
2009
2154
  cliTools: parseCliTools(profilePath, r.cliTools),
2155
+ lifecycle,
2156
+ lifecycleConfig,
2010
2157
  scripts: parseScripts(profilePath, r.scripts),
2011
2158
  outputContract: r.outputContract,
2012
2159
  inputArtifacts: parseInputArtifacts(profilePath, r.input),
@@ -2018,6 +2165,9 @@ function loadProfile(profilePath) {
2018
2165
  resetBetweenChildren: typeof r.resetBetweenChildren === "boolean" ? r.resetBetweenChildren : true,
2019
2166
  dir: path8.dirname(profilePath)
2020
2167
  };
2168
+ if (lifecycle) {
2169
+ applyLifecycle(profile, profilePath);
2170
+ }
2021
2171
  return profile;
2022
2172
  }
2023
2173
  function validateScriptReferences(profile, registeredScripts) {
@@ -63,37 +63,21 @@
63
63
  "usage": ""
64
64
  }
65
65
  ],
66
+ "lifecycle": "pr-branch",
67
+ "lifecycleConfig": {
68
+ "label": {
69
+ "name": "kody:fixing",
70
+ "color": "e99695",
71
+ "description": "kody: applying review feedback"
72
+ },
73
+ "context": "task"
74
+ },
66
75
  "scripts": {
67
76
  "preflight": [
68
- { "script": "syncFlow" },
69
- {
70
- "script": "setLifecycleLabel",
71
- "with": {
72
- "label": "kody:fixing",
73
- "color": "e99695",
74
- "description": "kody: applying review feedback"
75
- }
76
- },
77
- { "script": "fixFlow" },
78
- { "script": "loadTaskState" },
79
- { "script": "loadConventions" },
80
- { "script": "loadPriorArt" },
81
- { "script": "loadMemoryContext" },
82
- { "script": "loadCoverageRules" },
83
- { "script": "composePrompt" }
77
+ { "script": "fixFlow" }
84
78
  ],
85
79
  "postflight": [
86
- { "script": "parseAgentResult" },
87
- { "script": "requireFeedbackActions" },
88
- { "script": "verify" },
89
- { "script": "checkCoverageWithRetry" },
90
- { "script": "abortUnfinishedGitOps" },
91
- { "script": "commitAndPush" },
92
- { "script": "ensurePr" },
93
- { "script": "postIssueComment" },
94
- { "script": "writeRunSummary" },
95
- { "script": "saveTaskState" },
96
- { "script": "advanceFlow" }
80
+ { "script": "requireFeedbackActions" }
97
81
  ]
98
82
  },
99
83
  "output": {
@@ -44,62 +44,21 @@
44
44
  "mcpServers": []
45
45
  },
46
46
  "cliTools": [],
47
+ "lifecycle": "pr-branch",
48
+ "lifecycleConfig": {
49
+ "label": {
50
+ "name": "kody:fixing",
51
+ "color": "e99695",
52
+ "description": "kody: applying review feedback"
53
+ },
54
+ "context": "ci-fix",
55
+ "advance": false
56
+ },
47
57
  "scripts": {
48
58
  "preflight": [
49
- { "script": "syncFlow" },
50
- {
51
- "script": "setLifecycleLabel",
52
- "with": {
53
- "label": "kody:fixing",
54
- "color": "e99695",
55
- "description": "kody: applying review feedback"
56
- }
57
- },
58
- {
59
- "script": "fixCiFlow"
60
- },
61
- {
62
- "script": "loadTaskState"
63
- },
64
- {
65
- "script": "loadConventions"
66
- },
67
- {
68
- "script": "loadCoverageRules"
69
- },
70
- {
71
- "script": "composePrompt"
72
- }
59
+ { "script": "fixCiFlow" }
73
60
  ],
74
- "postflight": [
75
- {
76
- "script": "parseAgentResult"
77
- },
78
- {
79
- "script": "verify"
80
- },
81
- {
82
- "script": "checkCoverageWithRetry"
83
- },
84
- {
85
- "script": "abortUnfinishedGitOps"
86
- },
87
- {
88
- "script": "commitAndPush"
89
- },
90
- {
91
- "script": "ensurePr"
92
- },
93
- {
94
- "script": "postIssueComment"
95
- },
96
- {
97
- "script": "writeRunSummary"
98
- },
99
- {
100
- "script": "saveTaskState"
101
- }
102
- ]
61
+ "postflight": []
103
62
  },
104
63
  "output": {
105
64
  "actionTypes": [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -44,38 +44,24 @@
44
44
  "mcpServers": []
45
45
  },
46
46
  "cliTools": [],
47
+ "lifecycle": "pr-branch",
48
+ "lifecycleConfig": {
49
+ "label": {
50
+ "name": "kody:running",
51
+ "color": "fbca04",
52
+ "description": "kody: implementing the change"
53
+ },
54
+ "context": "task",
55
+ "contextExtras": ["resolveArtifacts"],
56
+ "sync": false,
57
+ "mirrorState": true
58
+ },
47
59
  "scripts": {
48
60
  "preflight": [
49
- {
50
- "script": "setLifecycleLabel",
51
- "with": {
52
- "label": "kody:running",
53
- "color": "fbca04",
54
- "description": "kody: implementing the change"
55
- }
56
- },
57
- { "script": "runFlow" },
58
- { "script": "loadTaskState" },
59
- { "script": "resolveArtifacts" },
60
- { "script": "loadPriorArt" },
61
- { "script": "loadMemoryContext" },
62
- { "script": "loadConventions" },
63
- { "script": "loadCoverageRules" },
64
- { "script": "composePrompt" }
61
+ { "script": "runFlow" }
65
62
  ],
66
63
  "postflight": [
67
- { "script": "parseAgentResult" },
68
- { "script": "requirePlanDeviations" },
69
- { "script": "verify" },
70
- { "script": "checkCoverageWithRetry" },
71
- { "script": "abortUnfinishedGitOps" },
72
- { "script": "commitAndPush" },
73
- { "script": "ensurePr" },
74
- { "script": "postIssueComment" },
75
- { "script": "writeRunSummary" },
76
- { "script": "saveTaskState" },
77
- { "script": "mirrorStateToPr" },
78
- { "script": "advanceFlow" }
64
+ { "script": "requirePlanDeviations" }
79
65
  ]
80
66
  },
81
67
  "input": {
@@ -51,6 +51,24 @@ export interface Profile {
51
51
  inputs: InputSpec[]
52
52
  claudeCode: ClaudeCodeSpec
53
53
  cliTools: CliToolSpec[]
54
+ /**
55
+ * Optional lifecycle macro. When set, the profile loader applies a
56
+ * predefined preflight/postflight wrapper around `scripts.preflight` and
57
+ * `scripts.postflight` before returning the profile. Registry of lifecycles
58
+ * lives in src/lifecycles/. Unknown values are rejected at load time.
59
+ *
60
+ * Lifecycles exist to consolidate orchestration boilerplate (label,
61
+ * context loading, verify, commit, comment) that recurs across many
62
+ * executables. Per-executable specifics still go in `scripts.preflight`
63
+ * and `scripts.postflight` — the lifecycle wraps them, it doesn't
64
+ * replace them.
65
+ */
66
+ lifecycle?: string
67
+ /**
68
+ * Lifecycle-specific configuration. Shape depends on `lifecycle`. Validated
69
+ * by each lifecycle expander, not by the generic profile parser.
70
+ */
71
+ lifecycleConfig?: Record<string, unknown>
54
72
  scripts: {
55
73
  preflight: ScriptEntry[]
56
74
  postflight: ScriptEntry[]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.64",
3
+ "version": "0.4.65",
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,18 +12,6 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
- "scripts": {
16
- "kody": "tsx bin/kody.ts",
17
- "build": "tsup && node scripts/copy-assets.cjs",
18
- "test": "vitest run tests/unit tests/int --no-coverage",
19
- "test:e2e": "vitest run tests/e2e --no-coverage",
20
- "test:all": "vitest run tests --no-coverage",
21
- "typecheck": "tsc --noEmit",
22
- "lint": "biome check",
23
- "lint:fix": "biome check --write",
24
- "format": "biome format --write",
25
- "prepublishOnly": "pnpm build"
26
- },
27
15
  "dependencies": {
28
16
  "@actions/cache": "^6.0.0",
29
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -45,5 +33,16 @@
45
33
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
46
34
  },
47
35
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
48
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
49
- }
36
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
37
+ "scripts": {
38
+ "kody": "tsx bin/kody.ts",
39
+ "build": "tsup && node scripts/copy-assets.cjs",
40
+ "test": "vitest run tests/unit tests/int --no-coverage",
41
+ "test:e2e": "vitest run tests/e2e --no-coverage",
42
+ "test:all": "vitest run tests --no-coverage",
43
+ "typecheck": "tsc --noEmit",
44
+ "lint": "biome check",
45
+ "lint:fix": "biome check --write",
46
+ "format": "biome format --write"
47
+ }
48
+ }