@kody-ade/kody-engine 0.3.64 → 0.3.67

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
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@kody-ade/kody-engine",
6
- version: "0.3.64",
6
+ version: "0.3.67",
7
7
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
8
8
  license: "MIT",
9
9
  type: "module",
@@ -858,7 +858,14 @@ function autoDispatch(opts) {
858
858
  if (!executable) {
859
859
  executable = isPr ? opts?.config?.defaultPrExecutable ?? "fix" : opts?.config?.defaultExecutable ?? null;
860
860
  }
861
- if (!executable) return null;
861
+ if (!executable) {
862
+ const profileMissing = aliased ? getProfileInputs(aliased) === null : true;
863
+ process.stderr.write(
864
+ `[kody] dispatch: no executable resolved for issue_comment (firstToken=${firstToken ?? "<none>"}, aliased=${aliased ?? "<none>"}, profileFound=${!profileMissing}, defaultExecutable=${opts?.config?.defaultExecutable ?? "<unset>"})
865
+ `
866
+ );
867
+ return null;
868
+ }
862
869
  const inputs = getProfileInputs(executable);
863
870
  const effectiveInputs = inputs ?? [];
864
871
  const unknownProfile = inputs === null;
@@ -5891,10 +5898,7 @@ function tryPostPr3(prNumber, body, cwd) {
5891
5898
  function findPreviewDeploymentUrl(prNumber, cwd) {
5892
5899
  const sha = getPrHeadSha(prNumber, cwd);
5893
5900
  if (!sha) return null;
5894
- const raw = safeGh2(
5895
- ["api", `repos/{owner}/{repo}/deployments?sha=${sha}&environment=Preview&per_page=10`],
5896
- cwd
5897
- );
5901
+ const raw = safeGh2(["api", `repos/{owner}/{repo}/deployments?sha=${sha}&environment=Preview&per_page=10`], cwd);
5898
5902
  if (!raw) return null;
5899
5903
  let deployments;
5900
5904
  try {
@@ -5916,10 +5920,7 @@ function getPrHeadSha(prNumber, cwd) {
5916
5920
  return trimmed.length > 0 ? trimmed : null;
5917
5921
  }
5918
5922
  function latestSuccessUrl(deploymentId, cwd) {
5919
- const raw = safeGh2(
5920
- ["api", `repos/{owner}/{repo}/deployments/${deploymentId}/statuses?per_page=10`],
5921
- cwd
5922
- );
5923
+ const raw = safeGh2(["api", `repos/{owner}/{repo}/deployments/${deploymentId}/statuses?per_page=10`], cwd);
5923
5924
  if (!raw) return null;
5924
5925
  let statuses;
5925
5926
  try {
@@ -54,6 +54,61 @@ read_version() {
54
54
  version=$(read_version "$default_branch")
55
55
  echo "→ release deploy: v${version}"
56
56
 
57
+ # Read the CHANGELOG section for $version from the integration branch
58
+ # (where release-prepare just committed it). Handles both header shapes
59
+ # observed in the wild: `## [0.25.0] - 2026-04-15` and `## 0.22.0 (...)`.
60
+ # Prints the body lines (without the matched header). Empty stdout =
61
+ # fall back to the minimal PR body — never break the release.
62
+ read_changelog_section() {
63
+ local branch="$1" ver="$2" raw=""
64
+ if ! raw=$(git show "origin/${branch}:CHANGELOG.md" 2>/dev/null); then
65
+ return 0
66
+ fi
67
+ printf '%s' "$raw" | awk -v ver="$ver" '
68
+ BEGIN { capture = 0 }
69
+ # Match all observed header shapes:
70
+ # "## [0.25.0] - 2026-04-15" (bracketed, dash separator)
71
+ # "## 0.22.0 (2026-03-25)" (bare, parenthesized date)
72
+ # "## v0.25.5 — 2026-05-05" (v-prefixed, em-dash, kody release-prepare style)
73
+ /^##[[:space:]]/ {
74
+ if (capture) { exit }
75
+ header = $0
76
+ sub(/^##[[:space:]]+/, "", header)
77
+ sub(/^\[/, "", header); sub(/\].*/, "", header)
78
+ sub(/[[:space:]].*/, "", header)
79
+ sub(/[(].*/, "", header)
80
+ sub(/^v/, "", header)
81
+ if (header == ver) { capture = 1; next }
82
+ }
83
+ capture { print }
84
+ ' | awk '
85
+ # Trim leading and trailing blank lines from the captured block.
86
+ NF { if (!started) started = 1; out[++n] = $0; last = n; next }
87
+ started { out[++n] = $0 }
88
+ END { for (i = 1; i <= last; i++) print out[i] }
89
+ '
90
+ }
91
+
92
+ changelog_section=$(read_changelog_section "$default_branch" "$version" || true)
93
+ if [[ -z "$changelog_section" ]]; then
94
+ echo " no CHANGELOG section for v${version} on origin/${default_branch} — using minimal PR body"
95
+ fi
96
+
97
+ # Build the kody-managed body block. A marker pair lets us update the
98
+ # section idempotently on re-runs without clobbering anything a human
99
+ # pasted outside the markers.
100
+ build_pr_body() {
101
+ local tracking_line="$1"
102
+ printf 'Automated deploy PR opened by kody — promotes `%s` to `%s` for release **v%s**.\n\n' \
103
+ "$default_branch" "$release_branch" "$version"
104
+ if [[ -n "$changelog_section" ]]; then
105
+ printf '<!-- kody-changelog-start -->\n## What'\''s changing in v%s\n\n%s\n<!-- kody-changelog-end -->\n\n' \
106
+ "$version" "$changelog_section"
107
+ fi
108
+ printf 'Merge this PR to deploy v%s to `%s`.%s\n' \
109
+ "$version" "$release_branch" "$tracking_line"
110
+ }
111
+
57
112
  # Single-branch repos: nothing to deploy.
58
113
  if [[ -z "$release_branch" || "$release_branch" == "$default_branch" ]]; then
59
114
  echo "KODY_REASON=no releaseBranch configured (or equals defaultBranch) — nothing to deploy"
@@ -78,20 +133,25 @@ existing=$(gh pr list --head "$default_branch" --base "$release_branch" --state
78
133
  # reuse-existing-PR path.
79
134
  issue_arg="${KODY_ARG_ISSUE:-}"
80
135
 
136
+ # Same Tracking-Issue marker as release-prepare — non-closing reference
137
+ # so the originating release issue stays open through the deploy step
138
+ # while the Kody Dashboard can still link this PR to the task for preview.
139
+ tracking_line=""
140
+ if [[ "$issue_arg" =~ ^[0-9]+$ && "$issue_arg" != "0" ]]; then
141
+ tracking_line=$'\n\nTracking-Issue: #'"${issue_arg}"
142
+ fi
143
+ body=$(build_pr_body "$tracking_line")
144
+
81
145
  if [[ -n "$existing" ]]; then
82
146
  echo " reusing existing deploy PR: ${existing}"
83
147
  pr_url="$existing"
84
- else
85
- # Same Tracking-Issue marker as release-prepare non-closing reference
86
- # so the originating release issue stays open through the deploy step
87
- # while the Kody Dashboard can still link this PR to the task for preview.
88
- tracking_line=""
89
- if [[ "$issue_arg" =~ ^[0-9]+$ && "$issue_arg" != "0" ]]; then
90
- tracking_line=$'\n\nTracking-Issue: #'"${issue_arg}"
148
+ # Refresh the body so re-runs converge on the current changelog. Best-
149
+ # effort: a failed edit (e.g. permission-denied) shouldn't fail the
150
+ # release the PR already exists and its title/branch are unchanged.
151
+ if ! printf '%s' "$body" | gh pr edit "$pr_url" --body-file - >/dev/null 2>&1; then
152
+ echo "[kody release-deploy] WARN: failed to refresh deploy PR body" >&2
91
153
  fi
92
- body="Automated deploy PR opened by kody — promotes \`${default_branch}\` to \`${release_branch}\` for release **v${version}**.
93
-
94
- Merge this PR to deploy v${version} to \`${release_branch}\`.${tracking_line}"
154
+ else
95
155
  if ! pr_url=$(printf '%s' "$body" | gh pr create --head "$default_branch" --base "$release_branch" --title "deploy: ${default_branch} → ${release_branch} (v${version})" --body-file -); then
96
156
  echo "KODY_REASON=release deploy: gh pr create failed"
97
157
  echo "KODY_SKIP_AGENT=true"
@@ -127,9 +187,16 @@ echo "RELEASE_DEPLOY_PR=${pr_url}"
127
187
  echo "KODY_PR_URL=${pr_url}"
128
188
 
129
189
  # Optional post-deploy notification (e.g. Slack ping that a deploy PR is up).
190
+ # Substituted placeholders in the configured command:
191
+ # $VERSION — release version (e.g. 0.25.4)
192
+ # $DEPLOY_PR_URL — URL of the deploy PR just opened/reused
193
+ # The notifyCommand can use $DEPLOY_PR_URL to pull real content (e.g.
194
+ # `gh pr view $DEPLOY_PR_URL --json body --jq .body`) instead of
195
+ # rendering a hardcoded one-liner.
130
196
  notify_status="skipped"
131
197
  if [[ -n "$notify_cmd" ]]; then
132
198
  cmd="${notify_cmd//\$VERSION/$version}"
199
+ cmd="${cmd//\$DEPLOY_PR_URL/$pr_url}"
133
200
  echo " notify: ${cmd}"
134
201
  if timeout "${timeout_s}" bash -c "$cmd"; then
135
202
  notify_status="ok"
@@ -106,11 +106,22 @@ PY
106
106
 
107
107
  generate_changelog() {
108
108
  local new_version="$1"
109
- local last_tag
110
- if last_tag=$(git describe --tags --abbrev=0 --match 'v*' 2>/dev/null); then
111
- range="${last_tag}..HEAD"
112
- git log "$range" --pretty=format:'%s||%h' --no-merges 2>/dev/null || true
109
+ local last_tag count
110
+ # actions/checkout@v4 with fetch-depth: 0 still does NOT fetch tags by
111
+ # default — operators have to opt in via `fetch-tags: true`. When tags
112
+ # are absent, `git describe` returns empty and the else-branch's
113
+ # 100-commit window can over- or under-shoot. Try once to backfill
114
+ # tags before describing, ignoring failures (offline/sandbox runs).
115
+ if ! last_tag=$(git describe --tags --abbrev=0 --match 'v*' 2>/dev/null); then
116
+ git fetch --tags --quiet 2>/dev/null || true
117
+ last_tag=$(git describe --tags --abbrev=0 --match 'v*' 2>/dev/null || true)
118
+ fi
119
+ if [[ -n "$last_tag" ]]; then
120
+ count=$(git rev-list --count "${last_tag}..HEAD" --no-merges 2>/dev/null || echo "?")
121
+ echo " changelog: ${count} commits since ${last_tag}" >&2
122
+ git log "${last_tag}..HEAD" --pretty=format:'%s||%h' --no-merges 2>/dev/null || true
113
123
  else
124
+ echo " changelog: no previous v* tag found — using last 100 commits" >&2
114
125
  git log -n100 HEAD --pretty=format:'%s||%h' --no-merges 2>/dev/null || true
115
126
  fi
116
127
  }
File without changes
File without changes
File without changes
@@ -27,6 +27,7 @@ If a prior-art block is present above, READ THE DIFFS — those are failed or su
27
27
  - Read the tests for each of those files, if tests exist for the module.
28
28
  - Read at least one sibling module that already implements the same pattern you're about to follow — your edits should mirror an existing convention unless you can name why a new one is needed.
29
29
  - If your change requires writing or modifying a test, also check for repo-level testing guidance: `tests/README.md`, `TESTING.md`, or a "Testing"/"Tests" section in `AGENTS.md`/`CLAUDE.md`. If one exists, treat its patterns (auth setup, fixture creation, what NOT to do) as authoritative — they override anything you might infer from grepping individual files.
30
+ - **Removal/rename refactors** (deleting a call like `console.error`, renaming a function, dropping a method, replacing one API with another): before editing, grep the test directories for assertions tied to the OLD symbol — spies (`vi.spyOn(console`, `jest.spyOn(console`, `consoleErrorSpy`, `mockFn.mock.calls`), the literal function name, and any string the call produced. Enumerate every hit in your plan (step 2) and update those tests in step 4 in the same session. Skipping this grep is a hard failure even if your local test runs pass — the wrapper runs the full suite and you cannot fix breakages after DONE.
30
31
  - If a file you need to read does not exist, say so explicitly in your plan (step 2). Do not guess at its contents.
31
32
  2. **Plan** — before any Edit/Write, output a short plan (5–10 lines): what files you'll change, the approach, what could go wrong. No fluff.
32
33
  3. **Build** — Edit/Write to implement the change. Stay within the plan; if you discover the plan was wrong, briefly say so and adjust.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.3.64",
3
+ "version": "0.3.67",
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,6 +12,18 @@
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
+ },
15
27
  "dependencies": {
16
28
  "@actions/cache": "^6.0.0",
17
29
  "@anthropic-ai/claude-agent-sdk": "0.2.119"
@@ -32,16 +44,5 @@
32
44
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
33
45
  },
34
46
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
35
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
36
- "scripts": {
37
- "kody": "tsx bin/kody.ts",
38
- "build": "tsup && node scripts/copy-assets.cjs",
39
- "test": "vitest run tests/unit tests/int --no-coverage",
40
- "test:e2e": "vitest run tests/e2e --no-coverage",
41
- "test:all": "vitest run tests --no-coverage",
42
- "typecheck": "tsc --noEmit",
43
- "lint": "biome check",
44
- "lint:fix": "biome check --write",
45
- "format": "biome format --write"
46
- }
47
- }
47
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
48
+ }