@kody-ade/kody-engine 0.4.17 → 0.4.19
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 +141 -2
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/goal-tick/tick.sh +101 -19
- package/dist/executables/job-tick/profile.json +6 -0
- package/dist/executables/job-tick/prompt.md +14 -1
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/package.json +14 -15
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.4.
|
|
6
|
+
version: "0.4.19",
|
|
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",
|
|
@@ -3897,6 +3897,83 @@ function failedAction3(reason) {
|
|
|
3897
3897
|
import * as fs19 from "fs";
|
|
3898
3898
|
import * as path18 from "path";
|
|
3899
3899
|
|
|
3900
|
+
// src/scripts/jobFrontmatter.ts
|
|
3901
|
+
var SCHEDULE_EVERY_VALUES = [
|
|
3902
|
+
"15m",
|
|
3903
|
+
"30m",
|
|
3904
|
+
"1h",
|
|
3905
|
+
"2h",
|
|
3906
|
+
"6h",
|
|
3907
|
+
"12h",
|
|
3908
|
+
"1d",
|
|
3909
|
+
"3d",
|
|
3910
|
+
"7d",
|
|
3911
|
+
"manual"
|
|
3912
|
+
];
|
|
3913
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
3914
|
+
function splitFrontmatter(raw) {
|
|
3915
|
+
const match = FRONTMATTER_RE.exec(raw);
|
|
3916
|
+
if (!match) return { frontmatter: {}, body: raw };
|
|
3917
|
+
const inner = match[1] ?? "";
|
|
3918
|
+
const body = raw.slice(match[0].length);
|
|
3919
|
+
return { frontmatter: parseFlatYaml(inner), body };
|
|
3920
|
+
}
|
|
3921
|
+
function isScheduleEvery(value) {
|
|
3922
|
+
return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
|
|
3923
|
+
}
|
|
3924
|
+
function scheduleEveryToMs(every) {
|
|
3925
|
+
const MIN = 60 * 1e3;
|
|
3926
|
+
const HOUR = 60 * MIN;
|
|
3927
|
+
const DAY = 24 * HOUR;
|
|
3928
|
+
switch (every) {
|
|
3929
|
+
case "15m":
|
|
3930
|
+
return 15 * MIN;
|
|
3931
|
+
case "30m":
|
|
3932
|
+
return 30 * MIN;
|
|
3933
|
+
case "1h":
|
|
3934
|
+
return HOUR;
|
|
3935
|
+
case "2h":
|
|
3936
|
+
return 2 * HOUR;
|
|
3937
|
+
case "6h":
|
|
3938
|
+
return 6 * HOUR;
|
|
3939
|
+
case "12h":
|
|
3940
|
+
return 12 * HOUR;
|
|
3941
|
+
case "1d":
|
|
3942
|
+
return DAY;
|
|
3943
|
+
case "3d":
|
|
3944
|
+
return 3 * DAY;
|
|
3945
|
+
case "7d":
|
|
3946
|
+
return 7 * DAY;
|
|
3947
|
+
case "manual":
|
|
3948
|
+
return Number.POSITIVE_INFINITY;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
function parseFlatYaml(text) {
|
|
3952
|
+
const out = {};
|
|
3953
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
3954
|
+
const line = rawLine.trim();
|
|
3955
|
+
if (!line || line.startsWith("#")) continue;
|
|
3956
|
+
const colon = line.indexOf(":");
|
|
3957
|
+
if (colon < 0) continue;
|
|
3958
|
+
const key = line.slice(0, colon).trim();
|
|
3959
|
+
const value = stripQuotes(line.slice(colon + 1).trim());
|
|
3960
|
+
if (key === "every" && isScheduleEvery(value)) {
|
|
3961
|
+
out.every = value;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
return out;
|
|
3965
|
+
}
|
|
3966
|
+
function stripQuotes(value) {
|
|
3967
|
+
if (value.length >= 2) {
|
|
3968
|
+
const first = value[0];
|
|
3969
|
+
const last = value[value.length - 1];
|
|
3970
|
+
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
3971
|
+
return value.slice(1, -1);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
return value;
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3900
3977
|
// src/scripts/issueStateComment.ts
|
|
3901
3978
|
function isStateEnvelope(x) {
|
|
3902
3979
|
if (x === null || typeof x !== "object") return false;
|
|
@@ -4254,7 +4331,15 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
4254
4331
|
process.stdout.write(`[jobs] ticking ${slugs.length} job(s) via ${targetExecutable}
|
|
4255
4332
|
`);
|
|
4256
4333
|
const results = [];
|
|
4334
|
+
const now = Date.now();
|
|
4257
4335
|
for (const slug of slugs) {
|
|
4336
|
+
const decision = await decideShouldFire(ctx.cwd, jobsDir, slug, backend, now);
|
|
4337
|
+
if (decision.skip) {
|
|
4338
|
+
process.stdout.write(`[jobs] \u23ED skip ${slug}: ${decision.reason}
|
|
4339
|
+
`);
|
|
4340
|
+
results.push({ slug, exitCode: 0, skipped: true, reason: decision.reason });
|
|
4341
|
+
continue;
|
|
4342
|
+
}
|
|
4258
4343
|
process.stdout.write(`[jobs] \u2192 tick ${slug}
|
|
4259
4344
|
`);
|
|
4260
4345
|
try {
|
|
@@ -4291,6 +4376,53 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
4291
4376
|
}
|
|
4292
4377
|
}
|
|
4293
4378
|
};
|
|
4379
|
+
async function decideShouldFire(cwd, jobsDir, slug, backend, now) {
|
|
4380
|
+
let every;
|
|
4381
|
+
try {
|
|
4382
|
+
const raw = fs19.readFileSync(path18.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
4383
|
+
every = splitFrontmatter(raw).frontmatter.every;
|
|
4384
|
+
} catch {
|
|
4385
|
+
return { skip: false, reason: "frontmatter unreadable" };
|
|
4386
|
+
}
|
|
4387
|
+
if (!every) return { skip: false, reason: "no schedule (every cron tick)" };
|
|
4388
|
+
if (every === "manual") {
|
|
4389
|
+
return { skip: true, reason: "manual-only (no auto-fire; trigger via dashboard Run now)" };
|
|
4390
|
+
}
|
|
4391
|
+
let lastFiredAt = null;
|
|
4392
|
+
try {
|
|
4393
|
+
const loaded = await backend.load(slug);
|
|
4394
|
+
const raw = loaded.state.data?.lastFiredAt;
|
|
4395
|
+
if (typeof raw === "string") {
|
|
4396
|
+
const ms = Date.parse(raw);
|
|
4397
|
+
if (!Number.isNaN(ms)) lastFiredAt = ms;
|
|
4398
|
+
}
|
|
4399
|
+
} catch {
|
|
4400
|
+
return { skip: false, reason: "state unreadable; firing" };
|
|
4401
|
+
}
|
|
4402
|
+
if (lastFiredAt === null) {
|
|
4403
|
+
return { skip: false, reason: `first tick (every ${every})` };
|
|
4404
|
+
}
|
|
4405
|
+
const intervalMs = scheduleEveryToMs(every);
|
|
4406
|
+
const elapsedMs = now - lastFiredAt;
|
|
4407
|
+
if (elapsedMs >= intervalMs) {
|
|
4408
|
+
return { skip: false, reason: `due (every ${every}, last ${formatAgo(elapsedMs)} ago)` };
|
|
4409
|
+
}
|
|
4410
|
+
const remainingMs = intervalMs - elapsedMs;
|
|
4411
|
+
return {
|
|
4412
|
+
skip: true,
|
|
4413
|
+
reason: `every ${every}; ${formatAgo(elapsedMs)} since last tick, next in ${formatAgo(remainingMs)}`
|
|
4414
|
+
};
|
|
4415
|
+
}
|
|
4416
|
+
function formatAgo(ms) {
|
|
4417
|
+
const sec = Math.max(0, Math.round(ms / 1e3));
|
|
4418
|
+
if (sec < 60) return `${sec}s`;
|
|
4419
|
+
const min = Math.round(sec / 60);
|
|
4420
|
+
if (min < 60) return `${min}m`;
|
|
4421
|
+
const hr = Math.round(min / 60);
|
|
4422
|
+
if (hr < 48) return `${hr}h`;
|
|
4423
|
+
const day = Math.round(hr / 24);
|
|
4424
|
+
return `${day}d`;
|
|
4425
|
+
}
|
|
4294
4426
|
function listJobSlugs(absDir) {
|
|
4295
4427
|
if (!fs19.existsSync(absDir)) return [];
|
|
4296
4428
|
let entries;
|
|
@@ -8123,9 +8255,16 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
8123
8255
|
if (!loaded) {
|
|
8124
8256
|
throw new Error("writeJobStateFile: ctx.data.jobState missing \u2014 preflight must run first");
|
|
8125
8257
|
}
|
|
8258
|
+
const stamped = {
|
|
8259
|
+
...next,
|
|
8260
|
+
data: {
|
|
8261
|
+
...next.data,
|
|
8262
|
+
lastFiredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8263
|
+
}
|
|
8264
|
+
};
|
|
8126
8265
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
8127
8266
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
8128
|
-
await backend.save(loaded,
|
|
8267
|
+
await backend.save(loaded, stamped);
|
|
8129
8268
|
};
|
|
8130
8269
|
|
|
8131
8270
|
// src/scripts/writeRunSummary.ts
|
|
File without changes
|
|
@@ -124,10 +124,19 @@ PY
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
ensure_goal_issue() {
|
|
127
|
-
# Create the umbrella goal issue (once), label it goal:<id> +
|
|
128
|
-
# and persist its number on state.json.
|
|
129
|
-
#
|
|
130
|
-
#
|
|
127
|
+
# Create-or-adopt the umbrella goal issue (once), label it goal:<id> +
|
|
128
|
+
# kody:building, and persist its number on state.json. The issue auto-closes
|
|
129
|
+
# when the final goal PR merges, via the `Closes #N` line we add to that PR
|
|
130
|
+
# body.
|
|
131
|
+
#
|
|
132
|
+
# Lookup order:
|
|
133
|
+
# 1. state.json `goalIssueNumber` — fast path.
|
|
134
|
+
# 2. Search GitHub for an existing umbrella by label `goal:<id>` + the
|
|
135
|
+
# canonical title `goal: <goal_id>`. This is the recovery path when
|
|
136
|
+
# state.json got wiped (e.g. dashboard pause/resume dropped the field
|
|
137
|
+
# in older versions). Without this lookup we'd open a duplicate
|
|
138
|
+
# umbrella every time goalIssueNumber goes missing.
|
|
139
|
+
# 3. Create a fresh umbrella as a last resort.
|
|
131
140
|
local existing
|
|
132
141
|
existing=$(read_state_field "goalIssueNumber")
|
|
133
142
|
if [ -n "$existing" ] && [ "$existing" != "0" ]; then
|
|
@@ -142,20 +151,34 @@ ensure_goal_issue() {
|
|
|
142
151
|
body=$(printf "Umbrella issue for goal **%s**.\n\nClosed automatically when the goal PR (\`%s\` → \`%s\`) merges.\n" \
|
|
143
152
|
"$goal_id" "$goal_branch" "$default_branch")
|
|
144
153
|
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
--
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
# Recovery path: an umbrella may already exist from a prior run that lost
|
|
155
|
+
# state. Match strictly by label + exact title to avoid grabbing a child
|
|
156
|
+
# task issue. Prefer OPEN issues; fall back to closed ones (the umbrella
|
|
157
|
+
# could have been closed by a prior goal PR merge that we're now re-driving).
|
|
158
|
+
num=$(gh api \
|
|
159
|
+
"repos/{owner}/{repo}/issues?labels=${label}&state=all&per_page=100" \
|
|
160
|
+
--jq "[.[] | select(.pull_request == null) | select(.title == \"${title}\")] | (map(select(.state == \"open\")) + map(select(.state != \"open\")))[0].number // empty" \
|
|
161
|
+
2>/dev/null || echo "")
|
|
162
|
+
|
|
163
|
+
if [ -n "$num" ] && [[ "$num" =~ ^[0-9]+$ ]]; then
|
|
164
|
+
echo "[goal-tick] adopted existing umbrella issue #${num} for ${goal_id}"
|
|
165
|
+
else
|
|
166
|
+
# `gh issue create` prints the new issue's URL on stdout
|
|
167
|
+
# (https://github.com/<owner>/<repo>/issues/<n>). It does NOT support
|
|
168
|
+
# --json/--jq, so parse the trailing number off the URL.
|
|
169
|
+
local url
|
|
170
|
+
url=$(gh issue create \
|
|
171
|
+
--title "$title" \
|
|
172
|
+
--body "$body" \
|
|
173
|
+
--label "$label" \
|
|
174
|
+
--label "kody:building" 2>/dev/null || echo "")
|
|
175
|
+
|
|
176
|
+
num="${url##*/}"
|
|
177
|
+
if [ -z "$num" ] || ! [[ "$num" =~ ^[0-9]+$ ]]; then
|
|
178
|
+
echo "[goal-tick] ensure_goal_issue: gh issue create failed (got '${url}') — continuing without umbrella issue"
|
|
179
|
+
return 0
|
|
180
|
+
fi
|
|
181
|
+
echo "[goal-tick] opened umbrella issue #${num} for ${goal_id}"
|
|
159
182
|
fi
|
|
160
183
|
|
|
161
184
|
python3 - "$state_file" "$num" <<'PY'
|
|
@@ -171,7 +194,6 @@ with open(path, "w") as f:
|
|
|
171
194
|
json.dump(s, f, indent=2)
|
|
172
195
|
f.write("\n")
|
|
173
196
|
PY
|
|
174
|
-
echo "[goal-tick] opened umbrella issue #${num} for ${goal_id}"
|
|
175
197
|
}
|
|
176
198
|
|
|
177
199
|
list_goal_issues() {
|
|
@@ -251,6 +273,66 @@ ensure_label "$failed_label" "b60205" "kody goal-runner: task failed; needs huma
|
|
|
251
273
|
# counting child tasks, so list_goal_issues can filter it out cleanly.
|
|
252
274
|
ensure_goal_issue
|
|
253
275
|
|
|
276
|
+
# Merge ready goal-task PRs into the goal branch. We own the merge here
|
|
277
|
+
# instead of relying on GitHub's `--auto` flag (which requires the repo's
|
|
278
|
+
# "Allow auto-merge" setting and silently no-ops when disabled). Only merge
|
|
279
|
+
# non-draft PRs with mergeable=MERGEABLE and mergeStateStatus=CLEAN — i.e.
|
|
280
|
+
# all required checks passed and there are no conflicts. Anything else
|
|
281
|
+
# (BLOCKED, DIRTY, BEHIND, UNSTABLE, draft) is left for the operator.
|
|
282
|
+
open_prs=$(gh pr list --base "$goal_branch" --state open --limit 50 \
|
|
283
|
+
--json number,isDraft,mergeable,mergeStateStatus 2>/dev/null || echo "[]")
|
|
284
|
+
echo "$open_prs" | python3 -c "
|
|
285
|
+
import json, sys
|
|
286
|
+
data = json.load(sys.stdin)
|
|
287
|
+
for pr in data:
|
|
288
|
+
if pr.get('isDraft'): continue
|
|
289
|
+
if pr.get('mergeable') != 'MERGEABLE': continue
|
|
290
|
+
if pr.get('mergeStateStatus') != 'CLEAN': continue
|
|
291
|
+
print(pr['number'])
|
|
292
|
+
" | while read -r pr_num; do
|
|
293
|
+
[ -n "$pr_num" ] || continue
|
|
294
|
+
echo "[goal-tick] merging PR #${pr_num} into ${goal_branch}"
|
|
295
|
+
if ! gh pr merge "$pr_num" --squash --delete-branch >/dev/null 2>&1; then
|
|
296
|
+
echo "[goal-tick] failed to merge PR #${pr_num} (continuing)"
|
|
297
|
+
fi
|
|
298
|
+
done
|
|
299
|
+
|
|
300
|
+
# Close dispatched task issues whose PR has merged into the goal branch.
|
|
301
|
+
# `Closes #N` in the PR body only auto-closes the issue when the PR merges
|
|
302
|
+
# into the default branch — goal-task PRs target the goal branch, so we must
|
|
303
|
+
# close the issues explicitly. Without this, in_flight stays > 0 forever and
|
|
304
|
+
# the goal stalls after task 1. We accept the linkage from either:
|
|
305
|
+
# - `Closes|Fixes|Resolves #N` in the PR body (authoritative), OR
|
|
306
|
+
# - leading number on the head ref (kody convention: `<issue>-<slug>`).
|
|
307
|
+
merged_prs=$(gh pr list --base "$goal_branch" --state merged --limit 50 --json number,headRefName,body 2>/dev/null || echo "[]")
|
|
308
|
+
echo "$merged_prs" | python3 -c "
|
|
309
|
+
import json, re, sys
|
|
310
|
+
data = json.load(sys.stdin)
|
|
311
|
+
seen = set()
|
|
312
|
+
for pr in data:
|
|
313
|
+
n = None
|
|
314
|
+
body = pr.get('body') or ''
|
|
315
|
+
m = re.search(r'(?i)\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)\b', body)
|
|
316
|
+
if m:
|
|
317
|
+
n = int(m.group(1))
|
|
318
|
+
else:
|
|
319
|
+
bm = re.match(r'^(\d+)-', pr.get('headRefName') or '')
|
|
320
|
+
if bm:
|
|
321
|
+
n = int(bm.group(1))
|
|
322
|
+
if n and n not in seen:
|
|
323
|
+
seen.add(n)
|
|
324
|
+
print(n)
|
|
325
|
+
" | while read -r issue_num; do
|
|
326
|
+
[ -n "$issue_num" ] || continue
|
|
327
|
+
state=$(gh issue view "$issue_num" --json state --jq .state 2>/dev/null || echo "")
|
|
328
|
+
if [ "$state" = "OPEN" ]; then
|
|
329
|
+
echo "[goal-tick] closing #${issue_num} (PR merged into ${goal_branch})"
|
|
330
|
+
gh issue close "$issue_num" \
|
|
331
|
+
--comment "_Closed by goal-tick: PR for this task merged into \`${goal_branch}\`._" \
|
|
332
|
+
>/dev/null 2>&1 || echo "[goal-tick] failed to close #${issue_num} (continuing)"
|
|
333
|
+
fi
|
|
334
|
+
done
|
|
335
|
+
|
|
254
336
|
issues_json=$(list_goal_issues)
|
|
255
337
|
total=$(echo "$issues_json" | python3 -c "import json,sys; print(len(json.load(sys.stdin)))")
|
|
256
338
|
if [ "$total" = "0" ]; then
|
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
"type": "string",
|
|
11
11
|
"required": true,
|
|
12
12
|
"describe": "Job slug — basename (without .md) of the file under .kody/jobs/."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "force",
|
|
16
|
+
"flag": "--force",
|
|
17
|
+
"type": "bool",
|
|
18
|
+
"describe": "When true, the agent ignores the job body's cadence guard and executes the work this tick. All other body rules (allowed commands, restrictions, state schema) still apply. Used for manual triggers from the dashboard's 'Run now' button."
|
|
13
19
|
}
|
|
14
20
|
],
|
|
15
21
|
"claudeCode": {
|
|
@@ -20,6 +20,8 @@ This is the state you wrote at the end of the previous tick (or `null` if this i
|
|
|
20
20
|
|
|
21
21
|
## What to do on this tick
|
|
22
22
|
|
|
23
|
+
`forceRun = {{args.force}}` — set to `true` when an operator clicked "Run now" on the dashboard. When `forceRun` is `true`, ignore the job body's `**Cadence guard.**` paragraph (or any equivalent "skip if last run was within X" rule) and execute the work as if the guard had passed. All other body rules — allowed commands, restrictions, state schema — still apply. Force only overrides cadence.
|
|
24
|
+
|
|
23
25
|
1. **Check `done`.** If the prior state has `done: true`, emit the same state back unchanged and exit without any action.
|
|
24
26
|
2. **Re-read the job body.** It may have changed since the last tick.
|
|
25
27
|
3. **Execute exactly the work the body's `## Job` section describes**, subject to its `## Allowed Commands` and `## Restrictions`. Use the `## State` section to interpret and update `data`.
|
|
@@ -45,8 +47,19 @@ If you fail to emit this block, or the JSON is invalid, the tick fails and the g
|
|
|
45
47
|
## Rules
|
|
46
48
|
|
|
47
49
|
- Never edit, create, or delete files in the working tree.
|
|
48
|
-
- Never commit or push.
|
|
50
|
+
- Never commit or push via `git`. The only permitted commit path is `gh api -X PUT` against the report file (see exception below).
|
|
49
51
|
- Only shell calls allowed: `gh`. Everything must go through it.
|
|
50
52
|
- Keep each tick focused: do one action per candidate per wake. The cron will call you again.
|
|
51
53
|
- If state says you're waiting on something, just check and re-emit — don't spawn a duplicate.
|
|
52
54
|
- Honour the job body's `## Restrictions` over any inferred shortcut.
|
|
55
|
+
|
|
56
|
+
### Single permitted write: the job's report file
|
|
57
|
+
|
|
58
|
+
A job MAY (optionally — only if its body asks for it) write a single
|
|
59
|
+
markdown report file at the canonical path:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
.kody/reports/{{jobSlug}}.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Only that exact path. Only via `gh api -X PUT /repos/<owner>/<repo>/contents/.kody/reports/{{jobSlug}}.md` (with base64 content + `sha` of the existing file when updating). All other writes — code files, other report paths, other slugs — remain forbidden. The dashboard's `/reports` page surfaces these files automatically; this is the canonical channel for a job's diagnostic output when an issue comment isn't expressive enough.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.19",
|
|
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"
|
|
@@ -44,5 +32,16 @@
|
|
|
44
32
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
45
33
|
},
|
|
46
34
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
47
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
48
|
-
|
|
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
|
+
}
|