@kody-ade/kody-engine 0.4.58 → 0.4.60
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 +643 -428
- package/dist/executables/fix/profile.json +4 -1
- package/dist/executables/fix/prompt.md +1 -1
- package/dist/executables/fix-ci/profile.json +4 -1
- package/dist/executables/fix-ci/prompt.md +1 -1
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- 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/dist/executables/run/profile.json +4 -1
- package/dist/executables/run/prompt.md +1 -1
- package/dist/executables/types.ts +25 -0
- package/package.json +17 -15
package/dist/bin/kody.js
CHANGED
|
@@ -1,9 +1,309 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/events.ts
|
|
13
|
+
import * as crypto from "crypto";
|
|
14
|
+
import * as fs3 from "fs";
|
|
15
|
+
import * as path3 from "path";
|
|
16
|
+
function resolveRunId() {
|
|
17
|
+
if (cachedRunId) return cachedRunId;
|
|
18
|
+
if (process.env.KODY_RUN_ID) {
|
|
19
|
+
cachedRunId = process.env.KODY_RUN_ID;
|
|
20
|
+
return cachedRunId;
|
|
21
|
+
}
|
|
22
|
+
if (process.env.GITHUB_RUN_ID) {
|
|
23
|
+
const attempt = process.env.GITHUB_RUN_ATTEMPT ?? "1";
|
|
24
|
+
cachedRunId = `gh-${process.env.GITHUB_RUN_ID}-${attempt}`;
|
|
25
|
+
} else {
|
|
26
|
+
cachedRunId = `${Date.now().toString(36)}-${crypto.randomBytes(4).toString("hex")}`;
|
|
27
|
+
}
|
|
28
|
+
process.env.KODY_RUN_ID = cachedRunId;
|
|
29
|
+
return cachedRunId;
|
|
30
|
+
}
|
|
31
|
+
function emitEvent(cwd, ev) {
|
|
32
|
+
if (process.env.KODY_EVENTS === "0") return;
|
|
33
|
+
try {
|
|
34
|
+
const runId = resolveRunId();
|
|
35
|
+
const fullEvent = {
|
|
36
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37
|
+
runId,
|
|
38
|
+
...ev
|
|
39
|
+
};
|
|
40
|
+
const eventsPath = path3.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
41
|
+
fs3.mkdirSync(path3.dirname(eventsPath), { recursive: true });
|
|
42
|
+
fs3.appendFileSync(eventsPath, `${JSON.stringify(fullEvent)}
|
|
43
|
+
`);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function readEvents(cwd, runId) {
|
|
48
|
+
const eventsPath = path3.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
49
|
+
if (!fs3.existsSync(eventsPath)) return [];
|
|
50
|
+
const lines = fs3.readFileSync(eventsPath, "utf-8").split("\n");
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed) continue;
|
|
55
|
+
try {
|
|
56
|
+
out.push(JSON.parse(trimmed));
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function listRuns(cwd) {
|
|
63
|
+
const runsDir = path3.join(cwd, ".kody", "runs");
|
|
64
|
+
if (!fs3.existsSync(runsDir)) return [];
|
|
65
|
+
return fs3.readdirSync(runsDir).filter((name) => {
|
|
66
|
+
try {
|
|
67
|
+
return fs3.statSync(path3.join(runsDir, name)).isDirectory();
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}).sort();
|
|
72
|
+
}
|
|
73
|
+
var cachedRunId;
|
|
74
|
+
var init_events = __esm({
|
|
75
|
+
"src/events.ts"() {
|
|
76
|
+
"use strict";
|
|
77
|
+
cachedRunId = null;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// src/verify.ts
|
|
82
|
+
import { spawn } from "child_process";
|
|
83
|
+
function runCommand(command, cwd) {
|
|
84
|
+
return new Promise((resolve4) => {
|
|
85
|
+
const start = Date.now();
|
|
86
|
+
const child = spawn(command, {
|
|
87
|
+
cwd,
|
|
88
|
+
shell: true,
|
|
89
|
+
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" },
|
|
90
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
91
|
+
});
|
|
92
|
+
const buffers = [];
|
|
93
|
+
let totalSize = 0;
|
|
94
|
+
const collect = (chunk) => {
|
|
95
|
+
buffers.push(chunk);
|
|
96
|
+
totalSize += chunk.length;
|
|
97
|
+
while (totalSize > TAIL_CHARS * 4 && buffers.length > 1) {
|
|
98
|
+
totalSize -= buffers[0].length;
|
|
99
|
+
buffers.shift();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
child.stdout?.on("data", collect);
|
|
103
|
+
child.stderr?.on("data", collect);
|
|
104
|
+
const timer = setTimeout(() => {
|
|
105
|
+
child.kill("SIGTERM");
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
if (!child.killed) child.kill("SIGKILL");
|
|
108
|
+
}, 5e3);
|
|
109
|
+
}, COMMAND_TIMEOUT_MS);
|
|
110
|
+
child.on("exit", (code) => {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
|
|
113
|
+
resolve4({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
|
|
114
|
+
});
|
|
115
|
+
child.on("error", (err) => {
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
resolve4({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async function verifyAll(config, cwd) {
|
|
122
|
+
const commands = [];
|
|
123
|
+
if (config.quality.typecheck) commands.push({ name: "typecheck", cmd: config.quality.typecheck });
|
|
124
|
+
if (config.quality.testUnit) commands.push({ name: "test", cmd: config.quality.testUnit });
|
|
125
|
+
if (config.quality.lint) commands.push({ name: "lint", cmd: config.quality.lint });
|
|
126
|
+
if (config.quality.format) commands.push({ name: "format", cmd: config.quality.format });
|
|
127
|
+
const failed = [];
|
|
128
|
+
const details = {};
|
|
129
|
+
for (const { name, cmd } of commands) {
|
|
130
|
+
const result = await runCommand(cmd, cwd);
|
|
131
|
+
details[name] = result;
|
|
132
|
+
if (result.exitCode !== 0) failed.push(name);
|
|
133
|
+
}
|
|
134
|
+
return { ok: failed.length === 0, failed, details };
|
|
135
|
+
}
|
|
136
|
+
async function applyTestRetries(initial, testCommand, cwd, runner, testRetries = DEFAULT_TEST_RETRIES) {
|
|
137
|
+
if (initial.ok) return { ...initial, recovered: [] };
|
|
138
|
+
const recovered = [];
|
|
139
|
+
const details = { ...initial.details };
|
|
140
|
+
let failed = [...initial.failed];
|
|
141
|
+
if (failed.includes("test") && testCommand && testRetries > 0) {
|
|
142
|
+
for (let attempt = 1; attempt <= testRetries; attempt++) {
|
|
143
|
+
const retry = await runner(testCommand, cwd);
|
|
144
|
+
details[`test (retry ${attempt})`] = retry;
|
|
145
|
+
if (retry.exitCode === 0) {
|
|
146
|
+
failed = failed.filter((f) => f !== "test");
|
|
147
|
+
recovered.push("test");
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { ok: failed.length === 0, failed, details, recovered };
|
|
153
|
+
}
|
|
154
|
+
async function verifyAllWithRetry(config, cwd, opts) {
|
|
155
|
+
const initial = await verifyAll(config, cwd);
|
|
156
|
+
return applyTestRetries(initial, config.quality.testUnit, cwd, runCommand, opts?.testRetries);
|
|
157
|
+
}
|
|
158
|
+
function stripAnsi(s) {
|
|
159
|
+
return s.replace(ANSI_RE, "");
|
|
160
|
+
}
|
|
161
|
+
function summarizeFailure(result) {
|
|
162
|
+
const lines = [`verify failed: ${result.failed.join(", ")}`];
|
|
163
|
+
for (const name of result.failed) {
|
|
164
|
+
const d = result.details[name];
|
|
165
|
+
if (!d) continue;
|
|
166
|
+
lines.push(`
|
|
167
|
+
--- ${name} (exit ${d.exitCode}, ${(d.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
168
|
+
lines.push(stripAnsi(d.tail));
|
|
169
|
+
for (let attempt = 1; ; attempt++) {
|
|
170
|
+
const retry = result.details[`${name} (retry ${attempt})`];
|
|
171
|
+
if (!retry) break;
|
|
172
|
+
lines.push(`
|
|
173
|
+
--- ${name} (retry ${attempt}: exit ${retry.exitCode}, ${(retry.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
174
|
+
lines.push(stripAnsi(retry.tail));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return lines.join("\n");
|
|
178
|
+
}
|
|
179
|
+
var TAIL_CHARS, COMMAND_TIMEOUT_MS, DEFAULT_TEST_RETRIES, ANSI_RE;
|
|
180
|
+
var init_verify = __esm({
|
|
181
|
+
"src/verify.ts"() {
|
|
182
|
+
"use strict";
|
|
183
|
+
TAIL_CHARS = 4e3;
|
|
184
|
+
COMMAND_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
185
|
+
DEFAULT_TEST_RETRIES = 2;
|
|
186
|
+
ANSI_RE = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// src/verifyMcp.ts
|
|
191
|
+
var verifyMcp_exports = {};
|
|
192
|
+
__export(verifyMcp_exports, {
|
|
193
|
+
DEFAULT_MAX_VERIFY_ATTEMPTS: () => DEFAULT_MAX_VERIFY_ATTEMPTS,
|
|
194
|
+
buildVerifyMcpServer: () => buildVerifyMcpServer,
|
|
195
|
+
truncateVerifyResult: () => truncateVerifyResult
|
|
196
|
+
});
|
|
197
|
+
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
198
|
+
import { z } from "zod";
|
|
199
|
+
function buildVerifyMcpServer(opts) {
|
|
200
|
+
const state = {
|
|
201
|
+
attempts: 0,
|
|
202
|
+
maxAttempts: opts.maxAttempts ?? DEFAULT_MAX_VERIFY_ATTEMPTS
|
|
203
|
+
};
|
|
204
|
+
const runVerify = opts.__runVerify ?? verifyAllWithRetry;
|
|
205
|
+
const verifyTool = tool(
|
|
206
|
+
"verify",
|
|
207
|
+
"Run the project's quality gates (typecheck, lint, tests). Returns ok=true with empty failures when everything passes. Call this before declaring DONE. If ok=false, read the truncated failures, fix the code, commit, and call verify() again. You have a bounded number of attempts; after that the tool stops accepting calls and you must wrap up with whatever state is current.",
|
|
208
|
+
{},
|
|
209
|
+
async (_args, _extra) => {
|
|
210
|
+
state.attempts++;
|
|
211
|
+
const attempt = state.attempts;
|
|
212
|
+
if (attempt > state.maxAttempts) {
|
|
213
|
+
emitEvent(opts.cwd, {
|
|
214
|
+
executable: opts.executable,
|
|
215
|
+
kind: "error",
|
|
216
|
+
name: "verify_tool",
|
|
217
|
+
outcome: "failed",
|
|
218
|
+
meta: { reason: "budget exhausted", attempts: attempt, maxAttempts: state.maxAttempts }
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
text: JSON.stringify({
|
|
225
|
+
ok: false,
|
|
226
|
+
locked: true,
|
|
227
|
+
reason: `verify budget exhausted (${state.maxAttempts} attempts used)`
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const startedAt = Date.now();
|
|
234
|
+
const result = await runVerify(opts.config, opts.cwd);
|
|
235
|
+
const durationMs = Date.now() - startedAt;
|
|
236
|
+
emitEvent(opts.cwd, {
|
|
237
|
+
executable: opts.executable,
|
|
238
|
+
kind: "postflight",
|
|
239
|
+
name: `verify_attempt_${attempt}`,
|
|
240
|
+
durationMs,
|
|
241
|
+
outcome: result.ok ? "ok" : "failed",
|
|
242
|
+
meta: {
|
|
243
|
+
attempt,
|
|
244
|
+
failureCount: result.failed.length,
|
|
245
|
+
recovered: result.recovered ?? []
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
const payload = truncateVerifyResult(result, state, attempt);
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{
|
|
252
|
+
type: "text",
|
|
253
|
+
text: JSON.stringify(payload)
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
return createSdkMcpServer({
|
|
260
|
+
name: "kody-verify",
|
|
261
|
+
version: "0.1.0",
|
|
262
|
+
tools: [verifyTool]
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function truncateVerifyResult(result, state, attempt) {
|
|
266
|
+
const failures = result.failed.slice(0, 5).map((name) => {
|
|
267
|
+
const detail = result.details[name];
|
|
268
|
+
const tail = detail?.tail ?? "";
|
|
269
|
+
return {
|
|
270
|
+
name,
|
|
271
|
+
exitCode: detail?.exitCode ?? -1,
|
|
272
|
+
tail: tail.length > PER_FAILURE_TAIL_CHARS ? `\u2026${tail.slice(-PER_FAILURE_TAIL_CHARS)}` : tail
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
const payload = {
|
|
276
|
+
ok: result.ok,
|
|
277
|
+
attempt,
|
|
278
|
+
attemptsRemaining: Math.max(0, state.maxAttempts - attempt),
|
|
279
|
+
failures,
|
|
280
|
+
...result.recovered && result.recovered.length > 0 ? { recovered: result.recovered } : {}
|
|
281
|
+
};
|
|
282
|
+
const json = JSON.stringify(payload);
|
|
283
|
+
if (json.length <= MAX_RESULT_BYTES) return payload;
|
|
284
|
+
for (const f of payload.failures) {
|
|
285
|
+
f.tail = f.tail.slice(-Math.max(120, Math.floor(PER_FAILURE_TAIL_CHARS / 4)));
|
|
286
|
+
if (JSON.stringify(payload).length <= MAX_RESULT_BYTES) return payload;
|
|
287
|
+
}
|
|
288
|
+
for (const f of payload.failures) f.tail = "";
|
|
289
|
+
return payload;
|
|
290
|
+
}
|
|
291
|
+
var DEFAULT_MAX_VERIFY_ATTEMPTS, MAX_RESULT_BYTES, PER_FAILURE_TAIL_CHARS;
|
|
292
|
+
var init_verifyMcp = __esm({
|
|
293
|
+
"src/verifyMcp.ts"() {
|
|
294
|
+
"use strict";
|
|
295
|
+
init_events();
|
|
296
|
+
init_verify();
|
|
297
|
+
DEFAULT_MAX_VERIFY_ATTEMPTS = 4;
|
|
298
|
+
MAX_RESULT_BYTES = 2048;
|
|
299
|
+
PER_FAILURE_TAIL_CHARS = 600;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
2
302
|
|
|
3
303
|
// package.json
|
|
4
304
|
var package_default = {
|
|
5
305
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.4.
|
|
306
|
+
version: "0.4.60",
|
|
7
307
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
308
|
license: "MIT",
|
|
9
309
|
type: "module",
|
|
@@ -29,7 +329,8 @@ var package_default = {
|
|
|
29
329
|
},
|
|
30
330
|
dependencies: {
|
|
31
331
|
"@actions/cache": "^6.0.0",
|
|
32
|
-
"@anthropic-ai/claude-agent-sdk": "0.2.119"
|
|
332
|
+
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
333
|
+
zod: "^4.0.0"
|
|
33
334
|
},
|
|
34
335
|
devDependencies: {
|
|
35
336
|
"@biomejs/biome": "^2.4.12",
|
|
@@ -52,8 +353,8 @@ var package_default = {
|
|
|
52
353
|
|
|
53
354
|
// src/chat-cli.ts
|
|
54
355
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
55
|
-
import * as
|
|
56
|
-
import * as
|
|
356
|
+
import * as fs33 from "fs";
|
|
357
|
+
import * as path31 from "path";
|
|
57
358
|
|
|
58
359
|
// src/chat/events.ts
|
|
59
360
|
import * as fs from "fs";
|
|
@@ -119,8 +420,8 @@ function makeRunId(sessionId, suffix) {
|
|
|
119
420
|
}
|
|
120
421
|
|
|
121
422
|
// src/agent.ts
|
|
122
|
-
import * as
|
|
123
|
-
import * as
|
|
423
|
+
import * as fs4 from "fs";
|
|
424
|
+
import * as path4 from "path";
|
|
124
425
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
125
426
|
|
|
126
427
|
// src/config.ts
|
|
@@ -412,10 +713,10 @@ function resolveTurnTimeoutMs(opts) {
|
|
|
412
713
|
return DEFAULT_TURN_TIMEOUT_MS;
|
|
413
714
|
}
|
|
414
715
|
async function runAgent(opts) {
|
|
415
|
-
const ndjsonDir = opts.ndjsonDir ??
|
|
416
|
-
|
|
417
|
-
const ndjsonPath =
|
|
418
|
-
const fullLog =
|
|
716
|
+
const ndjsonDir = opts.ndjsonDir ?? path4.join(opts.cwd, ".kody");
|
|
717
|
+
fs4.mkdirSync(ndjsonDir, { recursive: true });
|
|
718
|
+
const ndjsonPath = path4.join(ndjsonDir, "last-run.jsonl");
|
|
719
|
+
const fullLog = fs4.createWriteStream(ndjsonPath, { flags: "w" });
|
|
419
720
|
const env = {
|
|
420
721
|
...process.env,
|
|
421
722
|
SKIP_HOOKS: "1",
|
|
@@ -452,15 +753,27 @@ async function runAgent(opts) {
|
|
|
452
753
|
permissionMode: opts.permissionModeOverride ?? "acceptEdits",
|
|
453
754
|
env
|
|
454
755
|
};
|
|
756
|
+
const mcpEntries = [];
|
|
455
757
|
if (opts.mcpServers && opts.mcpServers.length > 0) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
758
|
+
for (const s of opts.mcpServers) {
|
|
759
|
+
const cfg = { command: s.command };
|
|
760
|
+
if (s.args) cfg.args = s.args;
|
|
761
|
+
if (s.env) cfg.env = s.env;
|
|
762
|
+
mcpEntries.push([s.name, cfg]);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
if (opts.enableVerifyTool && opts.verifyConfig) {
|
|
766
|
+
const { buildVerifyMcpServer: buildVerifyMcpServer2 } = await Promise.resolve().then(() => (init_verifyMcp(), verifyMcp_exports));
|
|
767
|
+
const verifyServer = buildVerifyMcpServer2({
|
|
768
|
+
config: opts.verifyConfig,
|
|
769
|
+
cwd: opts.cwd,
|
|
770
|
+
executable: opts.executableName ?? "agent",
|
|
771
|
+
maxAttempts: typeof opts.verifyToolMaxAttempts === "number" && opts.verifyToolMaxAttempts > 0 ? opts.verifyToolMaxAttempts : void 0
|
|
772
|
+
});
|
|
773
|
+
mcpEntries.push(["kody-verify", verifyServer]);
|
|
774
|
+
}
|
|
775
|
+
if (mcpEntries.length > 0) {
|
|
776
|
+
queryOptions.mcpServers = Object.fromEntries(mcpEntries);
|
|
464
777
|
}
|
|
465
778
|
if (opts.pluginPaths && opts.pluginPaths.length > 0) {
|
|
466
779
|
queryOptions.plugins = opts.pluginPaths.map((p) => ({ type: "local", path: p }));
|
|
@@ -588,14 +901,14 @@ async function runAgent(opts) {
|
|
|
588
901
|
}
|
|
589
902
|
|
|
590
903
|
// src/chat/session.ts
|
|
591
|
-
import * as
|
|
592
|
-
import * as
|
|
904
|
+
import * as fs5 from "fs";
|
|
905
|
+
import * as path5 from "path";
|
|
593
906
|
function sessionFilePath(cwd, sessionId) {
|
|
594
|
-
return
|
|
907
|
+
return path5.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
595
908
|
}
|
|
596
909
|
function readMeta(file) {
|
|
597
|
-
if (!
|
|
598
|
-
const raw =
|
|
910
|
+
if (!fs5.existsSync(file)) return null;
|
|
911
|
+
const raw = fs5.readFileSync(file, "utf-8");
|
|
599
912
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
600
913
|
if (!firstLine2) return null;
|
|
601
914
|
try {
|
|
@@ -608,8 +921,8 @@ function readMeta(file) {
|
|
|
608
921
|
}
|
|
609
922
|
}
|
|
610
923
|
function readSession(file) {
|
|
611
|
-
if (!
|
|
612
|
-
const raw =
|
|
924
|
+
if (!fs5.existsSync(file)) return [];
|
|
925
|
+
const raw = fs5.readFileSync(file, "utf-8").trim();
|
|
613
926
|
if (!raw) return [];
|
|
614
927
|
const turns = [];
|
|
615
928
|
for (const line of raw.split("\n")) {
|
|
@@ -625,14 +938,14 @@ function readSession(file) {
|
|
|
625
938
|
return turns;
|
|
626
939
|
}
|
|
627
940
|
function appendTurn(file, turn) {
|
|
628
|
-
|
|
941
|
+
fs5.mkdirSync(path5.dirname(file), { recursive: true });
|
|
629
942
|
const line = JSON.stringify({
|
|
630
943
|
role: turn.role,
|
|
631
944
|
content: turn.content,
|
|
632
945
|
timestamp: turn.timestamp,
|
|
633
946
|
toolCalls: turn.toolCalls ?? []
|
|
634
947
|
});
|
|
635
|
-
|
|
948
|
+
fs5.appendFileSync(file, `${line}
|
|
636
949
|
`);
|
|
637
950
|
}
|
|
638
951
|
function seedInitialMessage(file, message) {
|
|
@@ -777,8 +1090,8 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
777
1090
|
|
|
778
1091
|
// src/chat/modes/interactive.ts
|
|
779
1092
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
780
|
-
import * as
|
|
781
|
-
import * as
|
|
1093
|
+
import * as fs6 from "fs";
|
|
1094
|
+
import * as path6 from "path";
|
|
782
1095
|
|
|
783
1096
|
// src/chat/inbox.ts
|
|
784
1097
|
import { execFileSync } from "child_process";
|
|
@@ -935,9 +1248,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
935
1248
|
return -1;
|
|
936
1249
|
}
|
|
937
1250
|
function commitTurn(cwd, sessionId, verbose) {
|
|
938
|
-
const sessionRel =
|
|
939
|
-
const eventsRel =
|
|
940
|
-
const paths = [sessionRel, eventsRel].filter((p) =>
|
|
1251
|
+
const sessionRel = path6.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
1252
|
+
const eventsRel = path6.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
1253
|
+
const paths = [sessionRel, eventsRel].filter((p) => fs6.existsSync(path6.join(cwd, p)));
|
|
941
1254
|
if (paths.length === 0) return;
|
|
942
1255
|
const stdio = verbose ? "inherit" : "pipe";
|
|
943
1256
|
const exec = (args) => execFileSync2("git", args, { cwd, stdio });
|
|
@@ -1013,11 +1326,11 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
1013
1326
|
|
|
1014
1327
|
// src/kody-cli.ts
|
|
1015
1328
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
1016
|
-
import * as
|
|
1017
|
-
import * as
|
|
1329
|
+
import * as fs32 from "fs";
|
|
1330
|
+
import * as path30 from "path";
|
|
1018
1331
|
|
|
1019
1332
|
// src/dispatch.ts
|
|
1020
|
-
import * as
|
|
1333
|
+
import * as fs8 from "fs";
|
|
1021
1334
|
|
|
1022
1335
|
// src/cron-match.ts
|
|
1023
1336
|
var FIELD_BOUNDS = [
|
|
@@ -1082,48 +1395,48 @@ function cronMatchesInWindow(spec, end, windowSec) {
|
|
|
1082
1395
|
}
|
|
1083
1396
|
|
|
1084
1397
|
// src/registry.ts
|
|
1085
|
-
import * as
|
|
1086
|
-
import * as
|
|
1398
|
+
import * as fs7 from "fs";
|
|
1399
|
+
import * as path7 from "path";
|
|
1087
1400
|
function getExecutablesRoot() {
|
|
1088
|
-
const here =
|
|
1401
|
+
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
1089
1402
|
const candidates = [
|
|
1090
|
-
|
|
1403
|
+
path7.join(here, "executables"),
|
|
1091
1404
|
// dev: src/
|
|
1092
|
-
|
|
1405
|
+
path7.join(here, "..", "executables"),
|
|
1093
1406
|
// built: dist/bin → dist/executables
|
|
1094
|
-
|
|
1407
|
+
path7.join(here, "..", "src", "executables")
|
|
1095
1408
|
// fallback
|
|
1096
1409
|
];
|
|
1097
1410
|
for (const c of candidates) {
|
|
1098
|
-
if (
|
|
1411
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
1099
1412
|
}
|
|
1100
1413
|
return candidates[0];
|
|
1101
1414
|
}
|
|
1102
1415
|
function getProjectExecutablesRoot() {
|
|
1103
|
-
return
|
|
1416
|
+
return path7.join(process.cwd(), ".kody", "executables");
|
|
1104
1417
|
}
|
|
1105
1418
|
function getBuiltinJobsRoot() {
|
|
1106
|
-
const here =
|
|
1419
|
+
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
1107
1420
|
const candidates = [
|
|
1108
|
-
|
|
1421
|
+
path7.join(here, "jobs"),
|
|
1109
1422
|
// dev: src/
|
|
1110
|
-
|
|
1423
|
+
path7.join(here, "..", "jobs"),
|
|
1111
1424
|
// built: dist/bin → dist/jobs
|
|
1112
|
-
|
|
1425
|
+
path7.join(here, "..", "src", "jobs")
|
|
1113
1426
|
// fallback
|
|
1114
1427
|
];
|
|
1115
1428
|
for (const c of candidates) {
|
|
1116
|
-
if (
|
|
1429
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
1117
1430
|
}
|
|
1118
1431
|
return candidates[0];
|
|
1119
1432
|
}
|
|
1120
1433
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
1121
|
-
if (!
|
|
1434
|
+
if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
|
|
1122
1435
|
const out = [];
|
|
1123
|
-
for (const ent of
|
|
1436
|
+
for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
|
|
1124
1437
|
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
1125
1438
|
const slug = ent.name.slice(0, -3);
|
|
1126
|
-
out.push({ slug, filePath:
|
|
1439
|
+
out.push({ slug, filePath: path7.join(root, ent.name) });
|
|
1127
1440
|
}
|
|
1128
1441
|
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
1129
1442
|
return out;
|
|
@@ -1136,13 +1449,13 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
1136
1449
|
const seen = /* @__PURE__ */ new Set();
|
|
1137
1450
|
const out = [];
|
|
1138
1451
|
for (const root of rootList) {
|
|
1139
|
-
if (!
|
|
1140
|
-
const entries =
|
|
1452
|
+
if (!fs7.existsSync(root)) continue;
|
|
1453
|
+
const entries = fs7.readdirSync(root, { withFileTypes: true });
|
|
1141
1454
|
for (const ent of entries) {
|
|
1142
1455
|
if (!ent.isDirectory()) continue;
|
|
1143
1456
|
if (seen.has(ent.name)) continue;
|
|
1144
|
-
const profilePath =
|
|
1145
|
-
if (
|
|
1457
|
+
const profilePath = path7.join(root, ent.name, "profile.json");
|
|
1458
|
+
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
1146
1459
|
out.push({ name: ent.name, profilePath });
|
|
1147
1460
|
seen.add(ent.name);
|
|
1148
1461
|
}
|
|
@@ -1154,8 +1467,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
1154
1467
|
if (!isSafeName(name)) return null;
|
|
1155
1468
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
1156
1469
|
for (const root of rootList) {
|
|
1157
|
-
const profilePath =
|
|
1158
|
-
if (
|
|
1470
|
+
const profilePath = path7.join(root, name, "profile.json");
|
|
1471
|
+
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
1159
1472
|
return profilePath;
|
|
1160
1473
|
}
|
|
1161
1474
|
}
|
|
@@ -1171,7 +1484,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
1171
1484
|
const profilePath = resolveExecutable(name, roots);
|
|
1172
1485
|
if (!profilePath) return null;
|
|
1173
1486
|
try {
|
|
1174
|
-
const raw = JSON.parse(
|
|
1487
|
+
const raw = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
1175
1488
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
1176
1489
|
return raw.inputs;
|
|
1177
1490
|
} catch {
|
|
@@ -1220,10 +1533,10 @@ function autoDispatch(opts) {
|
|
|
1220
1533
|
}
|
|
1221
1534
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
1222
1535
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
1223
|
-
if (!eventName || !eventPath || !
|
|
1536
|
+
if (!eventName || !eventPath || !fs8.existsSync(eventPath)) return null;
|
|
1224
1537
|
let event = {};
|
|
1225
1538
|
try {
|
|
1226
|
-
event = JSON.parse(
|
|
1539
|
+
event = JSON.parse(fs8.readFileSync(eventPath, "utf-8"));
|
|
1227
1540
|
} catch {
|
|
1228
1541
|
return null;
|
|
1229
1542
|
}
|
|
@@ -1296,7 +1609,7 @@ function autoDispatchTyped(opts) {
|
|
|
1296
1609
|
if (legacy) return { kind: "route", ...legacy };
|
|
1297
1610
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
1298
1611
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
1299
|
-
if (!eventName || !eventPath || !
|
|
1612
|
+
if (!eventName || !eventPath || !fs8.existsSync(eventPath)) {
|
|
1300
1613
|
return { kind: "silent", reason: "no GHA event context" };
|
|
1301
1614
|
}
|
|
1302
1615
|
if (eventName !== "issue_comment") {
|
|
@@ -1304,7 +1617,7 @@ function autoDispatchTyped(opts) {
|
|
|
1304
1617
|
}
|
|
1305
1618
|
let event = {};
|
|
1306
1619
|
try {
|
|
1307
|
-
event = JSON.parse(
|
|
1620
|
+
event = JSON.parse(fs8.readFileSync(eventPath, "utf-8"));
|
|
1308
1621
|
} catch {
|
|
1309
1622
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
1310
1623
|
}
|
|
@@ -1338,7 +1651,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
1338
1651
|
for (const exe of listExecutables()) {
|
|
1339
1652
|
let raw;
|
|
1340
1653
|
try {
|
|
1341
|
-
raw =
|
|
1654
|
+
raw = fs8.readFileSync(exe.profilePath, "utf-8");
|
|
1342
1655
|
} catch {
|
|
1343
1656
|
continue;
|
|
1344
1657
|
}
|
|
@@ -1593,71 +1906,9 @@ function postPrReviewComment(prNumber, body, cwd) {
|
|
|
1593
1906
|
|
|
1594
1907
|
// src/executor.ts
|
|
1595
1908
|
import { execFileSync as execFileSync29, spawn as spawn5 } from "child_process";
|
|
1596
|
-
import * as
|
|
1597
|
-
import * as
|
|
1598
|
-
|
|
1599
|
-
// src/events.ts
|
|
1600
|
-
import * as crypto from "crypto";
|
|
1601
|
-
import * as fs8 from "fs";
|
|
1602
|
-
import * as path7 from "path";
|
|
1603
|
-
var cachedRunId = null;
|
|
1604
|
-
function resolveRunId() {
|
|
1605
|
-
if (cachedRunId) return cachedRunId;
|
|
1606
|
-
if (process.env.KODY_RUN_ID) {
|
|
1607
|
-
cachedRunId = process.env.KODY_RUN_ID;
|
|
1608
|
-
return cachedRunId;
|
|
1609
|
-
}
|
|
1610
|
-
if (process.env.GITHUB_RUN_ID) {
|
|
1611
|
-
const attempt = process.env.GITHUB_RUN_ATTEMPT ?? "1";
|
|
1612
|
-
cachedRunId = `gh-${process.env.GITHUB_RUN_ID}-${attempt}`;
|
|
1613
|
-
} else {
|
|
1614
|
-
cachedRunId = `${Date.now().toString(36)}-${crypto.randomBytes(4).toString("hex")}`;
|
|
1615
|
-
}
|
|
1616
|
-
process.env.KODY_RUN_ID = cachedRunId;
|
|
1617
|
-
return cachedRunId;
|
|
1618
|
-
}
|
|
1619
|
-
function emitEvent(cwd, ev) {
|
|
1620
|
-
if (process.env.KODY_EVENTS === "0") return;
|
|
1621
|
-
try {
|
|
1622
|
-
const runId = resolveRunId();
|
|
1623
|
-
const fullEvent = {
|
|
1624
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1625
|
-
runId,
|
|
1626
|
-
...ev
|
|
1627
|
-
};
|
|
1628
|
-
const eventsPath = path7.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
1629
|
-
fs8.mkdirSync(path7.dirname(eventsPath), { recursive: true });
|
|
1630
|
-
fs8.appendFileSync(eventsPath, `${JSON.stringify(fullEvent)}
|
|
1631
|
-
`);
|
|
1632
|
-
} catch {
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
function readEvents(cwd, runId) {
|
|
1636
|
-
const eventsPath = path7.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
1637
|
-
if (!fs8.existsSync(eventsPath)) return [];
|
|
1638
|
-
const lines = fs8.readFileSync(eventsPath, "utf-8").split("\n");
|
|
1639
|
-
const out = [];
|
|
1640
|
-
for (const line of lines) {
|
|
1641
|
-
const trimmed = line.trim();
|
|
1642
|
-
if (!trimmed) continue;
|
|
1643
|
-
try {
|
|
1644
|
-
out.push(JSON.parse(trimmed));
|
|
1645
|
-
} catch {
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
return out;
|
|
1649
|
-
}
|
|
1650
|
-
function listRuns(cwd) {
|
|
1651
|
-
const runsDir = path7.join(cwd, ".kody", "runs");
|
|
1652
|
-
if (!fs8.existsSync(runsDir)) return [];
|
|
1653
|
-
return fs8.readdirSync(runsDir).filter((name) => {
|
|
1654
|
-
try {
|
|
1655
|
-
return fs8.statSync(path7.join(runsDir, name)).isDirectory();
|
|
1656
|
-
} catch {
|
|
1657
|
-
return false;
|
|
1658
|
-
}
|
|
1659
|
-
}).sort();
|
|
1660
|
-
}
|
|
1909
|
+
import * as fs31 from "fs";
|
|
1910
|
+
import * as path29 from "path";
|
|
1911
|
+
init_events();
|
|
1661
1912
|
|
|
1662
1913
|
// src/profile.ts
|
|
1663
1914
|
import * as fs9 from "fs";
|
|
@@ -1721,6 +1972,10 @@ function loadProfile(profilePath) {
|
|
|
1721
1972
|
inputArtifacts: parseInputArtifacts(profilePath, r.input),
|
|
1722
1973
|
outputArtifacts: parseOutputArtifacts(profilePath, r.output),
|
|
1723
1974
|
children,
|
|
1975
|
+
// Default true: preserves legacy bug-safe behaviour where each
|
|
1976
|
+
// container child sees a clean tracked tree (see executor.ts).
|
|
1977
|
+
// Containers opt out by setting `"resetBetweenChildren": false`.
|
|
1978
|
+
resetBetweenChildren: typeof r.resetBetweenChildren === "boolean" ? r.resetBetweenChildren : true,
|
|
1724
1979
|
dir: path8.dirname(profilePath)
|
|
1725
1980
|
};
|
|
1726
1981
|
return profile;
|
|
@@ -1792,6 +2047,8 @@ function parseClaudeCode(p, raw) {
|
|
|
1792
2047
|
maxTurnTimeoutSec: typeof r.maxTurnTimeoutSec === "number" ? r.maxTurnTimeoutSec : null,
|
|
1793
2048
|
systemPromptAppend: typeof r.systemPromptAppend === "string" ? r.systemPromptAppend : null,
|
|
1794
2049
|
cacheable: r.cacheable === true,
|
|
2050
|
+
enableVerifyTool: r.enableVerifyTool === true,
|
|
2051
|
+
verifyAttempts: typeof r.verifyAttempts === "number" && r.verifyAttempts > 0 ? r.verifyAttempts : null,
|
|
1795
2052
|
tools,
|
|
1796
2053
|
hooks: Array.isArray(r.hooks) ? r.hooks : [],
|
|
1797
2054
|
skills: Array.isArray(r.skills) ? r.skills : [],
|
|
@@ -2085,7 +2342,7 @@ function errMsg(err) {
|
|
|
2085
2342
|
}
|
|
2086
2343
|
|
|
2087
2344
|
// src/litellm.ts
|
|
2088
|
-
import { execFileSync as execFileSync4, spawn } from "child_process";
|
|
2345
|
+
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
2089
2346
|
import * as fs10 from "fs";
|
|
2090
2347
|
import * as os from "os";
|
|
2091
2348
|
import * as path9 from "path";
|
|
@@ -2143,7 +2400,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
2143
2400
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
2144
2401
|
const logPath = path9.join(os.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
2145
2402
|
const outFd = fs10.openSync(logPath, "w");
|
|
2146
|
-
const child =
|
|
2403
|
+
const child = spawn2(cmd, args, {
|
|
2147
2404
|
stdio: ["ignore", outFd, outFd],
|
|
2148
2405
|
detached: true,
|
|
2149
2406
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
@@ -2977,13 +3234,36 @@ function defaultLabelMap() {
|
|
|
2977
3234
|
}
|
|
2978
3235
|
|
|
2979
3236
|
// src/scripts/commitAndPush.ts
|
|
3237
|
+
import * as fs14 from "fs";
|
|
3238
|
+
import * as path13 from "path";
|
|
3239
|
+
init_events();
|
|
2980
3240
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
2981
|
-
|
|
3241
|
+
function sentinelPathForStage(cwd, profileName) {
|
|
3242
|
+
const runId = resolveRunId();
|
|
3243
|
+
return path13.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
3244
|
+
}
|
|
3245
|
+
var commitAndPush2 = async (ctx, profile) => {
|
|
2982
3246
|
const branch = ctx.data.branch;
|
|
2983
3247
|
if (!branch) {
|
|
2984
3248
|
ctx.data.commitResult = { committed: false, pushed: false };
|
|
2985
3249
|
return;
|
|
2986
3250
|
}
|
|
3251
|
+
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
3252
|
+
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
3253
|
+
if (sentinel && fs14.existsSync(sentinel)) {
|
|
3254
|
+
try {
|
|
3255
|
+
const replay = JSON.parse(fs14.readFileSync(sentinel, "utf-8"));
|
|
3256
|
+
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
3257
|
+
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
3258
|
+
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
3259
|
+
if (replay.salvagedFromMissingMarker) ctx.data.salvagedFromMissingMarker = true;
|
|
3260
|
+
ctx.data.commitIdempotencyReplay = true;
|
|
3261
|
+
process.stderr.write(`[kody commitAndPush] idempotency replay (sentinel ${sentinel})
|
|
3262
|
+
`);
|
|
3263
|
+
return;
|
|
3264
|
+
} catch {
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
2987
3267
|
const markerMissing = ctx.data.agentMarkerMissing === true;
|
|
2988
3268
|
if (ctx.data.agentDone === false && !markerMissing) {
|
|
2989
3269
|
ctx.data.commitResult = { committed: false, pushed: false, skippedReason: "agentDone=false" };
|
|
@@ -2995,12 +3275,12 @@ var commitAndPush2 = async (ctx) => {
|
|
|
2995
3275
|
}
|
|
2996
3276
|
const message = ctx.data.commitMessage || DEFAULT_COMMIT_MESSAGE;
|
|
2997
3277
|
try {
|
|
2998
|
-
const
|
|
2999
|
-
ctx.data.commitResult =
|
|
3000
|
-
const postCommitFiles =
|
|
3278
|
+
const result2 = commitAndPush(branch, message, ctx.cwd);
|
|
3279
|
+
ctx.data.commitResult = result2;
|
|
3280
|
+
const postCommitFiles = result2.committed ? listFilesInCommit("HEAD", ctx.cwd) : listChangedFiles(ctx.cwd);
|
|
3001
3281
|
ctx.data.changedFiles = postCommitFiles.filter((f) => !isForbiddenPath(f));
|
|
3002
|
-
if (
|
|
3003
|
-
const reason =
|
|
3282
|
+
if (result2.committed && !result2.pushed) {
|
|
3283
|
+
const reason = result2.pushError ?? "push failed (no error detail)";
|
|
3004
3284
|
ctx.data.commitCrash = reason;
|
|
3005
3285
|
if (ctx.output.exitCode === void 0 || ctx.output.exitCode === 0) {
|
|
3006
3286
|
ctx.output.exitCode = 4;
|
|
@@ -3017,15 +3297,36 @@ var commitAndPush2 = async (ctx) => {
|
|
|
3017
3297
|
`);
|
|
3018
3298
|
}
|
|
3019
3299
|
ctx.data.hasCommitsAhead = hasCommitsAhead(branch, ctx.config.git.defaultBranch, ctx.cwd);
|
|
3300
|
+
const result = ctx.data.commitResult;
|
|
3301
|
+
if (sentinel && result?.committed) {
|
|
3302
|
+
try {
|
|
3303
|
+
fs14.mkdirSync(path13.dirname(sentinel), { recursive: true });
|
|
3304
|
+
fs14.writeFileSync(
|
|
3305
|
+
sentinel,
|
|
3306
|
+
JSON.stringify(
|
|
3307
|
+
{
|
|
3308
|
+
commitResult: ctx.data.commitResult,
|
|
3309
|
+
changedFiles: ctx.data.changedFiles,
|
|
3310
|
+
hasCommitsAhead: ctx.data.hasCommitsAhead,
|
|
3311
|
+
salvagedFromMissingMarker: ctx.data.salvagedFromMissingMarker === true,
|
|
3312
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3313
|
+
},
|
|
3314
|
+
null,
|
|
3315
|
+
2
|
|
3316
|
+
)
|
|
3317
|
+
);
|
|
3318
|
+
} catch {
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3020
3321
|
};
|
|
3021
3322
|
|
|
3022
3323
|
// src/scripts/commitGoalState.ts
|
|
3023
3324
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
3024
|
-
import * as
|
|
3325
|
+
import * as path14 from "path";
|
|
3025
3326
|
var commitGoalState = async (ctx) => {
|
|
3026
3327
|
const goal = ctx.data.goal;
|
|
3027
3328
|
if (!goal) return;
|
|
3028
|
-
const stateRel =
|
|
3329
|
+
const stateRel = path14.posix.join(".kody", "goals", goal.id, "state.json");
|
|
3029
3330
|
try {
|
|
3030
3331
|
execFileSync9("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
3031
3332
|
} catch (err) {
|
|
@@ -3069,20 +3370,20 @@ function describeCommitMessage(goal) {
|
|
|
3069
3370
|
}
|
|
3070
3371
|
|
|
3071
3372
|
// src/scripts/composePrompt.ts
|
|
3072
|
-
import * as
|
|
3073
|
-
import * as
|
|
3373
|
+
import * as fs15 from "fs";
|
|
3374
|
+
import * as path15 from "path";
|
|
3074
3375
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
3075
3376
|
var composePrompt = async (ctx, profile) => {
|
|
3076
3377
|
const explicit = ctx.data.promptTemplate;
|
|
3077
3378
|
const mode = ctx.args.mode;
|
|
3078
3379
|
const candidates = [
|
|
3079
|
-
explicit ?
|
|
3080
|
-
mode ?
|
|
3081
|
-
|
|
3380
|
+
explicit ? path15.join(profile.dir, explicit) : null,
|
|
3381
|
+
mode ? path15.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
3382
|
+
path15.join(profile.dir, "prompt.md")
|
|
3082
3383
|
].filter(Boolean);
|
|
3083
3384
|
let templatePath = "";
|
|
3084
3385
|
for (const c of candidates) {
|
|
3085
|
-
if (
|
|
3386
|
+
if (fs15.existsSync(c)) {
|
|
3086
3387
|
templatePath = c;
|
|
3087
3388
|
break;
|
|
3088
3389
|
}
|
|
@@ -3090,7 +3391,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
3090
3391
|
if (!templatePath) {
|
|
3091
3392
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
3092
3393
|
}
|
|
3093
|
-
const template =
|
|
3394
|
+
const template = fs15.readFileSync(templatePath, "utf-8");
|
|
3094
3395
|
const tokens = {
|
|
3095
3396
|
...stringifyAll(ctx.args, "args."),
|
|
3096
3397
|
...stringifyAll(ctx.data, ""),
|
|
@@ -3168,8 +3469,8 @@ function formatToolsUsage(profile) {
|
|
|
3168
3469
|
|
|
3169
3470
|
// src/scripts/createQaGoal.ts
|
|
3170
3471
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
3171
|
-
import * as
|
|
3172
|
-
import * as
|
|
3472
|
+
import * as fs16 from "fs";
|
|
3473
|
+
import * as path16 from "path";
|
|
3173
3474
|
|
|
3174
3475
|
// src/scripts/postReviewResult.ts
|
|
3175
3476
|
function detectVerdict(body) {
|
|
@@ -3421,8 +3722,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
3421
3722
|
return { number: Number(m[1]), created: true };
|
|
3422
3723
|
}
|
|
3423
3724
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
3424
|
-
const dir =
|
|
3425
|
-
|
|
3725
|
+
const dir = path16.join(cwd, ".kody", "goals", goalId);
|
|
3726
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
3426
3727
|
const state = {
|
|
3427
3728
|
version: 1,
|
|
3428
3729
|
state: "active",
|
|
@@ -3430,8 +3731,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
3430
3731
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3431
3732
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
3432
3733
|
};
|
|
3433
|
-
const filePath =
|
|
3434
|
-
|
|
3734
|
+
const filePath = path16.join(dir, "state.json");
|
|
3735
|
+
fs16.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
3435
3736
|
`);
|
|
3436
3737
|
return filePath;
|
|
3437
3738
|
}
|
|
@@ -3836,7 +4137,10 @@ function mergePrSquash(prNumber, cwd) {
|
|
|
3836
4137
|
}
|
|
3837
4138
|
function editPrBase(prNumber, baseBranch, cwd) {
|
|
3838
4139
|
try {
|
|
3839
|
-
gh(
|
|
4140
|
+
gh(
|
|
4141
|
+
["api", "--method", "PATCH", `repos/{owner}/{repo}/pulls/${prNumber}`, "-f", `base=${baseBranch}`],
|
|
4142
|
+
{ cwd }
|
|
4143
|
+
);
|
|
3840
4144
|
return { ok: true };
|
|
3841
4145
|
} catch (err) {
|
|
3842
4146
|
return fail(err);
|
|
@@ -3921,15 +4225,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
3921
4225
|
|
|
3922
4226
|
// src/scripts/diagMcp.ts
|
|
3923
4227
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
3924
|
-
import * as
|
|
4228
|
+
import * as fs17 from "fs";
|
|
3925
4229
|
import * as os3 from "os";
|
|
3926
|
-
import * as
|
|
4230
|
+
import * as path17 from "path";
|
|
3927
4231
|
var diagMcp = async (_ctx) => {
|
|
3928
4232
|
const home = os3.homedir();
|
|
3929
|
-
const cacheDir =
|
|
4233
|
+
const cacheDir = path17.join(home, ".cache", "ms-playwright");
|
|
3930
4234
|
let entries = [];
|
|
3931
4235
|
try {
|
|
3932
|
-
entries =
|
|
4236
|
+
entries = fs17.readdirSync(cacheDir);
|
|
3933
4237
|
} catch {
|
|
3934
4238
|
}
|
|
3935
4239
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -3955,17 +4259,17 @@ var diagMcp = async (_ctx) => {
|
|
|
3955
4259
|
};
|
|
3956
4260
|
|
|
3957
4261
|
// src/scripts/discoverQaContext.ts
|
|
3958
|
-
import * as
|
|
3959
|
-
import * as
|
|
4262
|
+
import * as fs19 from "fs";
|
|
4263
|
+
import * as path19 from "path";
|
|
3960
4264
|
|
|
3961
4265
|
// src/scripts/frameworkDetectors.ts
|
|
3962
|
-
import * as
|
|
3963
|
-
import * as
|
|
4266
|
+
import * as fs18 from "fs";
|
|
4267
|
+
import * as path18 from "path";
|
|
3964
4268
|
function detectFrameworks(cwd) {
|
|
3965
4269
|
const out = [];
|
|
3966
4270
|
let deps = {};
|
|
3967
4271
|
try {
|
|
3968
|
-
const pkg = JSON.parse(
|
|
4272
|
+
const pkg = JSON.parse(fs18.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
|
|
3969
4273
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3970
4274
|
} catch {
|
|
3971
4275
|
return out;
|
|
@@ -4002,7 +4306,7 @@ function detectFrameworks(cwd) {
|
|
|
4002
4306
|
}
|
|
4003
4307
|
function findFile(cwd, candidates) {
|
|
4004
4308
|
for (const c of candidates) {
|
|
4005
|
-
if (
|
|
4309
|
+
if (fs18.existsSync(path18.join(cwd, c))) return c;
|
|
4006
4310
|
}
|
|
4007
4311
|
return null;
|
|
4008
4312
|
}
|
|
@@ -4015,18 +4319,18 @@ var COLLECTION_DIRS = [
|
|
|
4015
4319
|
function discoverPayloadCollections(cwd) {
|
|
4016
4320
|
const out = [];
|
|
4017
4321
|
for (const dir of COLLECTION_DIRS) {
|
|
4018
|
-
const full =
|
|
4019
|
-
if (!
|
|
4322
|
+
const full = path18.join(cwd, dir);
|
|
4323
|
+
if (!fs18.existsSync(full)) continue;
|
|
4020
4324
|
let files;
|
|
4021
4325
|
try {
|
|
4022
|
-
files =
|
|
4326
|
+
files = fs18.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
4023
4327
|
} catch {
|
|
4024
4328
|
continue;
|
|
4025
4329
|
}
|
|
4026
4330
|
for (const file of files) {
|
|
4027
4331
|
try {
|
|
4028
|
-
const filePath =
|
|
4029
|
-
const content =
|
|
4332
|
+
const filePath = path18.join(full, file);
|
|
4333
|
+
const content = fs18.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
4030
4334
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
4031
4335
|
if (!slugMatch) continue;
|
|
4032
4336
|
const slug = slugMatch[1];
|
|
@@ -4040,7 +4344,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
4040
4344
|
out.push({
|
|
4041
4345
|
name,
|
|
4042
4346
|
slug,
|
|
4043
|
-
filePath:
|
|
4347
|
+
filePath: path18.relative(cwd, filePath),
|
|
4044
4348
|
fields: fields.slice(0, 20),
|
|
4045
4349
|
hasAdmin
|
|
4046
4350
|
});
|
|
@@ -4054,28 +4358,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
4054
4358
|
function discoverAdminComponents(cwd, collections) {
|
|
4055
4359
|
const out = [];
|
|
4056
4360
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
4057
|
-
const full =
|
|
4058
|
-
if (!
|
|
4361
|
+
const full = path18.join(cwd, dir);
|
|
4362
|
+
if (!fs18.existsSync(full)) continue;
|
|
4059
4363
|
let entries;
|
|
4060
4364
|
try {
|
|
4061
|
-
entries =
|
|
4365
|
+
entries = fs18.readdirSync(full, { withFileTypes: true });
|
|
4062
4366
|
} catch {
|
|
4063
4367
|
continue;
|
|
4064
4368
|
}
|
|
4065
4369
|
for (const entry of entries) {
|
|
4066
|
-
const entryPath =
|
|
4370
|
+
const entryPath = path18.join(full, entry.name);
|
|
4067
4371
|
let name;
|
|
4068
4372
|
let filePath;
|
|
4069
4373
|
if (entry.isDirectory()) {
|
|
4070
4374
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
4071
|
-
(f) =>
|
|
4375
|
+
(f) => fs18.existsSync(path18.join(entryPath, f))
|
|
4072
4376
|
);
|
|
4073
4377
|
if (!indexFile) continue;
|
|
4074
4378
|
name = entry.name;
|
|
4075
|
-
filePath =
|
|
4379
|
+
filePath = path18.relative(cwd, path18.join(entryPath, indexFile));
|
|
4076
4380
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
4077
4381
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
4078
|
-
filePath =
|
|
4382
|
+
filePath = path18.relative(cwd, entryPath);
|
|
4079
4383
|
} else {
|
|
4080
4384
|
continue;
|
|
4081
4385
|
}
|
|
@@ -4083,7 +4387,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
4083
4387
|
if (collections) {
|
|
4084
4388
|
for (const col of collections) {
|
|
4085
4389
|
try {
|
|
4086
|
-
const colContent =
|
|
4390
|
+
const colContent = fs18.readFileSync(path18.join(cwd, col.filePath), "utf-8");
|
|
4087
4391
|
if (colContent.includes(name)) {
|
|
4088
4392
|
usedInCollection = col.slug;
|
|
4089
4393
|
break;
|
|
@@ -4102,8 +4406,8 @@ function scanApiRoutes(cwd) {
|
|
|
4102
4406
|
const out = [];
|
|
4103
4407
|
const appDirs = ["src/app", "app"];
|
|
4104
4408
|
for (const appDir of appDirs) {
|
|
4105
|
-
const apiDir =
|
|
4106
|
-
if (!
|
|
4409
|
+
const apiDir = path18.join(cwd, appDir, "api");
|
|
4410
|
+
if (!fs18.existsSync(apiDir)) continue;
|
|
4107
4411
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
4108
4412
|
break;
|
|
4109
4413
|
}
|
|
@@ -4112,14 +4416,14 @@ function scanApiRoutes(cwd) {
|
|
|
4112
4416
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
4113
4417
|
let entries;
|
|
4114
4418
|
try {
|
|
4115
|
-
entries =
|
|
4419
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
4116
4420
|
} catch {
|
|
4117
4421
|
return;
|
|
4118
4422
|
}
|
|
4119
4423
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
4120
4424
|
if (routeFile) {
|
|
4121
4425
|
try {
|
|
4122
|
-
const content =
|
|
4426
|
+
const content = fs18.readFileSync(path18.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
4123
4427
|
const methods = HTTP_METHODS.filter(
|
|
4124
4428
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
4125
4429
|
);
|
|
@@ -4127,7 +4431,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
4127
4431
|
out.push({
|
|
4128
4432
|
path: prefix,
|
|
4129
4433
|
methods,
|
|
4130
|
-
filePath:
|
|
4434
|
+
filePath: path18.relative(cwd, path18.join(dir, routeFile.name))
|
|
4131
4435
|
});
|
|
4132
4436
|
}
|
|
4133
4437
|
} catch {
|
|
@@ -4138,7 +4442,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
4138
4442
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
4139
4443
|
let segment = entry.name;
|
|
4140
4444
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
4141
|
-
walkApiRoutes(
|
|
4445
|
+
walkApiRoutes(path18.join(dir, entry.name), prefix, cwd, out);
|
|
4142
4446
|
continue;
|
|
4143
4447
|
}
|
|
4144
4448
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -4146,7 +4450,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
4146
4450
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
4147
4451
|
segment = `:${segment.slice(1, -1)}`;
|
|
4148
4452
|
}
|
|
4149
|
-
walkApiRoutes(
|
|
4453
|
+
walkApiRoutes(path18.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
4150
4454
|
}
|
|
4151
4455
|
}
|
|
4152
4456
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -4166,10 +4470,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
4166
4470
|
function scanEnvVars(cwd) {
|
|
4167
4471
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
4168
4472
|
for (const envFile of candidates) {
|
|
4169
|
-
const envPath =
|
|
4170
|
-
if (!
|
|
4473
|
+
const envPath = path18.join(cwd, envFile);
|
|
4474
|
+
if (!fs18.existsSync(envPath)) continue;
|
|
4171
4475
|
try {
|
|
4172
|
-
const content =
|
|
4476
|
+
const content = fs18.readFileSync(envPath, "utf-8");
|
|
4173
4477
|
const vars = [];
|
|
4174
4478
|
for (const line of content.split("\n")) {
|
|
4175
4479
|
const trimmed = line.trim();
|
|
@@ -4217,9 +4521,9 @@ function runQaDiscovery(cwd) {
|
|
|
4217
4521
|
}
|
|
4218
4522
|
function detectDevServer(cwd, out) {
|
|
4219
4523
|
try {
|
|
4220
|
-
const pkg = JSON.parse(
|
|
4524
|
+
const pkg = JSON.parse(fs19.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
|
|
4221
4525
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4222
|
-
const pm =
|
|
4526
|
+
const pm = fs19.existsSync(path19.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs19.existsSync(path19.join(cwd, "yarn.lock")) ? "yarn" : fs19.existsSync(path19.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
4223
4527
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
4224
4528
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
4225
4529
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -4229,8 +4533,8 @@ function detectDevServer(cwd, out) {
|
|
|
4229
4533
|
function scanFrontendRoutes(cwd, out) {
|
|
4230
4534
|
const appDirs = ["src/app", "app"];
|
|
4231
4535
|
for (const appDir of appDirs) {
|
|
4232
|
-
const full =
|
|
4233
|
-
if (!
|
|
4536
|
+
const full = path19.join(cwd, appDir);
|
|
4537
|
+
if (!fs19.existsSync(full)) continue;
|
|
4234
4538
|
walkFrontendRoutes(full, "", out);
|
|
4235
4539
|
break;
|
|
4236
4540
|
}
|
|
@@ -4238,7 +4542,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
4238
4542
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
4239
4543
|
let entries;
|
|
4240
4544
|
try {
|
|
4241
|
-
entries =
|
|
4545
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
4242
4546
|
} catch {
|
|
4243
4547
|
return;
|
|
4244
4548
|
}
|
|
@@ -4255,7 +4559,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
4255
4559
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
4256
4560
|
let segment = entry.name;
|
|
4257
4561
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
4258
|
-
walkFrontendRoutes(
|
|
4562
|
+
walkFrontendRoutes(path19.join(dir, entry.name), prefix, out);
|
|
4259
4563
|
continue;
|
|
4260
4564
|
}
|
|
4261
4565
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -4263,7 +4567,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
4263
4567
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
4264
4568
|
segment = `:${segment.slice(1, -1)}`;
|
|
4265
4569
|
}
|
|
4266
|
-
walkFrontendRoutes(
|
|
4570
|
+
walkFrontendRoutes(path19.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
4267
4571
|
}
|
|
4268
4572
|
}
|
|
4269
4573
|
function detectAuthFiles(cwd, out) {
|
|
@@ -4280,23 +4584,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
4280
4584
|
"src/app/api/oauth"
|
|
4281
4585
|
];
|
|
4282
4586
|
for (const c of candidates) {
|
|
4283
|
-
if (
|
|
4587
|
+
if (fs19.existsSync(path19.join(cwd, c))) out.authFiles.push(c);
|
|
4284
4588
|
}
|
|
4285
4589
|
}
|
|
4286
4590
|
function detectRoles(cwd, out) {
|
|
4287
4591
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
4288
4592
|
for (const rp of rolePaths) {
|
|
4289
|
-
const dir =
|
|
4290
|
-
if (!
|
|
4593
|
+
const dir = path19.join(cwd, rp);
|
|
4594
|
+
if (!fs19.existsSync(dir)) continue;
|
|
4291
4595
|
let files;
|
|
4292
4596
|
try {
|
|
4293
|
-
files =
|
|
4597
|
+
files = fs19.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
4294
4598
|
} catch {
|
|
4295
4599
|
continue;
|
|
4296
4600
|
}
|
|
4297
4601
|
for (const f of files) {
|
|
4298
4602
|
try {
|
|
4299
|
-
const content =
|
|
4603
|
+
const content = fs19.readFileSync(path19.join(dir, f), "utf-8").slice(0, 5e3);
|
|
4300
4604
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
4301
4605
|
if (roleMatches) {
|
|
4302
4606
|
for (const m of roleMatches) {
|
|
@@ -4529,8 +4833,8 @@ function failedAction3(reason) {
|
|
|
4529
4833
|
}
|
|
4530
4834
|
|
|
4531
4835
|
// src/scripts/dispatchJobFileTicks.ts
|
|
4532
|
-
import * as
|
|
4533
|
-
import * as
|
|
4836
|
+
import * as fs21 from "fs";
|
|
4837
|
+
import * as path21 from "path";
|
|
4534
4838
|
|
|
4535
4839
|
// src/scripts/jobFrontmatter.ts
|
|
4536
4840
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -4781,8 +5085,8 @@ var ContentsApiBackend = class {
|
|
|
4781
5085
|
};
|
|
4782
5086
|
|
|
4783
5087
|
// src/scripts/jobState/localFileBackend.ts
|
|
4784
|
-
import * as
|
|
4785
|
-
import * as
|
|
5088
|
+
import * as fs20 from "fs";
|
|
5089
|
+
import * as path20 from "path";
|
|
4786
5090
|
var LocalFileBackend = class {
|
|
4787
5091
|
name = "local-file";
|
|
4788
5092
|
cwd;
|
|
@@ -4797,7 +5101,7 @@ var LocalFileBackend = class {
|
|
|
4797
5101
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
4798
5102
|
this.cwd = opts.cwd;
|
|
4799
5103
|
this.jobsDir = opts.jobsDir;
|
|
4800
|
-
this.absDir =
|
|
5104
|
+
this.absDir = path20.join(opts.cwd, opts.jobsDir);
|
|
4801
5105
|
this.owner = opts.owner;
|
|
4802
5106
|
this.repo = opts.repo;
|
|
4803
5107
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -4812,7 +5116,7 @@ var LocalFileBackend = class {
|
|
|
4812
5116
|
`);
|
|
4813
5117
|
return;
|
|
4814
5118
|
}
|
|
4815
|
-
|
|
5119
|
+
fs20.mkdirSync(this.absDir, { recursive: true });
|
|
4816
5120
|
const prefix = this.cacheKeyPrefix();
|
|
4817
5121
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
4818
5122
|
try {
|
|
@@ -4841,7 +5145,7 @@ var LocalFileBackend = class {
|
|
|
4841
5145
|
`);
|
|
4842
5146
|
return;
|
|
4843
5147
|
}
|
|
4844
|
-
if (!
|
|
5148
|
+
if (!fs20.existsSync(this.absDir)) {
|
|
4845
5149
|
return;
|
|
4846
5150
|
}
|
|
4847
5151
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -4857,11 +5161,11 @@ var LocalFileBackend = class {
|
|
|
4857
5161
|
}
|
|
4858
5162
|
load(slug) {
|
|
4859
5163
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
4860
|
-
const absPath =
|
|
4861
|
-
if (!
|
|
5164
|
+
const absPath = path20.join(this.cwd, relPath);
|
|
5165
|
+
if (!fs20.existsSync(absPath)) {
|
|
4862
5166
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
4863
5167
|
}
|
|
4864
|
-
const raw =
|
|
5168
|
+
const raw = fs20.readFileSync(absPath, "utf-8");
|
|
4865
5169
|
let parsed;
|
|
4866
5170
|
try {
|
|
4867
5171
|
parsed = JSON.parse(raw);
|
|
@@ -4878,10 +5182,10 @@ var LocalFileBackend = class {
|
|
|
4878
5182
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
4879
5183
|
return false;
|
|
4880
5184
|
}
|
|
4881
|
-
const absPath =
|
|
4882
|
-
|
|
5185
|
+
const absPath = path20.join(this.cwd, loaded.path);
|
|
5186
|
+
fs20.mkdirSync(path20.dirname(absPath), { recursive: true });
|
|
4883
5187
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
4884
|
-
|
|
5188
|
+
fs20.writeFileSync(absPath, body, "utf-8");
|
|
4885
5189
|
return true;
|
|
4886
5190
|
}
|
|
4887
5191
|
cacheKeyPrefix() {
|
|
@@ -4958,7 +5262,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
4958
5262
|
await backend.hydrate();
|
|
4959
5263
|
}
|
|
4960
5264
|
try {
|
|
4961
|
-
const slugs = listJobSlugs(
|
|
5265
|
+
const slugs = listJobSlugs(path21.join(ctx.cwd, jobsDir));
|
|
4962
5266
|
ctx.data.jobSlugCount = slugs.length;
|
|
4963
5267
|
if (slugs.length === 0) {
|
|
4964
5268
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -5057,17 +5361,17 @@ function formatAgo(ms) {
|
|
|
5057
5361
|
}
|
|
5058
5362
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
5059
5363
|
try {
|
|
5060
|
-
const raw =
|
|
5364
|
+
const raw = fs21.readFileSync(path21.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
5061
5365
|
return splitFrontmatter(raw).frontmatter;
|
|
5062
5366
|
} catch {
|
|
5063
5367
|
return {};
|
|
5064
5368
|
}
|
|
5065
5369
|
}
|
|
5066
5370
|
function listJobSlugs(absDir) {
|
|
5067
|
-
if (!
|
|
5371
|
+
if (!fs21.existsSync(absDir)) return [];
|
|
5068
5372
|
let entries;
|
|
5069
5373
|
try {
|
|
5070
|
-
entries =
|
|
5374
|
+
entries = fs21.readdirSync(absDir, { withFileTypes: true });
|
|
5071
5375
|
} catch {
|
|
5072
5376
|
return [];
|
|
5073
5377
|
}
|
|
@@ -5712,7 +6016,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd, baseBranch)
|
|
|
5712
6016
|
|
|
5713
6017
|
// src/gha.ts
|
|
5714
6018
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
5715
|
-
import * as
|
|
6019
|
+
import * as fs22 from "fs";
|
|
5716
6020
|
function getRunUrl() {
|
|
5717
6021
|
const server = process.env.GITHUB_SERVER_URL;
|
|
5718
6022
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -5723,10 +6027,10 @@ function getRunUrl() {
|
|
|
5723
6027
|
function reactToTriggerComment(cwd) {
|
|
5724
6028
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5725
6029
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5726
|
-
if (!eventPath || !
|
|
6030
|
+
if (!eventPath || !fs22.existsSync(eventPath)) return;
|
|
5727
6031
|
let event = null;
|
|
5728
6032
|
try {
|
|
5729
|
-
event = JSON.parse(
|
|
6033
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
5730
6034
|
} catch {
|
|
5731
6035
|
return;
|
|
5732
6036
|
}
|
|
@@ -6011,22 +6315,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
6011
6315
|
|
|
6012
6316
|
// src/scripts/initFlow.ts
|
|
6013
6317
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
6014
|
-
import * as
|
|
6015
|
-
import * as
|
|
6318
|
+
import * as fs24 from "fs";
|
|
6319
|
+
import * as path23 from "path";
|
|
6016
6320
|
|
|
6017
6321
|
// src/scripts/loadQaGuide.ts
|
|
6018
|
-
import * as
|
|
6019
|
-
import * as
|
|
6322
|
+
import * as fs23 from "fs";
|
|
6323
|
+
import * as path22 from "path";
|
|
6020
6324
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
6021
6325
|
var loadQaGuide = async (ctx) => {
|
|
6022
|
-
const full =
|
|
6023
|
-
if (!
|
|
6326
|
+
const full = path22.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
6327
|
+
if (!fs23.existsSync(full)) {
|
|
6024
6328
|
ctx.data.qaGuide = "";
|
|
6025
6329
|
ctx.data.qaGuidePath = "";
|
|
6026
6330
|
return;
|
|
6027
6331
|
}
|
|
6028
6332
|
try {
|
|
6029
|
-
ctx.data.qaGuide =
|
|
6333
|
+
ctx.data.qaGuide = fs23.readFileSync(full, "utf-8");
|
|
6030
6334
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
6031
6335
|
} catch {
|
|
6032
6336
|
ctx.data.qaGuide = "";
|
|
@@ -6036,9 +6340,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
6036
6340
|
|
|
6037
6341
|
// src/scripts/initFlow.ts
|
|
6038
6342
|
function detectPackageManager(cwd) {
|
|
6039
|
-
if (
|
|
6040
|
-
if (
|
|
6041
|
-
if (
|
|
6343
|
+
if (fs24.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
6344
|
+
if (fs24.existsSync(path23.join(cwd, "yarn.lock"))) return "yarn";
|
|
6345
|
+
if (fs24.existsSync(path23.join(cwd, "bun.lockb"))) return "bun";
|
|
6042
6346
|
return "npm";
|
|
6043
6347
|
}
|
|
6044
6348
|
function qualityCommandsFor(pm) {
|
|
@@ -6160,48 +6464,48 @@ function performInit(cwd, force) {
|
|
|
6160
6464
|
const pm = detectPackageManager(cwd);
|
|
6161
6465
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
6162
6466
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
6163
|
-
const configPath =
|
|
6164
|
-
if (
|
|
6467
|
+
const configPath = path23.join(cwd, "kody.config.json");
|
|
6468
|
+
if (fs24.existsSync(configPath) && !force) {
|
|
6165
6469
|
skipped.push("kody.config.json");
|
|
6166
6470
|
} else {
|
|
6167
6471
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
6168
|
-
|
|
6472
|
+
fs24.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
6169
6473
|
`);
|
|
6170
6474
|
wrote.push("kody.config.json");
|
|
6171
6475
|
}
|
|
6172
|
-
const workflowDir =
|
|
6173
|
-
const workflowPath =
|
|
6174
|
-
if (
|
|
6476
|
+
const workflowDir = path23.join(cwd, ".github", "workflows");
|
|
6477
|
+
const workflowPath = path23.join(workflowDir, "kody.yml");
|
|
6478
|
+
if (fs24.existsSync(workflowPath) && !force) {
|
|
6175
6479
|
skipped.push(".github/workflows/kody.yml");
|
|
6176
6480
|
} else {
|
|
6177
|
-
|
|
6178
|
-
|
|
6481
|
+
fs24.mkdirSync(workflowDir, { recursive: true });
|
|
6482
|
+
fs24.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
6179
6483
|
wrote.push(".github/workflows/kody.yml");
|
|
6180
6484
|
}
|
|
6181
|
-
const hasUi =
|
|
6485
|
+
const hasUi = fs24.existsSync(path23.join(cwd, "src/app")) || fs24.existsSync(path23.join(cwd, "app")) || fs24.existsSync(path23.join(cwd, "pages"));
|
|
6182
6486
|
if (hasUi) {
|
|
6183
|
-
const qaGuidePath =
|
|
6184
|
-
if (
|
|
6487
|
+
const qaGuidePath = path23.join(cwd, QA_GUIDE_REL_PATH);
|
|
6488
|
+
if (fs24.existsSync(qaGuidePath) && !force) {
|
|
6185
6489
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
6186
6490
|
} else {
|
|
6187
|
-
|
|
6491
|
+
fs24.mkdirSync(path23.dirname(qaGuidePath), { recursive: true });
|
|
6188
6492
|
const discovery = runQaDiscovery(cwd);
|
|
6189
|
-
|
|
6493
|
+
fs24.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
6190
6494
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
6191
6495
|
}
|
|
6192
6496
|
}
|
|
6193
6497
|
const builtinJobs = listBuiltinJobs();
|
|
6194
6498
|
if (builtinJobs.length > 0) {
|
|
6195
|
-
const jobsDir =
|
|
6196
|
-
|
|
6499
|
+
const jobsDir = path23.join(cwd, ".kody", "jobs");
|
|
6500
|
+
fs24.mkdirSync(jobsDir, { recursive: true });
|
|
6197
6501
|
for (const job of builtinJobs) {
|
|
6198
|
-
const rel =
|
|
6199
|
-
const target =
|
|
6200
|
-
if (
|
|
6502
|
+
const rel = path23.join(".kody", "jobs", `${job.slug}.md`);
|
|
6503
|
+
const target = path23.join(cwd, rel);
|
|
6504
|
+
if (fs24.existsSync(target) && !force) {
|
|
6201
6505
|
skipped.push(rel);
|
|
6202
6506
|
continue;
|
|
6203
6507
|
}
|
|
6204
|
-
|
|
6508
|
+
fs24.writeFileSync(target, fs24.readFileSync(job.filePath, "utf-8"));
|
|
6205
6509
|
wrote.push(rel);
|
|
6206
6510
|
}
|
|
6207
6511
|
}
|
|
@@ -6213,12 +6517,12 @@ function performInit(cwd, force) {
|
|
|
6213
6517
|
continue;
|
|
6214
6518
|
}
|
|
6215
6519
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
6216
|
-
const target =
|
|
6217
|
-
if (
|
|
6520
|
+
const target = path23.join(workflowDir, `kody-${exe.name}.yml`);
|
|
6521
|
+
if (fs24.existsSync(target) && !force) {
|
|
6218
6522
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
6219
6523
|
continue;
|
|
6220
6524
|
}
|
|
6221
|
-
|
|
6525
|
+
fs24.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
6222
6526
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
6223
6527
|
}
|
|
6224
6528
|
let labels;
|
|
@@ -6307,14 +6611,14 @@ var loadCoverageRules = async (ctx) => {
|
|
|
6307
6611
|
};
|
|
6308
6612
|
|
|
6309
6613
|
// src/goal/state.ts
|
|
6310
|
-
import * as
|
|
6311
|
-
import * as
|
|
6614
|
+
import * as fs25 from "fs";
|
|
6615
|
+
import * as path24 from "path";
|
|
6312
6616
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
6313
6617
|
var GoalStateError = class extends Error {
|
|
6314
|
-
constructor(
|
|
6315
|
-
super(`Invalid goal state at ${
|
|
6618
|
+
constructor(path32, message) {
|
|
6619
|
+
super(`Invalid goal state at ${path32}:
|
|
6316
6620
|
${message}`);
|
|
6317
|
-
this.path =
|
|
6621
|
+
this.path = path32;
|
|
6318
6622
|
this.name = "GoalStateError";
|
|
6319
6623
|
}
|
|
6320
6624
|
path;
|
|
@@ -6358,16 +6662,16 @@ function serializeGoalState(s) {
|
|
|
6358
6662
|
`;
|
|
6359
6663
|
}
|
|
6360
6664
|
function goalStatePath(cwd, goalId) {
|
|
6361
|
-
return
|
|
6665
|
+
return path24.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
6362
6666
|
}
|
|
6363
6667
|
function readGoalState(cwd, goalId) {
|
|
6364
6668
|
const file = goalStatePath(cwd, goalId);
|
|
6365
|
-
if (!
|
|
6669
|
+
if (!fs25.existsSync(file)) {
|
|
6366
6670
|
throw new GoalStateError(file, "file not found");
|
|
6367
6671
|
}
|
|
6368
6672
|
let raw;
|
|
6369
6673
|
try {
|
|
6370
|
-
raw = JSON.parse(
|
|
6674
|
+
raw = JSON.parse(fs25.readFileSync(file, "utf-8"));
|
|
6371
6675
|
} catch (err) {
|
|
6372
6676
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
6373
6677
|
}
|
|
@@ -6375,8 +6679,8 @@ function readGoalState(cwd, goalId) {
|
|
|
6375
6679
|
}
|
|
6376
6680
|
function writeGoalState(cwd, goalId, state) {
|
|
6377
6681
|
const file = goalStatePath(cwd, goalId);
|
|
6378
|
-
|
|
6379
|
-
|
|
6682
|
+
fs25.mkdirSync(path24.dirname(file), { recursive: true });
|
|
6683
|
+
fs25.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
6380
6684
|
}
|
|
6381
6685
|
function nowIso() {
|
|
6382
6686
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -6470,8 +6774,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
6470
6774
|
};
|
|
6471
6775
|
|
|
6472
6776
|
// src/scripts/loadJobFromFile.ts
|
|
6473
|
-
import * as
|
|
6474
|
-
import * as
|
|
6777
|
+
import * as fs26 from "fs";
|
|
6778
|
+
import * as path25 from "path";
|
|
6475
6779
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
6476
6780
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
6477
6781
|
const slugArg = String(args?.slugArg ?? "job");
|
|
@@ -6479,11 +6783,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
6479
6783
|
if (!slug) {
|
|
6480
6784
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
6481
6785
|
}
|
|
6482
|
-
const absPath =
|
|
6483
|
-
if (!
|
|
6786
|
+
const absPath = path25.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
6787
|
+
if (!fs26.existsSync(absPath)) {
|
|
6484
6788
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
6485
6789
|
}
|
|
6486
|
-
const raw =
|
|
6790
|
+
const raw = fs26.readFileSync(absPath, "utf-8");
|
|
6487
6791
|
const { title, body } = parseJobFile(raw, slug);
|
|
6488
6792
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
6489
6793
|
const loaded = await backend.load(slug);
|
|
@@ -6515,16 +6819,16 @@ function humanizeSlug(slug) {
|
|
|
6515
6819
|
}
|
|
6516
6820
|
|
|
6517
6821
|
// src/scripts/loadMemoryContext.ts
|
|
6518
|
-
import * as
|
|
6519
|
-
import * as
|
|
6822
|
+
import * as fs27 from "fs";
|
|
6823
|
+
import * as path26 from "path";
|
|
6520
6824
|
var MEMORY_DIR_RELATIVE = ".kody/memory";
|
|
6521
6825
|
var MAX_PAGES = 8;
|
|
6522
6826
|
var PER_PAGE_MAX_BYTES = 4e3;
|
|
6523
6827
|
var TOTAL_MAX_BYTES = 24e3;
|
|
6524
6828
|
var TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
6525
6829
|
var loadMemoryContext = async (ctx) => {
|
|
6526
|
-
const memoryAbs =
|
|
6527
|
-
if (!
|
|
6830
|
+
const memoryAbs = path26.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
6831
|
+
if (!fs27.existsSync(memoryAbs)) {
|
|
6528
6832
|
ctx.data.memoryContext = "";
|
|
6529
6833
|
return;
|
|
6530
6834
|
}
|
|
@@ -6549,21 +6853,21 @@ function collectPages(memoryAbs) {
|
|
|
6549
6853
|
walkMd(memoryAbs, (file) => {
|
|
6550
6854
|
let stat;
|
|
6551
6855
|
try {
|
|
6552
|
-
stat =
|
|
6856
|
+
stat = fs27.statSync(file);
|
|
6553
6857
|
} catch {
|
|
6554
6858
|
return;
|
|
6555
6859
|
}
|
|
6556
6860
|
let raw;
|
|
6557
6861
|
try {
|
|
6558
|
-
raw =
|
|
6862
|
+
raw = fs27.readFileSync(file, "utf-8");
|
|
6559
6863
|
} catch {
|
|
6560
6864
|
return;
|
|
6561
6865
|
}
|
|
6562
6866
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
6563
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
6867
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path26.basename(file, ".md");
|
|
6564
6868
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
6565
6869
|
out.push({
|
|
6566
|
-
relPath:
|
|
6870
|
+
relPath: path26.relative(memoryAbs, file),
|
|
6567
6871
|
title,
|
|
6568
6872
|
updated,
|
|
6569
6873
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -6631,16 +6935,16 @@ function walkMd(root, visit) {
|
|
|
6631
6935
|
const dir = stack.pop();
|
|
6632
6936
|
let names;
|
|
6633
6937
|
try {
|
|
6634
|
-
names =
|
|
6938
|
+
names = fs27.readdirSync(dir);
|
|
6635
6939
|
} catch {
|
|
6636
6940
|
continue;
|
|
6637
6941
|
}
|
|
6638
6942
|
for (const name of names) {
|
|
6639
6943
|
if (name.startsWith(".")) continue;
|
|
6640
|
-
const full =
|
|
6944
|
+
const full = path26.join(dir, name);
|
|
6641
6945
|
let stat;
|
|
6642
6946
|
try {
|
|
6643
|
-
stat =
|
|
6947
|
+
stat = fs27.statSync(full);
|
|
6644
6948
|
} catch {
|
|
6645
6949
|
continue;
|
|
6646
6950
|
}
|
|
@@ -6744,9 +7048,12 @@ function formatReviewComments(raw) {
|
|
|
6744
7048
|
}
|
|
6745
7049
|
}
|
|
6746
7050
|
|
|
7051
|
+
// src/scripts/loadTaskContext.ts
|
|
7052
|
+
init_events();
|
|
7053
|
+
|
|
6747
7054
|
// src/taskContext.ts
|
|
6748
|
-
import * as
|
|
6749
|
-
import * as
|
|
7055
|
+
import * as fs28 from "fs";
|
|
7056
|
+
import * as path27 from "path";
|
|
6750
7057
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
6751
7058
|
function buildTaskContext(args) {
|
|
6752
7059
|
return {
|
|
@@ -6762,10 +7069,10 @@ function buildTaskContext(args) {
|
|
|
6762
7069
|
}
|
|
6763
7070
|
function persistTaskContext(cwd, ctx) {
|
|
6764
7071
|
try {
|
|
6765
|
-
const dir =
|
|
6766
|
-
|
|
6767
|
-
const file =
|
|
6768
|
-
|
|
7072
|
+
const dir = path27.join(cwd, ".kody", "runs", ctx.runId);
|
|
7073
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
7074
|
+
const file = path27.join(dir, "task-context.json");
|
|
7075
|
+
fs28.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
6769
7076
|
`);
|
|
6770
7077
|
return file;
|
|
6771
7078
|
} catch (err) {
|
|
@@ -8120,8 +8427,8 @@ function resolveBaseOverride(value) {
|
|
|
8120
8427
|
|
|
8121
8428
|
// src/scripts/runTickScript.ts
|
|
8122
8429
|
import { spawnSync } from "child_process";
|
|
8123
|
-
import * as
|
|
8124
|
-
import * as
|
|
8430
|
+
import * as fs29 from "fs";
|
|
8431
|
+
import * as path28 from "path";
|
|
8125
8432
|
var runTickScript = async (ctx, _profile, args) => {
|
|
8126
8433
|
ctx.skipAgent = true;
|
|
8127
8434
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -8133,13 +8440,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8133
8440
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
8134
8441
|
return;
|
|
8135
8442
|
}
|
|
8136
|
-
const jobPath =
|
|
8137
|
-
if (!
|
|
8443
|
+
const jobPath = path28.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8444
|
+
if (!fs29.existsSync(jobPath)) {
|
|
8138
8445
|
ctx.output.exitCode = 99;
|
|
8139
8446
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
8140
8447
|
return;
|
|
8141
8448
|
}
|
|
8142
|
-
const raw =
|
|
8449
|
+
const raw = fs29.readFileSync(jobPath, "utf-8");
|
|
8143
8450
|
const { frontmatter } = splitFrontmatter(raw);
|
|
8144
8451
|
const tickScript = frontmatter.tickScript;
|
|
8145
8452
|
if (!tickScript) {
|
|
@@ -8147,8 +8454,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8147
8454
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
8148
8455
|
return;
|
|
8149
8456
|
}
|
|
8150
|
-
const scriptPath =
|
|
8151
|
-
if (!
|
|
8457
|
+
const scriptPath = path28.isAbsolute(tickScript) ? tickScript : path28.join(ctx.cwd, tickScript);
|
|
8458
|
+
if (!fs29.existsSync(scriptPath)) {
|
|
8152
8459
|
ctx.output.exitCode = 99;
|
|
8153
8460
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
8154
8461
|
return;
|
|
@@ -8499,110 +8806,8 @@ function tryPostPr6(prNumber, body, cwd) {
|
|
|
8499
8806
|
}
|
|
8500
8807
|
}
|
|
8501
8808
|
|
|
8502
|
-
// src/verify.ts
|
|
8503
|
-
import { spawn as spawn2 } from "child_process";
|
|
8504
|
-
var TAIL_CHARS = 4e3;
|
|
8505
|
-
var COMMAND_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
8506
|
-
var DEFAULT_TEST_RETRIES = 2;
|
|
8507
|
-
function runCommand(command, cwd) {
|
|
8508
|
-
return new Promise((resolve4) => {
|
|
8509
|
-
const start = Date.now();
|
|
8510
|
-
const child = spawn2(command, {
|
|
8511
|
-
cwd,
|
|
8512
|
-
shell: true,
|
|
8513
|
-
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" },
|
|
8514
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
8515
|
-
});
|
|
8516
|
-
const buffers = [];
|
|
8517
|
-
let totalSize = 0;
|
|
8518
|
-
const collect = (chunk) => {
|
|
8519
|
-
buffers.push(chunk);
|
|
8520
|
-
totalSize += chunk.length;
|
|
8521
|
-
while (totalSize > TAIL_CHARS * 4 && buffers.length > 1) {
|
|
8522
|
-
totalSize -= buffers[0].length;
|
|
8523
|
-
buffers.shift();
|
|
8524
|
-
}
|
|
8525
|
-
};
|
|
8526
|
-
child.stdout?.on("data", collect);
|
|
8527
|
-
child.stderr?.on("data", collect);
|
|
8528
|
-
const timer = setTimeout(() => {
|
|
8529
|
-
child.kill("SIGTERM");
|
|
8530
|
-
setTimeout(() => {
|
|
8531
|
-
if (!child.killed) child.kill("SIGKILL");
|
|
8532
|
-
}, 5e3);
|
|
8533
|
-
}, COMMAND_TIMEOUT_MS);
|
|
8534
|
-
child.on("exit", (code) => {
|
|
8535
|
-
clearTimeout(timer);
|
|
8536
|
-
const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
|
|
8537
|
-
resolve4({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
|
|
8538
|
-
});
|
|
8539
|
-
child.on("error", (err) => {
|
|
8540
|
-
clearTimeout(timer);
|
|
8541
|
-
resolve4({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
|
|
8542
|
-
});
|
|
8543
|
-
});
|
|
8544
|
-
}
|
|
8545
|
-
async function verifyAll(config, cwd) {
|
|
8546
|
-
const commands = [];
|
|
8547
|
-
if (config.quality.typecheck) commands.push({ name: "typecheck", cmd: config.quality.typecheck });
|
|
8548
|
-
if (config.quality.testUnit) commands.push({ name: "test", cmd: config.quality.testUnit });
|
|
8549
|
-
if (config.quality.lint) commands.push({ name: "lint", cmd: config.quality.lint });
|
|
8550
|
-
if (config.quality.format) commands.push({ name: "format", cmd: config.quality.format });
|
|
8551
|
-
const failed = [];
|
|
8552
|
-
const details = {};
|
|
8553
|
-
for (const { name, cmd } of commands) {
|
|
8554
|
-
const result = await runCommand(cmd, cwd);
|
|
8555
|
-
details[name] = result;
|
|
8556
|
-
if (result.exitCode !== 0) failed.push(name);
|
|
8557
|
-
}
|
|
8558
|
-
return { ok: failed.length === 0, failed, details };
|
|
8559
|
-
}
|
|
8560
|
-
async function applyTestRetries(initial, testCommand, cwd, runner, testRetries = DEFAULT_TEST_RETRIES) {
|
|
8561
|
-
if (initial.ok) return { ...initial, recovered: [] };
|
|
8562
|
-
const recovered = [];
|
|
8563
|
-
const details = { ...initial.details };
|
|
8564
|
-
let failed = [...initial.failed];
|
|
8565
|
-
if (failed.includes("test") && testCommand && testRetries > 0) {
|
|
8566
|
-
for (let attempt = 1; attempt <= testRetries; attempt++) {
|
|
8567
|
-
const retry = await runner(testCommand, cwd);
|
|
8568
|
-
details[`test (retry ${attempt})`] = retry;
|
|
8569
|
-
if (retry.exitCode === 0) {
|
|
8570
|
-
failed = failed.filter((f) => f !== "test");
|
|
8571
|
-
recovered.push("test");
|
|
8572
|
-
break;
|
|
8573
|
-
}
|
|
8574
|
-
}
|
|
8575
|
-
}
|
|
8576
|
-
return { ok: failed.length === 0, failed, details, recovered };
|
|
8577
|
-
}
|
|
8578
|
-
async function verifyAllWithRetry(config, cwd, opts) {
|
|
8579
|
-
const initial = await verifyAll(config, cwd);
|
|
8580
|
-
return applyTestRetries(initial, config.quality.testUnit, cwd, runCommand, opts?.testRetries);
|
|
8581
|
-
}
|
|
8582
|
-
var ANSI_RE = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
8583
|
-
function stripAnsi(s) {
|
|
8584
|
-
return s.replace(ANSI_RE, "");
|
|
8585
|
-
}
|
|
8586
|
-
function summarizeFailure(result) {
|
|
8587
|
-
const lines = [`verify failed: ${result.failed.join(", ")}`];
|
|
8588
|
-
for (const name of result.failed) {
|
|
8589
|
-
const d = result.details[name];
|
|
8590
|
-
if (!d) continue;
|
|
8591
|
-
lines.push(`
|
|
8592
|
-
--- ${name} (exit ${d.exitCode}, ${(d.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
8593
|
-
lines.push(stripAnsi(d.tail));
|
|
8594
|
-
for (let attempt = 1; ; attempt++) {
|
|
8595
|
-
const retry = result.details[`${name} (retry ${attempt})`];
|
|
8596
|
-
if (!retry) break;
|
|
8597
|
-
lines.push(`
|
|
8598
|
-
--- ${name} (retry ${attempt}: exit ${retry.exitCode}, ${(retry.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
8599
|
-
lines.push(stripAnsi(retry.tail));
|
|
8600
|
-
}
|
|
8601
|
-
}
|
|
8602
|
-
return lines.join("\n");
|
|
8603
|
-
}
|
|
8604
|
-
|
|
8605
8809
|
// src/scripts/verify.ts
|
|
8810
|
+
init_verify();
|
|
8606
8811
|
var verify = async (ctx) => {
|
|
8607
8812
|
try {
|
|
8608
8813
|
const result = await verifyAllWithRetry(ctx.config, ctx.cwd);
|
|
@@ -9085,7 +9290,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
9085
9290
|
};
|
|
9086
9291
|
|
|
9087
9292
|
// src/scripts/writeRunSummary.ts
|
|
9088
|
-
import * as
|
|
9293
|
+
import * as fs30 from "fs";
|
|
9089
9294
|
var writeRunSummary = async (ctx, profile) => {
|
|
9090
9295
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
9091
9296
|
if (!summaryPath) return;
|
|
@@ -9107,7 +9312,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
9107
9312
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
9108
9313
|
lines.push("");
|
|
9109
9314
|
try {
|
|
9110
|
-
|
|
9315
|
+
fs30.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
9111
9316
|
`);
|
|
9112
9317
|
} catch {
|
|
9113
9318
|
}
|
|
@@ -9215,21 +9420,21 @@ function firstRequiredFailure(results, tools) {
|
|
|
9215
9420
|
}
|
|
9216
9421
|
return null;
|
|
9217
9422
|
}
|
|
9218
|
-
function verifyOne(
|
|
9219
|
-
const result = { name:
|
|
9220
|
-
let present = runShell(
|
|
9221
|
-
if (!present &&
|
|
9222
|
-
runShell(
|
|
9223
|
-
present = runShell(
|
|
9423
|
+
function verifyOne(tool2, cwd) {
|
|
9424
|
+
const result = { name: tool2.name, present: false, verified: false };
|
|
9425
|
+
let present = runShell(tool2.install.checkCommand, cwd);
|
|
9426
|
+
if (!present && tool2.install.installCommand) {
|
|
9427
|
+
runShell(tool2.install.installCommand, cwd, 12e4);
|
|
9428
|
+
present = runShell(tool2.install.checkCommand, cwd);
|
|
9224
9429
|
}
|
|
9225
9430
|
result.present = present;
|
|
9226
9431
|
if (!present) {
|
|
9227
|
-
result.error = `tool "${
|
|
9432
|
+
result.error = `tool "${tool2.name}" not on PATH (check: ${tool2.install.checkCommand})`;
|
|
9228
9433
|
return result;
|
|
9229
9434
|
}
|
|
9230
|
-
const verified = runShell(
|
|
9435
|
+
const verified = runShell(tool2.verify, cwd);
|
|
9231
9436
|
result.verified = verified;
|
|
9232
|
-
if (!verified) result.error = `tool "${
|
|
9437
|
+
if (!verified) result.error = `tool "${tool2.name}" failed verify: ${tool2.verify}`;
|
|
9233
9438
|
return result;
|
|
9234
9439
|
}
|
|
9235
9440
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
@@ -9324,9 +9529,9 @@ async function runExecutable(profileName, input) {
|
|
|
9324
9529
|
data: {},
|
|
9325
9530
|
output: { exitCode: 0 }
|
|
9326
9531
|
};
|
|
9327
|
-
const ndjsonDir =
|
|
9532
|
+
const ndjsonDir = path29.join(input.cwd, ".kody");
|
|
9328
9533
|
const invokeAgent = async (prompt) => {
|
|
9329
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
9534
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path29.isAbsolute(p) ? p : path29.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
9330
9535
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
9331
9536
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
9332
9537
|
return runAgent({
|
|
@@ -9346,6 +9551,10 @@ async function runExecutable(profileName, input) {
|
|
|
9346
9551
|
maxTurnTimeoutMs: typeof profile.claudeCode.maxTurnTimeoutSec === "number" ? Math.floor(profile.claudeCode.maxTurnTimeoutSec * 1e3) : void 0,
|
|
9347
9552
|
systemPromptAppend: profile.claudeCode.systemPromptAppend,
|
|
9348
9553
|
cacheable: profile.claudeCode.cacheable,
|
|
9554
|
+
enableVerifyTool: profile.claudeCode.enableVerifyTool,
|
|
9555
|
+
verifyToolMaxAttempts: profile.claudeCode.verifyAttempts ?? null,
|
|
9556
|
+
verifyConfig: profile.claudeCode.enableVerifyTool ? config : void 0,
|
|
9557
|
+
executableName: profileName,
|
|
9349
9558
|
settingSources: profile.claudeCode.settingSources
|
|
9350
9559
|
});
|
|
9351
9560
|
};
|
|
@@ -9490,7 +9699,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
9490
9699
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
9491
9700
|
try {
|
|
9492
9701
|
const profilePath = resolveProfilePath(profileName);
|
|
9493
|
-
if (!
|
|
9702
|
+
if (!fs31.existsSync(profilePath)) return null;
|
|
9494
9703
|
return loadProfile(profilePath).inputs;
|
|
9495
9704
|
} catch {
|
|
9496
9705
|
return null;
|
|
@@ -9499,17 +9708,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
9499
9708
|
function resolveProfilePath(profileName) {
|
|
9500
9709
|
const found = resolveExecutable(profileName);
|
|
9501
9710
|
if (found) return found;
|
|
9502
|
-
const here =
|
|
9711
|
+
const here = path29.dirname(new URL(import.meta.url).pathname);
|
|
9503
9712
|
const candidates = [
|
|
9504
|
-
|
|
9713
|
+
path29.join(here, "executables", profileName, "profile.json"),
|
|
9505
9714
|
// same-dir sibling (dev)
|
|
9506
|
-
|
|
9715
|
+
path29.join(here, "..", "executables", profileName, "profile.json"),
|
|
9507
9716
|
// up one (prod: dist/bin → dist/executables)
|
|
9508
|
-
|
|
9717
|
+
path29.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
9509
9718
|
// fallback
|
|
9510
9719
|
];
|
|
9511
9720
|
for (const c of candidates) {
|
|
9512
|
-
if (
|
|
9721
|
+
if (fs31.existsSync(c)) return c;
|
|
9513
9722
|
}
|
|
9514
9723
|
return candidates[0];
|
|
9515
9724
|
}
|
|
@@ -9606,8 +9815,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
9606
9815
|
var SIGKILL_GRACE_MS = 5e3;
|
|
9607
9816
|
async function runShellEntry(entry, ctx, profile) {
|
|
9608
9817
|
const shellName = entry.shell;
|
|
9609
|
-
const shellPath =
|
|
9610
|
-
if (!
|
|
9818
|
+
const shellPath = path29.join(profile.dir, shellName);
|
|
9819
|
+
if (!fs31.existsSync(shellPath)) {
|
|
9611
9820
|
ctx.skipAgent = true;
|
|
9612
9821
|
ctx.output.exitCode = 99;
|
|
9613
9822
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -9742,7 +9951,12 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
9742
9951
|
const child = children[currentIdx];
|
|
9743
9952
|
process.stderr.write(`[kody container] step ${iteration}: invoking ${child.exec}
|
|
9744
9953
|
`);
|
|
9745
|
-
|
|
9954
|
+
if (profile.resetBetweenChildren !== false) {
|
|
9955
|
+
resetWorkingTree(input.cwd);
|
|
9956
|
+
} else {
|
|
9957
|
+
process.stderr.write(`[kody container] resetBetweenChildren=false; preserving tracked tree
|
|
9958
|
+
`);
|
|
9959
|
+
}
|
|
9746
9960
|
const priorState = readContainerState(ctx, child, reader);
|
|
9747
9961
|
if (priorState.core?.prUrl) knownPrUrl = priorState.core.prUrl;
|
|
9748
9962
|
const priorAction = priorState.executables?.[child.exec]?.lastAction;
|
|
@@ -10052,9 +10266,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
10052
10266
|
return token;
|
|
10053
10267
|
}
|
|
10054
10268
|
function detectPackageManager2(cwd) {
|
|
10055
|
-
if (
|
|
10056
|
-
if (
|
|
10057
|
-
if (
|
|
10269
|
+
if (fs32.existsSync(path30.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
10270
|
+
if (fs32.existsSync(path30.join(cwd, "yarn.lock"))) return "yarn";
|
|
10271
|
+
if (fs32.existsSync(path30.join(cwd, "bun.lockb"))) return "bun";
|
|
10058
10272
|
return "npm";
|
|
10059
10273
|
}
|
|
10060
10274
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -10141,11 +10355,11 @@ function configureGitIdentity(cwd) {
|
|
|
10141
10355
|
}
|
|
10142
10356
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
10143
10357
|
if (!issueNumber) return;
|
|
10144
|
-
const logPath =
|
|
10358
|
+
const logPath = path30.join(cwd, ".kody", "last-run.jsonl");
|
|
10145
10359
|
let tail = "";
|
|
10146
10360
|
try {
|
|
10147
|
-
if (
|
|
10148
|
-
const content =
|
|
10361
|
+
if (fs32.existsSync(logPath)) {
|
|
10362
|
+
const content = fs32.readFileSync(logPath, "utf-8");
|
|
10149
10363
|
tail = content.slice(-3e3);
|
|
10150
10364
|
}
|
|
10151
10365
|
} catch {
|
|
@@ -10170,7 +10384,7 @@ async function runCi(argv) {
|
|
|
10170
10384
|
return 0;
|
|
10171
10385
|
}
|
|
10172
10386
|
const args = parseCiArgs(argv);
|
|
10173
|
-
const cwd = args.cwd ?
|
|
10387
|
+
const cwd = args.cwd ? path30.resolve(args.cwd) : process.cwd();
|
|
10174
10388
|
let earlyConfig;
|
|
10175
10389
|
try {
|
|
10176
10390
|
earlyConfig = loadConfig(cwd);
|
|
@@ -10180,9 +10394,9 @@ async function runCi(argv) {
|
|
|
10180
10394
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
10181
10395
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
10182
10396
|
let manualWorkflowDispatch = false;
|
|
10183
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
10397
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs32.existsSync(dispatchEventPath)) {
|
|
10184
10398
|
try {
|
|
10185
|
-
const evt = JSON.parse(
|
|
10399
|
+
const evt = JSON.parse(fs32.readFileSync(dispatchEventPath, "utf-8"));
|
|
10186
10400
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
10187
10401
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
10188
10402
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -10441,9 +10655,9 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
10441
10655
|
return result;
|
|
10442
10656
|
}
|
|
10443
10657
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
10444
|
-
const sessionFile =
|
|
10445
|
-
const eventsFile =
|
|
10446
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
10658
|
+
const sessionFile = path31.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
10659
|
+
const eventsFile = path31.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
10660
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs33.existsSync(path31.join(cwd, p)));
|
|
10447
10661
|
if (paths.length === 0) return;
|
|
10448
10662
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
10449
10663
|
try {
|
|
@@ -10481,7 +10695,7 @@ async function runChat(argv) {
|
|
|
10481
10695
|
${CHAT_HELP}`);
|
|
10482
10696
|
return 64;
|
|
10483
10697
|
}
|
|
10484
|
-
const cwd = args.cwd ?
|
|
10698
|
+
const cwd = args.cwd ? path31.resolve(args.cwd) : process.cwd();
|
|
10485
10699
|
const sessionId = args.sessionId;
|
|
10486
10700
|
const unpackedSecrets = unpackAllSecrets();
|
|
10487
10701
|
if (unpackedSecrets > 0) {
|
|
@@ -10533,7 +10747,7 @@ ${CHAT_HELP}`);
|
|
|
10533
10747
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
10534
10748
|
const meta = readMeta(sessionFile);
|
|
10535
10749
|
process.stdout.write(
|
|
10536
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
10750
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs33.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
10537
10751
|
`
|
|
10538
10752
|
);
|
|
10539
10753
|
try {
|
|
@@ -10571,6 +10785,7 @@ ${CHAT_HELP}`);
|
|
|
10571
10785
|
}
|
|
10572
10786
|
|
|
10573
10787
|
// src/stats.ts
|
|
10788
|
+
init_events();
|
|
10574
10789
|
function parseStatsArgs(argv) {
|
|
10575
10790
|
const out = { cwd: process.cwd() };
|
|
10576
10791
|
for (let i = 0; i < argv.length; i++) {
|