@kody-ade/kody-engine 0.3.21 → 0.3.22

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.
@@ -1,62 +1,21 @@
1
1
  {
2
2
  "name": "release",
3
- "role": "utility",
4
- "phase": "shipped",
5
- "describe": "Version bump + changelog + release PR (prepare), or tag + publish + GH release (finalize). No agent.",
3
+ "role": "orchestrator",
4
+ "describe": "Release orchestrator: drives release-prepare → merge PR → release-publish → release-deploy. No agent — postflight entries ARE the transition table, evaluated top-to-bottom via runWhen.",
6
5
  "inputs": [
7
- {
8
- "name": "mode",
9
- "flag": "--mode",
10
- "type": "enum",
11
- "values": [
12
- "prepare",
13
- "finalize"
14
- ],
15
- "required": false,
16
- "describe": "`prepare` (default): bump + changelog + release PR. `finalize`: E2E gate + tag + publish + GH release."
17
- },
18
- {
19
- "name": "bump",
20
- "flag": "--bump",
21
- "type": "enum",
22
- "values": [
23
- "patch",
24
- "minor",
25
- "major"
26
- ],
27
- "required": false,
28
- "describe": "Version bump when mode=prepare (ignored in finalize). Default patch."
29
- },
30
- {
31
- "name": "dry-run",
32
- "flag": "--dry-run",
33
- "type": "bool",
34
- "required": false,
35
- "describe": "Print plan without writing files, creating PRs, tagging, or publishing."
36
- },
37
6
  {
38
7
  "name": "issue",
39
8
  "flag": "--issue",
40
9
  "type": "int",
41
- "required": false,
42
- "describe": "Issue number to post success/failure follow-up on. Auto-populated by dispatch when triggered via @kody comment."
43
- },
44
- {
45
- "name": "prefer",
46
- "flag": "--prefer",
47
- "type": "enum",
48
- "values": [
49
- "ours",
50
- "theirs"
51
- ],
52
- "required": false,
53
- "describe": "On release/vX.Y.Z branch collision (prepare mode): 'ours' force-pushes over the remote branch; 'theirs' reuses the existing branch and its open PR. Default (unset): refuse on non-ff."
10
+ "required": true,
11
+ "describe": "GitHub issue number to drive the release flow on."
54
12
  }
55
13
  ],
56
14
  "claudeCode": {
57
15
  "model": "inherit",
58
- "permissionMode": "acceptEdits",
59
- "maxTurns": null,
16
+ "permissionMode": "default",
17
+ "maxTurns": 0,
18
+ "maxThinkingTokens": null,
60
19
  "systemPromptAppend": null,
61
20
  "tools": [],
62
21
  "hooks": [],
@@ -67,12 +26,51 @@
67
26
  "mcpServers": []
68
27
  },
69
28
  "cliTools": [],
29
+ "inputArtifacts": [],
30
+ "outputArtifacts": [],
70
31
  "scripts": {
71
32
  "preflight": [
72
33
  {
73
- "script": "releaseFlow"
74
- }
34
+ "script": "setLifecycleLabel",
35
+ "with": {
36
+ "label": "kody-flow:release",
37
+ "color": "5319e7",
38
+ "description": "kody flow: release"
39
+ }
40
+ },
41
+ {
42
+ "script": "setLifecycleLabel",
43
+ "with": {
44
+ "label": "kody:orchestrating",
45
+ "color": "1d76db",
46
+ "description": "kody: orchestrating a multi-stage flow"
47
+ }
48
+ },
49
+ { "script": "loadIssueContext" },
50
+ { "script": "loadTaskState" },
51
+ { "script": "skipAgent" }
75
52
  ],
76
- "postflight": []
53
+ "postflight": [
54
+ { "script": "startFlow", "with": { "entry": "release-prepare", "target": "issue" } },
55
+
56
+ { "script": "mergeReleasePr",
57
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RELEASE_PREPARE_COMPLETED" } },
58
+
59
+ { "script": "dispatch", "with": { "next": "release-publish", "target": "issue" },
60
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RELEASE_MERGE_COMPLETED" } },
61
+
62
+ { "script": "dispatch", "with": { "next": "release-deploy", "target": "issue" },
63
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RELEASE_PUBLISH_COMPLETED" } },
64
+
65
+ { "script": "finishFlow",
66
+ "with": { "reason": "release-completed", "label": "kody:done", "color": "0e8a16", "description": "kody: release complete" },
67
+ "runWhen": { "data.taskState.core.lastOutcome.type": "RELEASE_DEPLOY_COMPLETED" } },
68
+
69
+ { "script": "finishFlow",
70
+ "with": { "reason": "release-failed", "label": "kody:failed", "color": "e11d21", "description": "kody: release flow failed" },
71
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["RELEASE_PREPARE_FAILED", "RELEASE_MERGE_FAILED", "RELEASE_PUBLISH_FAILED", "RELEASE_DEPLOY_FAILED"] } },
72
+
73
+ { "script": "persistFlowState" }
74
+ ]
77
75
  }
78
76
  }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # release-deploy: run the configured deployCommand and/or notifyCommand
4
+ # after release-publish has tagged and published the artifact. No agent.
5
+ #
6
+ # Both commands are optional. With neither set, deploy is a no-op success
7
+ # (the orchestrator still advances to "done").
8
+ #
9
+ # Inputs (env):
10
+ # KODY_ARG_DRY_RUN true|false
11
+ # KODY_ARG_ISSUE triggering issue/PR number (optional)
12
+ #
13
+ # Config (env):
14
+ # KODY_CFG_RELEASE_DEPLOYCOMMAND optional; $VERSION substituted
15
+ # KODY_CFG_RELEASE_NOTIFYCOMMAND optional; $VERSION substituted
16
+ # KODY_CFG_RELEASE_TIMEOUTMS per-command timeout in ms (default 600000)
17
+ #
18
+ # Stdout signals:
19
+ # KODY_REASON=<text>
20
+ # KODY_SKIP_AGENT=true
21
+
22
+ set -euo pipefail
23
+
24
+ dry_run="${KODY_ARG_DRY_RUN:-false}"
25
+ deploy_cmd="${KODY_CFG_RELEASE_DEPLOYCOMMAND:-}"
26
+ notify_cmd="${KODY_CFG_RELEASE_NOTIFYCOMMAND:-}"
27
+ timeout_ms="${KODY_CFG_RELEASE_TIMEOUTMS:-600000}"
28
+ timeout_s=$((timeout_ms / 1000))
29
+
30
+ read_pkg_version() {
31
+ python3 -c "import json; print(json.load(open('package.json'))['version'])"
32
+ }
33
+
34
+ if [[ ! -f package.json ]]; then
35
+ echo "KODY_REASON=release deploy: package.json not found"
36
+ echo "KODY_SKIP_AGENT=true"
37
+ exit 99
38
+ fi
39
+
40
+ version=$(read_pkg_version)
41
+ echo "→ release deploy: v${version}"
42
+
43
+ if [[ -z "$deploy_cmd" && -z "$notify_cmd" ]]; then
44
+ echo "KODY_REASON=no deployCommand or notifyCommand configured — nothing to run"
45
+ echo "KODY_SKIP_AGENT=true"
46
+ exit 0
47
+ fi
48
+
49
+ if [[ "$dry_run" == "true" ]]; then
50
+ echo "KODY_REASON=dry-run — would run deploy/notify commands"
51
+ echo "KODY_SKIP_AGENT=true"
52
+ exit 0
53
+ fi
54
+
55
+ export HUSKY=0 SKIP_HOOKS=1 CI="${CI:-1}"
56
+
57
+ deploy_status="skipped"
58
+ if [[ -n "$deploy_cmd" ]]; then
59
+ cmd="${deploy_cmd//\$VERSION/$version}"
60
+ echo " deploy: ${cmd}"
61
+ if timeout "${timeout_s}" bash -c "$cmd"; then
62
+ deploy_status="ok"
63
+ else
64
+ deploy_status="failed"
65
+ echo "KODY_REASON=release deploy: deployCommand failed"
66
+ echo "KODY_SKIP_AGENT=true"
67
+ exit 1
68
+ fi
69
+ fi
70
+
71
+ notify_status="skipped"
72
+ if [[ -n "$notify_cmd" ]]; then
73
+ cmd="${notify_cmd//\$VERSION/$version}"
74
+ echo " notify: ${cmd}"
75
+ if timeout "${timeout_s}" bash -c "$cmd"; then
76
+ notify_status="ok"
77
+ else
78
+ notify_status="failed"
79
+ echo "[kody release-deploy] notifyCommand failed (non-fatal)" >&2
80
+ fi
81
+ fi
82
+
83
+ echo "KODY_REASON=deploy=${deploy_status} notify=${notify_status}"
84
+ echo "KODY_SKIP_AGENT=true"
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "release-deploy",
3
+ "role": "utility",
4
+ "phase": "shipped",
5
+ "describe": "Run the configured deployCommand and notifyCommand after release-publish has tagged + published. No agent.",
6
+ "inputs": [
7
+ {
8
+ "name": "dry-run",
9
+ "flag": "--dry-run",
10
+ "type": "bool",
11
+ "required": false,
12
+ "describe": "Print plan without running deploy/notify commands."
13
+ },
14
+ {
15
+ "name": "issue",
16
+ "flag": "--issue",
17
+ "type": "int",
18
+ "required": false,
19
+ "describe": "Issue/PR number to post the terminal notice on. Auto-injected by dispatch."
20
+ }
21
+ ],
22
+ "claudeCode": {
23
+ "model": "inherit",
24
+ "permissionMode": "acceptEdits",
25
+ "maxTurns": 0,
26
+ "maxThinkingTokens": null,
27
+ "systemPromptAppend": null,
28
+ "tools": [],
29
+ "hooks": [],
30
+ "skills": [],
31
+ "commands": [],
32
+ "subagents": [],
33
+ "plugins": [],
34
+ "mcpServers": []
35
+ },
36
+ "cliTools": [],
37
+ "inputArtifacts": [],
38
+ "outputArtifacts": [],
39
+ "scripts": {
40
+ "preflight": [
41
+ { "script": "setCommentTarget", "with": { "type": "issue" } },
42
+ { "script": "loadTaskState" },
43
+ { "shell": "deploy.sh" },
44
+ { "script": "skipAgent" }
45
+ ],
46
+ "postflight": [
47
+ { "script": "recordOutcome" },
48
+ { "script": "saveTaskState" },
49
+ { "script": "notifyTerminal", "with": { "label": "release deploy" } },
50
+ { "script": "advanceFlow" }
51
+ ]
52
+ }
53
+ }
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # release-prepare: bump version files, generate CHANGELOG.md, commit on a
4
+ # release branch, open the release PR. Pure mechanical work — no agent.
5
+ #
6
+ # Inputs (env, set by the executor):
7
+ # KODY_ARG_BUMP patch|minor|major (default: patch)
8
+ # KODY_ARG_DRY_RUN true|false
9
+ # KODY_ARG_PREFER ours|theirs (optional)
10
+ # KODY_ARG_ISSUE triggering issue/PR number (optional)
11
+ #
12
+ # Config (env, flattened from kody.config.json):
13
+ # KODY_CFG_GIT_DEFAULTBRANCH e.g. main
14
+ # KODY_CFG_RELEASE_VERSIONFILES JSON array, e.g. ["package.json"]
15
+ #
16
+ # Stdout signals to the executor:
17
+ # KODY_PR_URL=<url> — set ctx.output.prUrl (final PR URL)
18
+ # KODY_REASON=<text> — set ctx.output.reason (failure or dry-run note)
19
+ # KODY_SKIP_AGENT=true — bypass the agent (always; this is a no-agent flow)
20
+
21
+ set -euo pipefail
22
+
23
+ # ── Helpers ────────────────────────────────────────────────────────────────
24
+
25
+ bump="${KODY_ARG_BUMP:-patch}"
26
+ dry_run="${KODY_ARG_DRY_RUN:-false}"
27
+ prefer="${KODY_ARG_PREFER:-}"
28
+ default_branch="${KODY_CFG_GIT_DEFAULTBRANCH:-main}"
29
+ version_files_json="${KODY_CFG_RELEASE_VERSIONFILES:-}"
30
+
31
+ fail() {
32
+ local reason="$1"
33
+ echo "KODY_REASON=$reason"
34
+ echo "KODY_SKIP_AGENT=true"
35
+ exit "${2:-1}"
36
+ }
37
+
38
+ bump_version() {
39
+ local cur="$1" kind="$2"
40
+ local rest="${cur#*-}"
41
+ local core="${cur%%-*}"
42
+ if ! [[ "$core" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
43
+ fail "release prepare: cannot parse version '$cur' (expected x.y.z[-suffix])" 99
44
+ fi
45
+ local maj="${BASH_REMATCH[1]}" min="${BASH_REMATCH[2]}" pat="${BASH_REMATCH[3]}"
46
+ case "$kind" in
47
+ major) maj=$((maj + 1)); min=0; pat=0 ;;
48
+ minor) min=$((min + 1)); pat=0 ;;
49
+ patch|*) pat=$((pat + 1)) ;;
50
+ esac
51
+ echo "${maj}.${min}.${pat}"
52
+ }
53
+
54
+ read_pkg_version() {
55
+ python3 -c "import json,sys; print(json.load(open('package.json'))['version'])"
56
+ }
57
+
58
+ write_pkg_version() {
59
+ local file="$1" new="$2"
60
+ python3 - "$file" "$new" <<'PY'
61
+ import json, sys
62
+ path, new = sys.argv[1], sys.argv[2]
63
+ try:
64
+ with open(path) as f:
65
+ text = f.read()
66
+ except FileNotFoundError:
67
+ print("MISSING")
68
+ sys.exit(0)
69
+ try:
70
+ data = json.loads(text)
71
+ except Exception:
72
+ print("UNCHANGED")
73
+ sys.exit(0)
74
+ if data.get("version") == new:
75
+ print("UNCHANGED")
76
+ sys.exit(0)
77
+ data["version"] = new
78
+ indent = 2
79
+ with open(path, "w") as f:
80
+ f.write(json.dumps(data, indent=indent) + "\n")
81
+ print("WROTE")
82
+ PY
83
+ }
84
+
85
+ resolve_version_files() {
86
+ if [[ -z "$version_files_json" ]]; then
87
+ echo "package.json"
88
+ return
89
+ fi
90
+ python3 - <<PY
91
+ import json, os, sys
92
+ raw = os.environ.get("KODY_CFG_RELEASE_VERSIONFILES", "")
93
+ try:
94
+ arr = json.loads(raw)
95
+ except Exception:
96
+ print("package.json")
97
+ sys.exit(0)
98
+ if isinstance(arr, list) and arr:
99
+ for f in arr:
100
+ if isinstance(f, str) and f:
101
+ print(f)
102
+ else:
103
+ print("package.json")
104
+ PY
105
+ }
106
+
107
+ generate_changelog() {
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
113
+ else
114
+ git log -n100 HEAD --pretty=format:'%s||%h' --no-merges 2>/dev/null || true
115
+ fi
116
+ }
117
+
118
+ format_changelog() {
119
+ local new_version="$1"
120
+ local raw="$2"
121
+ local date_str
122
+ date_str=$(date -u +%Y-%m-%d)
123
+ python3 - "$new_version" "$date_str" <<PY
124
+ import sys, re
125
+ new_version, date_str = sys.argv[1], sys.argv[2]
126
+ raw = sys.stdin.read()
127
+ buckets = {k: [] for k in ("feat", "fix", "perf", "refactor", "docs", "chore", "other")}
128
+ for line in raw.splitlines():
129
+ line = line.strip()
130
+ if not line:
131
+ continue
132
+ if "||" not in line:
133
+ continue
134
+ subject, sha = line.split("||", 1)
135
+ if re.match(r"(?i)^chore:\s*release\s+v\d", subject):
136
+ continue
137
+ m = re.match(r"^(\w+)(?:\(.*?\))?\s*:\s*(.+)$", subject)
138
+ if m:
139
+ kind = m.group(1).lower()
140
+ msg = m.group(2)
141
+ else:
142
+ kind = "other"
143
+ msg = subject
144
+ buckets.setdefault(kind, buckets["other"]).append(f"- {msg} ({sha})")
145
+ labels = [
146
+ ("feat", "Features"),
147
+ ("fix", "Fixes"),
148
+ ("perf", "Performance"),
149
+ ("refactor", "Refactoring"),
150
+ ("docs", "Docs"),
151
+ ("chore", "Chores"),
152
+ ("other", "Other"),
153
+ ]
154
+ parts = [f"## v{new_version} — {date_str}", ""]
155
+ emitted = False
156
+ for key, label in labels:
157
+ items = buckets.get(key) or []
158
+ if not items:
159
+ continue
160
+ parts.append(f"### {label}")
161
+ parts.extend(items)
162
+ parts.append("")
163
+ emitted = True
164
+ if not emitted:
165
+ parts.append("_No notable commits since the last release._")
166
+ parts.append("")
167
+ sys.stdout.write("\n".join(parts))
168
+ PY
169
+ }
170
+
171
+ prepend_changelog() {
172
+ local entry="$1"
173
+ local header='# Changelog
174
+
175
+ All notable changes to this project will be documented in this file.
176
+
177
+ '
178
+ if [[ -f CHANGELOG.md ]]; then
179
+ if grep -qE '^#\s*Changelog\b' CHANGELOG.md; then
180
+ python3 - "$entry" <<'PY'
181
+ import sys
182
+ entry = sys.argv[1]
183
+ with open("CHANGELOG.md") as f:
184
+ prior = f.read()
185
+ idx = prior.index("\n", prior.index("# Changelog"))
186
+ new = prior[: idx + 1] + "\n" + entry + prior[idx + 1 :]
187
+ with open("CHANGELOG.md", "w") as f:
188
+ f.write(new)
189
+ PY
190
+ else
191
+ python3 - "$entry" <<'PY'
192
+ import sys
193
+ entry = sys.argv[1]
194
+ header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n"
195
+ with open("CHANGELOG.md") as f:
196
+ prior = f.read()
197
+ with open("CHANGELOG.md", "w") as f:
198
+ f.write(header + entry + prior)
199
+ PY
200
+ fi
201
+ else
202
+ python3 - "$entry" <<'PY'
203
+ import sys
204
+ entry = sys.argv[1]
205
+ header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n"
206
+ with open("CHANGELOG.md", "w") as f:
207
+ f.write(header + entry)
208
+ PY
209
+ fi
210
+ }
211
+
212
+ remote_branch_exists() {
213
+ local branch="$1"
214
+ git ls-remote --heads origin "$branch" 2>/dev/null | grep -q .
215
+ }
216
+
217
+ find_open_pr() {
218
+ local branch="$1"
219
+ gh pr list --head "$branch" --state open --json url --limit 1 2>/dev/null \
220
+ | python3 -c 'import json,sys; data=json.load(sys.stdin); print(data[0]["url"] if data else "")' 2>/dev/null \
221
+ || echo ""
222
+ }
223
+
224
+ # ── Flow ───────────────────────────────────────────────────────────────────
225
+
226
+ if [[ ! -f package.json ]]; then
227
+ fail "release prepare: package.json not found" 99
228
+ fi
229
+
230
+ old_version=$(read_pkg_version)
231
+ new_version=$(bump_version "$old_version" "$bump")
232
+ tag="v${new_version}"
233
+ release_branch="release/${tag}"
234
+
235
+ echo "→ release prepare: ${old_version} → ${new_version} (${bump})"
236
+
237
+ if [[ "$dry_run" == "true" ]]; then
238
+ echo "RELEASE_PLAN=bump=${new_version} tag=${tag}"
239
+ echo "KODY_REASON=dry-run — would bump to ${new_version}${prefer:+ (--prefer ${prefer})}"
240
+ echo "KODY_SKIP_AGENT=true"
241
+ exit 0
242
+ fi
243
+
244
+ # Branch-collision gate.
245
+ collides=false
246
+ if remote_branch_exists "$release_branch"; then
247
+ collides=true
248
+ case "$prefer" in
249
+ theirs)
250
+ existing=$(find_open_pr "$release_branch")
251
+ if [[ -n "$existing" ]]; then
252
+ echo " reusing existing PR (--prefer theirs): ${existing}"
253
+ echo "KODY_PR_URL=${existing}"
254
+ echo "KODY_REASON=reused existing release PR"
255
+ echo "KODY_SKIP_AGENT=true"
256
+ exit 0
257
+ fi
258
+ fail "release prepare --prefer theirs: ${release_branch} exists on remote but has no open PR — nothing to reuse" 4
259
+ ;;
260
+ ours)
261
+ echo " branch ${release_branch} exists on remote — will force-push (--prefer ours)"
262
+ ;;
263
+ *)
264
+ fail "release prepare: branch ${release_branch} already exists on remote. Use --prefer ours to force-push, or --prefer theirs to reuse the existing PR." 4
265
+ ;;
266
+ esac
267
+ fi
268
+
269
+ # Bump version files.
270
+ mapfile -t files < <(resolve_version_files)
271
+ touched=()
272
+ for f in "${files[@]}"; do
273
+ res=$(write_pkg_version "$f" "$new_version")
274
+ if [[ "$res" == "WROTE" ]]; then
275
+ touched+=("$f")
276
+ fi
277
+ done
278
+ if [[ ${#touched[@]} -eq 0 ]]; then
279
+ fail "release prepare: no version strings updated (files: ${files[*]})"
280
+ fi
281
+ echo " wrote ${touched[*]}"
282
+
283
+ # Changelog.
284
+ raw_log=$(generate_changelog "$new_version") || raw_log=""
285
+ entry=$(printf '%s' "$raw_log" | format_changelog "$new_version")
286
+ prepend_changelog "$entry"
287
+ echo " wrote CHANGELOG.md"
288
+
289
+ # Commit + push.
290
+ export HUSKY=0 SKIP_HOOKS=1
291
+ git checkout -b "$release_branch"
292
+ for f in "${touched[@]}" CHANGELOG.md; do
293
+ git add -- "$f"
294
+ done
295
+ git -c commit.gpgsign=false commit -m "chore: release ${tag}"
296
+ if [[ "$collides" == "true" && "$prefer" == "ours" ]]; then
297
+ git push -u --force-with-lease origin "$release_branch"
298
+ else
299
+ git push -u origin "$release_branch"
300
+ fi
301
+
302
+ # Open PR (or link to existing one if --prefer ours collided).
303
+ pr_url=""
304
+ if [[ "$collides" == "true" && "$prefer" == "ours" ]]; then
305
+ pr_url=$(find_open_pr "$release_branch")
306
+ fi
307
+
308
+ if [[ -z "$pr_url" ]]; then
309
+ body_max=60000
310
+ if [[ ${#entry} -gt $body_max ]]; then
311
+ body_entry="${entry:0:$body_max}
312
+
313
+ _… truncated; see CHANGELOG.md_"
314
+ else
315
+ body_entry="$entry"
316
+ fi
317
+ body=$'Automated release PR opened by kody.\n\n'"$body_entry"$'\n\nMerge this and then run `kody release --mode finalize`.'
318
+ pr_url=$(printf '%s' "$body" | gh pr create --head "$release_branch" --base "$default_branch" --title "chore: release ${tag}" --body-file -)
319
+ fi
320
+
321
+ if [[ -z "$pr_url" ]]; then
322
+ fail "release prepare: gh pr create returned empty URL" 4
323
+ fi
324
+
325
+ echo "RELEASE_PR=${pr_url}"
326
+ echo "KODY_PR_URL=${pr_url}"
327
+ echo "KODY_REASON=opened release PR for ${tag}"
328
+ echo "KODY_SKIP_AGENT=true"
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "release-prepare",
3
+ "role": "utility",
4
+ "phase": "shipped",
5
+ "describe": "Bump version files, generate CHANGELOG.md, commit on a release branch, open the release PR. No agent.",
6
+ "inputs": [
7
+ {
8
+ "name": "bump",
9
+ "flag": "--bump",
10
+ "type": "enum",
11
+ "values": ["patch", "minor", "major"],
12
+ "required": false,
13
+ "describe": "Version bump increment. Default patch."
14
+ },
15
+ {
16
+ "name": "dry-run",
17
+ "flag": "--dry-run",
18
+ "type": "bool",
19
+ "required": false,
20
+ "describe": "Print plan without writing files, committing, or opening a PR."
21
+ },
22
+ {
23
+ "name": "prefer",
24
+ "flag": "--prefer",
25
+ "type": "enum",
26
+ "values": ["ours", "theirs"],
27
+ "required": false,
28
+ "describe": "On release/vX.Y.Z branch collision: 'ours' force-pushes; 'theirs' reuses the existing PR. Default refuses non-ff."
29
+ },
30
+ {
31
+ "name": "issue",
32
+ "flag": "--issue",
33
+ "type": "int",
34
+ "required": false,
35
+ "describe": "Issue/PR number to post the terminal notice on. Auto-injected by dispatch."
36
+ }
37
+ ],
38
+ "claudeCode": {
39
+ "model": "inherit",
40
+ "permissionMode": "acceptEdits",
41
+ "maxTurns": 0,
42
+ "maxThinkingTokens": null,
43
+ "systemPromptAppend": null,
44
+ "tools": [],
45
+ "hooks": [],
46
+ "skills": [],
47
+ "commands": [],
48
+ "subagents": [],
49
+ "plugins": [],
50
+ "mcpServers": []
51
+ },
52
+ "cliTools": [],
53
+ "inputArtifacts": [],
54
+ "outputArtifacts": [],
55
+ "scripts": {
56
+ "preflight": [
57
+ { "script": "setCommentTarget", "with": { "type": "issue" } },
58
+ { "script": "loadTaskState" },
59
+ { "shell": "prepare.sh" },
60
+ { "script": "skipAgent" }
61
+ ],
62
+ "postflight": [
63
+ { "script": "recordOutcome" },
64
+ { "script": "saveTaskState" },
65
+ { "script": "mirrorStateToPr" },
66
+ { "script": "notifyTerminal", "with": { "label": "release prepare" } },
67
+ { "script": "advanceFlow" }
68
+ ]
69
+ }
70
+ }