@kody-ade/kody-engine 0.4.65 → 0.4.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/kody.js
CHANGED
|
@@ -312,10 +312,559 @@ var init_verifyMcp = __esm({
|
|
|
312
312
|
}
|
|
313
313
|
});
|
|
314
314
|
|
|
315
|
+
// src/issue.ts
|
|
316
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
317
|
+
function ghToken() {
|
|
318
|
+
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
319
|
+
}
|
|
320
|
+
function gh(args, options) {
|
|
321
|
+
const token = ghToken();
|
|
322
|
+
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
323
|
+
return execFileSync3("gh", args, {
|
|
324
|
+
encoding: "utf-8",
|
|
325
|
+
timeout: API_TIMEOUT_MS,
|
|
326
|
+
cwd: options?.cwd,
|
|
327
|
+
env,
|
|
328
|
+
input: options?.input,
|
|
329
|
+
stdio: options?.input ? ["pipe", "pipe", "pipe"] : ["inherit", "pipe", "pipe"]
|
|
330
|
+
}).trim();
|
|
331
|
+
}
|
|
332
|
+
function getIssue(issueNumber, cwd) {
|
|
333
|
+
const output = gh(["issue", "view", String(issueNumber), "--json", "number,title,body,comments,labels"], { cwd });
|
|
334
|
+
const parsed = JSON.parse(output);
|
|
335
|
+
if (typeof parsed?.title !== "string") {
|
|
336
|
+
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
number: parsed.number ?? issueNumber,
|
|
340
|
+
title: parsed.title,
|
|
341
|
+
body: parsed.body ?? "",
|
|
342
|
+
comments: (parsed.comments ?? []).map((c) => ({
|
|
343
|
+
body: c.body ?? "",
|
|
344
|
+
author: c.author?.login ?? "unknown",
|
|
345
|
+
createdAt: c.createdAt ?? ""
|
|
346
|
+
})),
|
|
347
|
+
labels: Array.isArray(parsed.labels) ? parsed.labels.map((l) => l.name ?? "").filter((n) => n.length > 0) : []
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function stripKodyMentions(body) {
|
|
351
|
+
return body.replace(/(@)(kody)/gi, "$1\u200B$2");
|
|
352
|
+
}
|
|
353
|
+
function postIssueComment(issueNumber, body, cwd) {
|
|
354
|
+
try {
|
|
355
|
+
gh(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: stripKodyMentions(body), cwd });
|
|
356
|
+
} catch (err) {
|
|
357
|
+
process.stderr.write(
|
|
358
|
+
`[kody] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
359
|
+
`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function truncate2(s, maxBytes) {
|
|
364
|
+
if (s.length <= maxBytes) return s;
|
|
365
|
+
return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
|
|
366
|
+
}
|
|
367
|
+
function parsePrNumber(url) {
|
|
368
|
+
const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
369
|
+
if (!m) return null;
|
|
370
|
+
const n = parseInt(m[1], 10);
|
|
371
|
+
return Number.isFinite(n) ? n : null;
|
|
372
|
+
}
|
|
373
|
+
function getPr(prNumber, cwd) {
|
|
374
|
+
const output = gh(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
|
|
375
|
+
cwd
|
|
376
|
+
});
|
|
377
|
+
const parsed = JSON.parse(output);
|
|
378
|
+
if (typeof parsed?.title !== "string") {
|
|
379
|
+
throw new Error(`PR #${prNumber}: unexpected response shape`);
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
number: parsed.number ?? prNumber,
|
|
383
|
+
title: parsed.title,
|
|
384
|
+
body: parsed.body ?? "",
|
|
385
|
+
headRefName: String(parsed.headRefName ?? ""),
|
|
386
|
+
baseRefName: String(parsed.baseRefName ?? ""),
|
|
387
|
+
state: String(parsed.state ?? "")
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function getPrDiff(prNumber, cwd) {
|
|
391
|
+
try {
|
|
392
|
+
return gh(["pr", "diff", String(prNumber)], { cwd });
|
|
393
|
+
} catch (err) {
|
|
394
|
+
process.stderr.write(
|
|
395
|
+
`[kody] failed to fetch diff for PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
396
|
+
`
|
|
397
|
+
);
|
|
398
|
+
return "";
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function getPrReviews(prNumber, cwd) {
|
|
402
|
+
try {
|
|
403
|
+
const output = gh(["pr", "view", String(prNumber), "--json", "reviews"], { cwd });
|
|
404
|
+
const parsed = JSON.parse(output);
|
|
405
|
+
if (!Array.isArray(parsed?.reviews)) return [];
|
|
406
|
+
return parsed.reviews.map(
|
|
407
|
+
(r) => ({
|
|
408
|
+
body: r.body ?? "",
|
|
409
|
+
state: r.state ?? "",
|
|
410
|
+
author: r.author?.login ?? "unknown",
|
|
411
|
+
submittedAt: r.submittedAt ?? ""
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
} catch {
|
|
415
|
+
return [];
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function getPrComments(prNumber, cwd) {
|
|
419
|
+
try {
|
|
420
|
+
const output = gh(["pr", "view", String(prNumber), "--json", "comments"], { cwd });
|
|
421
|
+
const parsed = JSON.parse(output);
|
|
422
|
+
if (!Array.isArray(parsed?.comments)) return [];
|
|
423
|
+
return parsed.comments.map((c) => ({
|
|
424
|
+
body: c.body ?? "",
|
|
425
|
+
author: c.author?.login ?? "unknown",
|
|
426
|
+
createdAt: c.createdAt ?? ""
|
|
427
|
+
})).filter((c) => c.body.trim().length > 0);
|
|
428
|
+
} catch {
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function isReviewShaped(body) {
|
|
433
|
+
return VERDICT_HEADING.test(body);
|
|
434
|
+
}
|
|
435
|
+
function getPrLatestReviewBody(prNumber, cwd) {
|
|
436
|
+
const reviews = getPrReviews(prNumber, cwd).filter((r) => r.body.trim().length > 0).map((r) => ({ body: r.body, at: r.submittedAt }));
|
|
437
|
+
const comments = getPrComments(prNumber, cwd).filter((c) => isReviewShaped(c.body)).map((c) => ({ body: c.body, at: c.createdAt }));
|
|
438
|
+
const all = [...reviews, ...comments].sort((a, b) => (b.at || "").localeCompare(a.at || ""));
|
|
439
|
+
if (all.length > 0) return all[0].body;
|
|
440
|
+
const pr = getPr(prNumber, cwd);
|
|
441
|
+
return pr.body;
|
|
442
|
+
}
|
|
443
|
+
function postPrReviewComment(prNumber, body, cwd) {
|
|
444
|
+
try {
|
|
445
|
+
gh(["pr", "comment", String(prNumber), "--body-file", "-"], { input: stripKodyMentions(body), cwd });
|
|
446
|
+
} catch (err) {
|
|
447
|
+
process.stderr.write(
|
|
448
|
+
`[kody] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
449
|
+
`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
var API_TIMEOUT_MS, VERDICT_HEADING;
|
|
454
|
+
var init_issue = __esm({
|
|
455
|
+
"src/issue.ts"() {
|
|
456
|
+
"use strict";
|
|
457
|
+
API_TIMEOUT_MS = 3e4;
|
|
458
|
+
VERDICT_HEADING = /(^|\n)\s*#{1,6}\s*Verdict\s*:/i;
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// src/prompt.ts
|
|
463
|
+
import * as fs13 from "fs";
|
|
464
|
+
import * as path12 from "path";
|
|
465
|
+
function loadProjectConventions(projectDir) {
|
|
466
|
+
const out = [];
|
|
467
|
+
for (const rel of CONVENTION_FILES) {
|
|
468
|
+
const abs = path12.join(projectDir, rel);
|
|
469
|
+
if (!fs13.existsSync(abs)) continue;
|
|
470
|
+
let content;
|
|
471
|
+
try {
|
|
472
|
+
content = fs13.readFileSync(abs, "utf-8");
|
|
473
|
+
} catch {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const truncated = content.length > CONVENTIONS_PER_FILE_MAX_BYTES;
|
|
477
|
+
if (truncated) content = `${content.slice(0, CONVENTIONS_PER_FILE_MAX_BYTES)}
|
|
478
|
+
|
|
479
|
+
\u2026 (truncated)`;
|
|
480
|
+
out.push({ path: rel, content, truncated });
|
|
481
|
+
}
|
|
482
|
+
return out;
|
|
483
|
+
}
|
|
484
|
+
function parseAgentResult(finalText) {
|
|
485
|
+
const text = (finalText || "").trim();
|
|
486
|
+
if (!text)
|
|
487
|
+
return {
|
|
488
|
+
done: false,
|
|
489
|
+
commitMessage: "",
|
|
490
|
+
prSummary: "",
|
|
491
|
+
feedbackActions: "",
|
|
492
|
+
planDeviations: "",
|
|
493
|
+
priorArt: "",
|
|
494
|
+
failureReason: "agent produced no final message",
|
|
495
|
+
markerMissing: false
|
|
496
|
+
};
|
|
497
|
+
const MARKDOWN_PREFIX = "[\\s>*_#`~\\-]*";
|
|
498
|
+
const FAILED_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}FAILED${MARKDOWN_PREFIX}\\s*:\\s*(.+?)\\s*$`, "is");
|
|
499
|
+
const DONE_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}DONE\\b`, "i");
|
|
500
|
+
const failedMatch = text.match(FAILED_RE);
|
|
501
|
+
if (failedMatch) {
|
|
502
|
+
return {
|
|
503
|
+
done: false,
|
|
504
|
+
commitMessage: "",
|
|
505
|
+
prSummary: "",
|
|
506
|
+
feedbackActions: "",
|
|
507
|
+
planDeviations: "",
|
|
508
|
+
priorArt: "",
|
|
509
|
+
failureReason: stripMarkdownEmphasis(failedMatch[1]),
|
|
510
|
+
markerMissing: false
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
const hasDoneMarker = DONE_RE.test(text);
|
|
514
|
+
const hasCommitMsg = /^[\s>*_#`~-]*COMMIT_MSG\s*:/im.test(text);
|
|
515
|
+
const hasPrSummary = /^[\s>*_#`~-]*PR_SUMMARY\s*:/im.test(text);
|
|
516
|
+
const markerMissing = !hasDoneMarker && !hasCommitMsg && !hasPrSummary;
|
|
517
|
+
const commitMatch = text.match(/^[\s>*_#`~-]*COMMIT_MSG[\s>*_#`~-]*\s*:\s*(.+)$/im);
|
|
518
|
+
const commitMessage = commitMatch ? stripMarkdownEmphasis(commitMatch[1]) : "";
|
|
519
|
+
const feedbackActions = extractBlock(
|
|
520
|
+
text,
|
|
521
|
+
/(?:^|\n)[ \t]*FEEDBACK_ACTIONS\s*:[ \t]*\n/i,
|
|
522
|
+
/(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY|PRIOR_ART)\s*:/i
|
|
523
|
+
);
|
|
524
|
+
let planDeviations = extractBlock(
|
|
525
|
+
text,
|
|
526
|
+
/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*\n/i,
|
|
527
|
+
/(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS|PRIOR_ART)\s*:/i
|
|
528
|
+
);
|
|
529
|
+
if (!planDeviations) {
|
|
530
|
+
const inline = text.match(/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
531
|
+
if (inline) planDeviations = inline[1].trim();
|
|
532
|
+
}
|
|
533
|
+
let priorArt = "";
|
|
534
|
+
const priorArtInline = text.match(/(?:^|\n)[ \t]*PRIOR_ART\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
535
|
+
if (priorArtInline) priorArt = priorArtInline[1].trim();
|
|
536
|
+
const summaryStart = text.search(/(^|\n)[ \t]*PR_SUMMARY\s*:[ \t]*\n/i);
|
|
537
|
+
let prSummary = "";
|
|
538
|
+
if (summaryStart !== -1) {
|
|
539
|
+
const afterMarker = text.slice(summaryStart).replace(/^[\s\S]*?PR_SUMMARY\s*:[ \t]*\n/i, "");
|
|
540
|
+
prSummary = afterMarker.replace(/\n\s*```\s*$/g, "").replace(/```\s*$/g, "").trim();
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
done: true,
|
|
544
|
+
commitMessage,
|
|
545
|
+
prSummary,
|
|
546
|
+
feedbackActions,
|
|
547
|
+
planDeviations,
|
|
548
|
+
priorArt,
|
|
549
|
+
failureReason: "",
|
|
550
|
+
markerMissing
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function stripMarkdownEmphasis(s) {
|
|
554
|
+
return s.trim().replace(/^[*_`~]+|[*_`~]+$/g, "").trim();
|
|
555
|
+
}
|
|
556
|
+
function extractBlock(text, startMarker, endMarker) {
|
|
557
|
+
const startIdx = text.search(startMarker);
|
|
558
|
+
if (startIdx === -1) return "";
|
|
559
|
+
const afterStart = text.slice(startIdx).replace(startMarker, "");
|
|
560
|
+
const endIdx = afterStart.search(endMarker);
|
|
561
|
+
const body = endIdx === -1 ? afterStart : afterStart.slice(0, endIdx);
|
|
562
|
+
return body.replace(/\n\s*```\s*$/g, "").trim();
|
|
563
|
+
}
|
|
564
|
+
var CONVENTIONS_PER_FILE_MAX_BYTES, CONVENTION_FILES;
|
|
565
|
+
var init_prompt = __esm({
|
|
566
|
+
"src/prompt.ts"() {
|
|
567
|
+
"use strict";
|
|
568
|
+
CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
|
|
569
|
+
CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// src/scripts/loadConventions.ts
|
|
574
|
+
var loadConventions_exports = {};
|
|
575
|
+
__export(loadConventions_exports, {
|
|
576
|
+
loadConventions: () => loadConventions
|
|
577
|
+
});
|
|
578
|
+
var loadConventions;
|
|
579
|
+
var init_loadConventions = __esm({
|
|
580
|
+
"src/scripts/loadConventions.ts"() {
|
|
581
|
+
"use strict";
|
|
582
|
+
init_prompt();
|
|
583
|
+
loadConventions = async (ctx) => {
|
|
584
|
+
if (Array.isArray(ctx.data.conventions)) return;
|
|
585
|
+
const conventions = loadProjectConventions(ctx.cwd);
|
|
586
|
+
ctx.data.conventions = conventions;
|
|
587
|
+
if (conventions.length > 0) {
|
|
588
|
+
process.stderr.write(`[kody] loaded conventions: ${conventions.map((c) => c.path).join(", ")}
|
|
589
|
+
`);
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// src/scripts/loadCoverageRules.ts
|
|
596
|
+
var loadCoverageRules_exports = {};
|
|
597
|
+
__export(loadCoverageRules_exports, {
|
|
598
|
+
loadCoverageRules: () => loadCoverageRules
|
|
599
|
+
});
|
|
600
|
+
var loadCoverageRules;
|
|
601
|
+
var init_loadCoverageRules = __esm({
|
|
602
|
+
"src/scripts/loadCoverageRules.ts"() {
|
|
603
|
+
"use strict";
|
|
604
|
+
loadCoverageRules = async (ctx) => {
|
|
605
|
+
if (Array.isArray(ctx.data.coverageRules)) return;
|
|
606
|
+
ctx.data.coverageRules = ctx.config.testRequirements ?? [];
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// src/scripts/loadMemoryContext.ts
|
|
612
|
+
var loadMemoryContext_exports = {};
|
|
613
|
+
__export(loadMemoryContext_exports, {
|
|
614
|
+
loadMemoryContext: () => loadMemoryContext
|
|
615
|
+
});
|
|
616
|
+
import * as fs27 from "fs";
|
|
617
|
+
import * as path26 from "path";
|
|
618
|
+
function collectPages(memoryAbs) {
|
|
619
|
+
const out = [];
|
|
620
|
+
walkMd(memoryAbs, (file) => {
|
|
621
|
+
let stat;
|
|
622
|
+
try {
|
|
623
|
+
stat = fs27.statSync(file);
|
|
624
|
+
} catch {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
let raw;
|
|
628
|
+
try {
|
|
629
|
+
raw = fs27.readFileSync(file, "utf-8");
|
|
630
|
+
} catch {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
634
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path26.basename(file, ".md");
|
|
635
|
+
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
636
|
+
out.push({
|
|
637
|
+
relPath: path26.relative(memoryAbs, file),
|
|
638
|
+
title,
|
|
639
|
+
updated,
|
|
640
|
+
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
641
|
+
mtime: stat.mtimeMs
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
return out;
|
|
645
|
+
}
|
|
646
|
+
function extractQueryTerms(ctx) {
|
|
647
|
+
const terms = [];
|
|
648
|
+
const issue = ctx.data.issue;
|
|
649
|
+
const pr = ctx.data.pr;
|
|
650
|
+
if (issue?.title) terms.push(...tokenize(issue.title));
|
|
651
|
+
if (pr?.title) terms.push(...tokenize(pr.title));
|
|
652
|
+
return Array.from(new Set(terms)).slice(0, 20);
|
|
653
|
+
}
|
|
654
|
+
function tokenize(s) {
|
|
655
|
+
return s.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((t) => t.length >= 3);
|
|
656
|
+
}
|
|
657
|
+
function scorePages(pages, terms) {
|
|
658
|
+
return pages.map((p) => {
|
|
659
|
+
const haystack = `${p.title} ${p.content}`.toLowerCase();
|
|
660
|
+
let score = 0;
|
|
661
|
+
for (const t of terms) {
|
|
662
|
+
if (haystack.includes(t)) score++;
|
|
663
|
+
}
|
|
664
|
+
return { p, score };
|
|
665
|
+
}).sort((a, b) => {
|
|
666
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
667
|
+
return b.p.mtime - a.p.mtime;
|
|
668
|
+
}).map((x) => x.p);
|
|
669
|
+
}
|
|
670
|
+
function sortByRecency(pages) {
|
|
671
|
+
return [...pages].sort((a, b) => {
|
|
672
|
+
if (a.updated && b.updated && a.updated !== b.updated) {
|
|
673
|
+
return b.updated.localeCompare(a.updated);
|
|
674
|
+
}
|
|
675
|
+
return b.mtime - a.mtime;
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
function formatBlock(pages) {
|
|
679
|
+
if (pages.length === 0) return "";
|
|
680
|
+
const lines = [
|
|
681
|
+
"# Project memory (`.kody/memory/`)",
|
|
682
|
+
"",
|
|
683
|
+
"Pages from prior memorize ticks. Treat as advisory context \u2014 confirm against the codebase before acting.",
|
|
684
|
+
""
|
|
685
|
+
];
|
|
686
|
+
let total = 0;
|
|
687
|
+
for (const p of pages) {
|
|
688
|
+
const block = [`## ${p.title} \u2014 \`${p.relPath}\``, "", p.content].join("\n");
|
|
689
|
+
if (total + block.length > TOTAL_MAX_BYTES) {
|
|
690
|
+
lines.push("_\u2026 (further pages truncated to fit budget)_");
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
lines.push(block);
|
|
694
|
+
lines.push("");
|
|
695
|
+
total += block.length;
|
|
696
|
+
}
|
|
697
|
+
return lines.join("\n");
|
|
698
|
+
}
|
|
699
|
+
function walkMd(root, visit) {
|
|
700
|
+
const stack = [root];
|
|
701
|
+
while (stack.length > 0) {
|
|
702
|
+
const dir = stack.pop();
|
|
703
|
+
let names;
|
|
704
|
+
try {
|
|
705
|
+
names = fs27.readdirSync(dir);
|
|
706
|
+
} catch {
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
for (const name of names) {
|
|
710
|
+
if (name.startsWith(".")) continue;
|
|
711
|
+
const full = path26.join(dir, name);
|
|
712
|
+
let stat;
|
|
713
|
+
try {
|
|
714
|
+
stat = fs27.statSync(full);
|
|
715
|
+
} catch {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (stat.isDirectory()) {
|
|
719
|
+
stack.push(full);
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
if (stat.isFile() && full.endsWith(".md")) visit(full);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
var MEMORY_DIR_RELATIVE, MAX_PAGES, PER_PAGE_MAX_BYTES, TOTAL_MAX_BYTES, TRUNCATED_SUFFIX, loadMemoryContext;
|
|
727
|
+
var init_loadMemoryContext = __esm({
|
|
728
|
+
"src/scripts/loadMemoryContext.ts"() {
|
|
729
|
+
"use strict";
|
|
730
|
+
MEMORY_DIR_RELATIVE = ".kody/memory";
|
|
731
|
+
MAX_PAGES = 8;
|
|
732
|
+
PER_PAGE_MAX_BYTES = 4e3;
|
|
733
|
+
TOTAL_MAX_BYTES = 24e3;
|
|
734
|
+
TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
735
|
+
loadMemoryContext = async (ctx) => {
|
|
736
|
+
if (typeof ctx.data.memoryContext === "string") return;
|
|
737
|
+
const memoryAbs = path26.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
738
|
+
if (!fs27.existsSync(memoryAbs)) {
|
|
739
|
+
ctx.data.memoryContext = "";
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
let pages = [];
|
|
743
|
+
try {
|
|
744
|
+
pages = collectPages(memoryAbs);
|
|
745
|
+
} catch {
|
|
746
|
+
ctx.data.memoryContext = "";
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
if (pages.length === 0) {
|
|
750
|
+
ctx.data.memoryContext = "";
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const queryTerms = extractQueryTerms(ctx);
|
|
754
|
+
const ranked = queryTerms.length > 0 ? scorePages(pages, queryTerms) : sortByRecency(pages);
|
|
755
|
+
const top = ranked.slice(0, MAX_PAGES);
|
|
756
|
+
ctx.data.memoryContext = formatBlock(top);
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
// src/scripts/loadPriorArt.ts
|
|
762
|
+
var loadPriorArt_exports = {};
|
|
763
|
+
__export(loadPriorArt_exports, {
|
|
764
|
+
loadPriorArt: () => loadPriorArt
|
|
765
|
+
});
|
|
766
|
+
function parsePrNumbers(raw) {
|
|
767
|
+
if (!raw) return [];
|
|
768
|
+
const trimmed = raw.trim();
|
|
769
|
+
if (!trimmed) return [];
|
|
770
|
+
try {
|
|
771
|
+
const parsed = JSON.parse(trimmed);
|
|
772
|
+
if (!Array.isArray(parsed)) return [];
|
|
773
|
+
return parsed.filter((n) => typeof n === "number" && Number.isInteger(n) && n > 0);
|
|
774
|
+
} catch {
|
|
775
|
+
return [];
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function fetchPrBlock(prNumber, cwd) {
|
|
779
|
+
try {
|
|
780
|
+
const metaRaw = gh(["pr", "view", String(prNumber), "--json", "title,state,url,mergedAt,closedAt"], { cwd });
|
|
781
|
+
const meta = JSON.parse(metaRaw);
|
|
782
|
+
const diff = truncate3(safeGh(["pr", "diff", String(prNumber)], cwd), PER_PR_DIFF_MAX_BYTES);
|
|
783
|
+
const commentsRaw = safeGh(["pr", "view", String(prNumber), "--json", "comments,reviews"], cwd);
|
|
784
|
+
const commentsBlock = formatReviewComments(commentsRaw);
|
|
785
|
+
const lines = [
|
|
786
|
+
`## Prior art: PR #${prNumber} \u2014 ${meta.title ?? "(no title)"} [${meta.state ?? "unknown"}]`,
|
|
787
|
+
meta.url ? meta.url : "",
|
|
788
|
+
"",
|
|
789
|
+
"### Diff",
|
|
790
|
+
"```diff",
|
|
791
|
+
diff || "(empty)",
|
|
792
|
+
"```"
|
|
793
|
+
];
|
|
794
|
+
if (commentsBlock) {
|
|
795
|
+
lines.push("");
|
|
796
|
+
lines.push("### Review comments");
|
|
797
|
+
lines.push(commentsBlock);
|
|
798
|
+
}
|
|
799
|
+
return lines.filter((l) => l !== "").join("\n");
|
|
800
|
+
} catch (err) {
|
|
801
|
+
return `## Prior art: PR #${prNumber}
|
|
802
|
+
_Could not fetch \u2014 ${err instanceof Error ? err.message : String(err)}_`;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
function safeGh(args, cwd) {
|
|
806
|
+
try {
|
|
807
|
+
return gh(args, { cwd });
|
|
808
|
+
} catch {
|
|
809
|
+
return "";
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function truncate3(s, max) {
|
|
813
|
+
return s.length <= max ? s : s.slice(0, max) + TRUNCATED_SUFFIX2;
|
|
814
|
+
}
|
|
815
|
+
function formatReviewComments(raw) {
|
|
816
|
+
if (!raw) return "";
|
|
817
|
+
try {
|
|
818
|
+
const parsed = JSON.parse(raw);
|
|
819
|
+
const out = [];
|
|
820
|
+
for (const c of parsed.comments ?? []) {
|
|
821
|
+
if (!c.body) continue;
|
|
822
|
+
out.push(`- **${c.author?.login ?? "unknown"}**: ${c.body.replace(/\n/g, " ").slice(0, 500)}`);
|
|
823
|
+
}
|
|
824
|
+
for (const r of parsed.reviews ?? []) {
|
|
825
|
+
if (!r.body && !r.state) continue;
|
|
826
|
+
const state = r.state ? ` (${r.state})` : "";
|
|
827
|
+
const body = r.body ? `: ${r.body.replace(/\n/g, " ").slice(0, 500)}` : "";
|
|
828
|
+
out.push(`- **${r.author?.login ?? "unknown"}**${state}${body}`);
|
|
829
|
+
}
|
|
830
|
+
return out.join("\n");
|
|
831
|
+
} catch {
|
|
832
|
+
return "";
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
var PER_PR_DIFF_MAX_BYTES, TOTAL_MAX_BYTES2, TRUNCATED_SUFFIX2, loadPriorArt;
|
|
836
|
+
var init_loadPriorArt = __esm({
|
|
837
|
+
"src/scripts/loadPriorArt.ts"() {
|
|
838
|
+
"use strict";
|
|
839
|
+
init_issue();
|
|
840
|
+
PER_PR_DIFF_MAX_BYTES = 8e3;
|
|
841
|
+
TOTAL_MAX_BYTES2 = 3e4;
|
|
842
|
+
TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
843
|
+
loadPriorArt = async (ctx, _profile, args) => {
|
|
844
|
+
if (typeof ctx.data.priorArt === "string") return;
|
|
845
|
+
const artifactName = typeof args?.artifactName === "string" ? args.artifactName : "priorArt";
|
|
846
|
+
const state = ctx.data.taskState;
|
|
847
|
+
const artifact = state?.artifacts?.[artifactName];
|
|
848
|
+
const prNumbers = parsePrNumbers(artifact?.content);
|
|
849
|
+
if (prNumbers.length === 0) {
|
|
850
|
+
ctx.data.priorArt = "";
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const blocks = [];
|
|
854
|
+
for (const n of prNumbers) {
|
|
855
|
+
const block = fetchPrBlock(n, ctx.cwd);
|
|
856
|
+
if (block) blocks.push(block);
|
|
857
|
+
}
|
|
858
|
+
const joined = blocks.join("\n\n---\n\n");
|
|
859
|
+
ctx.data.priorArt = joined.length > TOTAL_MAX_BYTES2 ? joined.slice(0, TOTAL_MAX_BYTES2) + TRUNCATED_SUFFIX2 : joined;
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
|
|
315
864
|
// package.json
|
|
316
865
|
var package_default = {
|
|
317
866
|
name: "@kody-ade/kody-engine",
|
|
318
|
-
version: "0.4.
|
|
867
|
+
version: "0.4.66",
|
|
319
868
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
320
869
|
license: "MIT",
|
|
321
870
|
type: "module",
|
|
@@ -1775,146 +2324,8 @@ function coerceBare(spec, value) {
|
|
|
1775
2324
|
return value;
|
|
1776
2325
|
}
|
|
1777
2326
|
|
|
1778
|
-
// src/
|
|
1779
|
-
|
|
1780
|
-
var API_TIMEOUT_MS = 3e4;
|
|
1781
|
-
function ghToken() {
|
|
1782
|
-
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
1783
|
-
}
|
|
1784
|
-
function gh(args, options) {
|
|
1785
|
-
const token = ghToken();
|
|
1786
|
-
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
1787
|
-
return execFileSync3("gh", args, {
|
|
1788
|
-
encoding: "utf-8",
|
|
1789
|
-
timeout: API_TIMEOUT_MS,
|
|
1790
|
-
cwd: options?.cwd,
|
|
1791
|
-
env,
|
|
1792
|
-
input: options?.input,
|
|
1793
|
-
stdio: options?.input ? ["pipe", "pipe", "pipe"] : ["inherit", "pipe", "pipe"]
|
|
1794
|
-
}).trim();
|
|
1795
|
-
}
|
|
1796
|
-
function getIssue(issueNumber, cwd) {
|
|
1797
|
-
const output = gh(["issue", "view", String(issueNumber), "--json", "number,title,body,comments,labels"], { cwd });
|
|
1798
|
-
const parsed = JSON.parse(output);
|
|
1799
|
-
if (typeof parsed?.title !== "string") {
|
|
1800
|
-
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
1801
|
-
}
|
|
1802
|
-
return {
|
|
1803
|
-
number: parsed.number ?? issueNumber,
|
|
1804
|
-
title: parsed.title,
|
|
1805
|
-
body: parsed.body ?? "",
|
|
1806
|
-
comments: (parsed.comments ?? []).map((c) => ({
|
|
1807
|
-
body: c.body ?? "",
|
|
1808
|
-
author: c.author?.login ?? "unknown",
|
|
1809
|
-
createdAt: c.createdAt ?? ""
|
|
1810
|
-
})),
|
|
1811
|
-
labels: Array.isArray(parsed.labels) ? parsed.labels.map((l) => l.name ?? "").filter((n) => n.length > 0) : []
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
function stripKodyMentions(body) {
|
|
1815
|
-
return body.replace(/(@)(kody)/gi, "$1\u200B$2");
|
|
1816
|
-
}
|
|
1817
|
-
function postIssueComment(issueNumber, body, cwd) {
|
|
1818
|
-
try {
|
|
1819
|
-
gh(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: stripKodyMentions(body), cwd });
|
|
1820
|
-
} catch (err) {
|
|
1821
|
-
process.stderr.write(
|
|
1822
|
-
`[kody] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1823
|
-
`
|
|
1824
|
-
);
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
function truncate2(s, maxBytes) {
|
|
1828
|
-
if (s.length <= maxBytes) return s;
|
|
1829
|
-
return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
|
|
1830
|
-
}
|
|
1831
|
-
function parsePrNumber(url) {
|
|
1832
|
-
const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
1833
|
-
if (!m) return null;
|
|
1834
|
-
const n = parseInt(m[1], 10);
|
|
1835
|
-
return Number.isFinite(n) ? n : null;
|
|
1836
|
-
}
|
|
1837
|
-
function getPr(prNumber, cwd) {
|
|
1838
|
-
const output = gh(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
|
|
1839
|
-
cwd
|
|
1840
|
-
});
|
|
1841
|
-
const parsed = JSON.parse(output);
|
|
1842
|
-
if (typeof parsed?.title !== "string") {
|
|
1843
|
-
throw new Error(`PR #${prNumber}: unexpected response shape`);
|
|
1844
|
-
}
|
|
1845
|
-
return {
|
|
1846
|
-
number: parsed.number ?? prNumber,
|
|
1847
|
-
title: parsed.title,
|
|
1848
|
-
body: parsed.body ?? "",
|
|
1849
|
-
headRefName: String(parsed.headRefName ?? ""),
|
|
1850
|
-
baseRefName: String(parsed.baseRefName ?? ""),
|
|
1851
|
-
state: String(parsed.state ?? "")
|
|
1852
|
-
};
|
|
1853
|
-
}
|
|
1854
|
-
function getPrDiff(prNumber, cwd) {
|
|
1855
|
-
try {
|
|
1856
|
-
return gh(["pr", "diff", String(prNumber)], { cwd });
|
|
1857
|
-
} catch (err) {
|
|
1858
|
-
process.stderr.write(
|
|
1859
|
-
`[kody] failed to fetch diff for PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1860
|
-
`
|
|
1861
|
-
);
|
|
1862
|
-
return "";
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
function getPrReviews(prNumber, cwd) {
|
|
1866
|
-
try {
|
|
1867
|
-
const output = gh(["pr", "view", String(prNumber), "--json", "reviews"], { cwd });
|
|
1868
|
-
const parsed = JSON.parse(output);
|
|
1869
|
-
if (!Array.isArray(parsed?.reviews)) return [];
|
|
1870
|
-
return parsed.reviews.map(
|
|
1871
|
-
(r) => ({
|
|
1872
|
-
body: r.body ?? "",
|
|
1873
|
-
state: r.state ?? "",
|
|
1874
|
-
author: r.author?.login ?? "unknown",
|
|
1875
|
-
submittedAt: r.submittedAt ?? ""
|
|
1876
|
-
})
|
|
1877
|
-
);
|
|
1878
|
-
} catch {
|
|
1879
|
-
return [];
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
function getPrComments(prNumber, cwd) {
|
|
1883
|
-
try {
|
|
1884
|
-
const output = gh(["pr", "view", String(prNumber), "--json", "comments"], { cwd });
|
|
1885
|
-
const parsed = JSON.parse(output);
|
|
1886
|
-
if (!Array.isArray(parsed?.comments)) return [];
|
|
1887
|
-
return parsed.comments.map((c) => ({
|
|
1888
|
-
body: c.body ?? "",
|
|
1889
|
-
author: c.author?.login ?? "unknown",
|
|
1890
|
-
createdAt: c.createdAt ?? ""
|
|
1891
|
-
})).filter((c) => c.body.trim().length > 0);
|
|
1892
|
-
} catch {
|
|
1893
|
-
return [];
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
var VERDICT_HEADING = /(^|\n)\s*#{1,6}\s*Verdict\s*:/i;
|
|
1897
|
-
function isReviewShaped(body) {
|
|
1898
|
-
return VERDICT_HEADING.test(body);
|
|
1899
|
-
}
|
|
1900
|
-
function getPrLatestReviewBody(prNumber, cwd) {
|
|
1901
|
-
const reviews = getPrReviews(prNumber, cwd).filter((r) => r.body.trim().length > 0).map((r) => ({ body: r.body, at: r.submittedAt }));
|
|
1902
|
-
const comments = getPrComments(prNumber, cwd).filter((c) => isReviewShaped(c.body)).map((c) => ({ body: c.body, at: c.createdAt }));
|
|
1903
|
-
const all = [...reviews, ...comments].sort((a, b) => (b.at || "").localeCompare(a.at || ""));
|
|
1904
|
-
if (all.length > 0) return all[0].body;
|
|
1905
|
-
const pr = getPr(prNumber, cwd);
|
|
1906
|
-
return pr.body;
|
|
1907
|
-
}
|
|
1908
|
-
function postPrReviewComment(prNumber, body, cwd) {
|
|
1909
|
-
try {
|
|
1910
|
-
gh(["pr", "comment", String(prNumber), "--body-file", "-"], { input: stripKodyMentions(body), cwd });
|
|
1911
|
-
} catch (err) {
|
|
1912
|
-
process.stderr.write(
|
|
1913
|
-
`[kody] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1914
|
-
`
|
|
1915
|
-
);
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
2327
|
+
// src/kody-cli.ts
|
|
2328
|
+
init_issue();
|
|
1918
2329
|
|
|
1919
2330
|
// src/executor.ts
|
|
1920
2331
|
import { execFileSync as execFileSync29, spawn as spawn5 } from "child_process";
|
|
@@ -1922,6 +2333,9 @@ import * as fs31 from "fs";
|
|
|
1922
2333
|
import * as path29 from "path";
|
|
1923
2334
|
init_events();
|
|
1924
2335
|
|
|
2336
|
+
// src/lifecycleLabels.ts
|
|
2337
|
+
init_issue();
|
|
2338
|
+
|
|
1925
2339
|
// src/profile.ts
|
|
1926
2340
|
import * as fs9 from "fs";
|
|
1927
2341
|
import * as path8 from "path";
|
|
@@ -2086,7 +2500,8 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2086
2500
|
"output",
|
|
2087
2501
|
// legacy JSON name for outputArtifacts source
|
|
2088
2502
|
"children",
|
|
2089
|
-
"resetBetweenChildren"
|
|
2503
|
+
"resetBetweenChildren",
|
|
2504
|
+
"preloadContext"
|
|
2090
2505
|
]);
|
|
2091
2506
|
function loadProfile(profilePath) {
|
|
2092
2507
|
if (!fs9.existsSync(profilePath)) {
|
|
@@ -2163,6 +2578,9 @@ function loadProfile(profilePath) {
|
|
|
2163
2578
|
// container child sees a clean tracked tree (see executor.ts).
|
|
2164
2579
|
// Containers opt out by setting `"resetBetweenChildren": false`.
|
|
2165
2580
|
resetBetweenChildren: typeof r.resetBetweenChildren === "boolean" ? r.resetBetweenChildren : true,
|
|
2581
|
+
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
2582
|
+
// flip to true after end-to-end verification.
|
|
2583
|
+
preloadContext: r.preloadContext === true,
|
|
2166
2584
|
dir: path8.dirname(profilePath)
|
|
2167
2585
|
};
|
|
2168
2586
|
if (lifecycle) {
|
|
@@ -3227,129 +3645,25 @@ function checkCoverage(addedFiles, requirements) {
|
|
|
3227
3645
|
const expected = renderSiblingPath(file, req.requireSibling);
|
|
3228
3646
|
if (!addedSet.has(expected)) {
|
|
3229
3647
|
misses.push({ file, expectedTest: expected });
|
|
3230
|
-
}
|
|
3231
|
-
break;
|
|
3232
|
-
}
|
|
3233
|
-
}
|
|
3234
|
-
return misses;
|
|
3235
|
-
}
|
|
3236
|
-
function formatMissesForFeedback(misses) {
|
|
3237
|
-
if (misses.length === 0) return "";
|
|
3238
|
-
const lines = ["The following files were added without a sibling test file:"];
|
|
3239
|
-
for (const m of misses) lines.push(`- \`${m.file}\` \u2192 expected \`${m.expectedTest}\``);
|
|
3240
|
-
lines.push("");
|
|
3241
|
-
lines.push(
|
|
3242
|
-
"Add the missing test files. Each should cover the new file's public API with at least a happy path and one failure path. Then re-emit DONE / COMMIT_MSG / PR_SUMMARY."
|
|
3243
|
-
);
|
|
3244
|
-
return lines.join("\n");
|
|
3245
|
-
}
|
|
3246
|
-
|
|
3247
|
-
// src/prompt.ts
|
|
3248
|
-
import * as fs13 from "fs";
|
|
3249
|
-
import * as path12 from "path";
|
|
3250
|
-
var CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
|
|
3251
|
-
var CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
3252
|
-
function loadProjectConventions(projectDir) {
|
|
3253
|
-
const out = [];
|
|
3254
|
-
for (const rel of CONVENTION_FILES) {
|
|
3255
|
-
const abs = path12.join(projectDir, rel);
|
|
3256
|
-
if (!fs13.existsSync(abs)) continue;
|
|
3257
|
-
let content;
|
|
3258
|
-
try {
|
|
3259
|
-
content = fs13.readFileSync(abs, "utf-8");
|
|
3260
|
-
} catch {
|
|
3261
|
-
continue;
|
|
3262
|
-
}
|
|
3263
|
-
const truncated = content.length > CONVENTIONS_PER_FILE_MAX_BYTES;
|
|
3264
|
-
if (truncated) content = `${content.slice(0, CONVENTIONS_PER_FILE_MAX_BYTES)}
|
|
3265
|
-
|
|
3266
|
-
\u2026 (truncated)`;
|
|
3267
|
-
out.push({ path: rel, content, truncated });
|
|
3268
|
-
}
|
|
3269
|
-
return out;
|
|
3270
|
-
}
|
|
3271
|
-
function parseAgentResult(finalText) {
|
|
3272
|
-
const text = (finalText || "").trim();
|
|
3273
|
-
if (!text)
|
|
3274
|
-
return {
|
|
3275
|
-
done: false,
|
|
3276
|
-
commitMessage: "",
|
|
3277
|
-
prSummary: "",
|
|
3278
|
-
feedbackActions: "",
|
|
3279
|
-
planDeviations: "",
|
|
3280
|
-
priorArt: "",
|
|
3281
|
-
failureReason: "agent produced no final message",
|
|
3282
|
-
markerMissing: false
|
|
3283
|
-
};
|
|
3284
|
-
const MARKDOWN_PREFIX = "[\\s>*_#`~\\-]*";
|
|
3285
|
-
const FAILED_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}FAILED${MARKDOWN_PREFIX}\\s*:\\s*(.+?)\\s*$`, "is");
|
|
3286
|
-
const DONE_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}DONE\\b`, "i");
|
|
3287
|
-
const failedMatch = text.match(FAILED_RE);
|
|
3288
|
-
if (failedMatch) {
|
|
3289
|
-
return {
|
|
3290
|
-
done: false,
|
|
3291
|
-
commitMessage: "",
|
|
3292
|
-
prSummary: "",
|
|
3293
|
-
feedbackActions: "",
|
|
3294
|
-
planDeviations: "",
|
|
3295
|
-
priorArt: "",
|
|
3296
|
-
failureReason: stripMarkdownEmphasis(failedMatch[1]),
|
|
3297
|
-
markerMissing: false
|
|
3298
|
-
};
|
|
3299
|
-
}
|
|
3300
|
-
const hasDoneMarker = DONE_RE.test(text);
|
|
3301
|
-
const hasCommitMsg = /^[\s>*_#`~-]*COMMIT_MSG\s*:/im.test(text);
|
|
3302
|
-
const hasPrSummary = /^[\s>*_#`~-]*PR_SUMMARY\s*:/im.test(text);
|
|
3303
|
-
const markerMissing = !hasDoneMarker && !hasCommitMsg && !hasPrSummary;
|
|
3304
|
-
const commitMatch = text.match(/^[\s>*_#`~-]*COMMIT_MSG[\s>*_#`~-]*\s*:\s*(.+)$/im);
|
|
3305
|
-
const commitMessage = commitMatch ? stripMarkdownEmphasis(commitMatch[1]) : "";
|
|
3306
|
-
const feedbackActions = extractBlock(
|
|
3307
|
-
text,
|
|
3308
|
-
/(?:^|\n)[ \t]*FEEDBACK_ACTIONS\s*:[ \t]*\n/i,
|
|
3309
|
-
/(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY|PRIOR_ART)\s*:/i
|
|
3310
|
-
);
|
|
3311
|
-
let planDeviations = extractBlock(
|
|
3312
|
-
text,
|
|
3313
|
-
/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*\n/i,
|
|
3314
|
-
/(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS|PRIOR_ART)\s*:/i
|
|
3315
|
-
);
|
|
3316
|
-
if (!planDeviations) {
|
|
3317
|
-
const inline = text.match(/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
3318
|
-
if (inline) planDeviations = inline[1].trim();
|
|
3319
|
-
}
|
|
3320
|
-
let priorArt = "";
|
|
3321
|
-
const priorArtInline = text.match(/(?:^|\n)[ \t]*PRIOR_ART\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
3322
|
-
if (priorArtInline) priorArt = priorArtInline[1].trim();
|
|
3323
|
-
const summaryStart = text.search(/(^|\n)[ \t]*PR_SUMMARY\s*:[ \t]*\n/i);
|
|
3324
|
-
let prSummary = "";
|
|
3325
|
-
if (summaryStart !== -1) {
|
|
3326
|
-
const afterMarker = text.slice(summaryStart).replace(/^[\s\S]*?PR_SUMMARY\s*:[ \t]*\n/i, "");
|
|
3327
|
-
prSummary = afterMarker.replace(/\n\s*```\s*$/g, "").replace(/```\s*$/g, "").trim();
|
|
3648
|
+
}
|
|
3649
|
+
break;
|
|
3650
|
+
}
|
|
3328
3651
|
}
|
|
3329
|
-
return
|
|
3330
|
-
done: true,
|
|
3331
|
-
commitMessage,
|
|
3332
|
-
prSummary,
|
|
3333
|
-
feedbackActions,
|
|
3334
|
-
planDeviations,
|
|
3335
|
-
priorArt,
|
|
3336
|
-
failureReason: "",
|
|
3337
|
-
markerMissing
|
|
3338
|
-
};
|
|
3339
|
-
}
|
|
3340
|
-
function stripMarkdownEmphasis(s) {
|
|
3341
|
-
return s.trim().replace(/^[*_`~]+|[*_`~]+$/g, "").trim();
|
|
3652
|
+
return misses;
|
|
3342
3653
|
}
|
|
3343
|
-
function
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
const
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3654
|
+
function formatMissesForFeedback(misses) {
|
|
3655
|
+
if (misses.length === 0) return "";
|
|
3656
|
+
const lines = ["The following files were added without a sibling test file:"];
|
|
3657
|
+
for (const m of misses) lines.push(`- \`${m.file}\` \u2192 expected \`${m.expectedTest}\``);
|
|
3658
|
+
lines.push("");
|
|
3659
|
+
lines.push(
|
|
3660
|
+
"Add the missing test files. Each should cover the new file's public API with at least a happy path and one failure path. Then re-emit DONE / COMMIT_MSG / PR_SUMMARY."
|
|
3661
|
+
);
|
|
3662
|
+
return lines.join("\n");
|
|
3350
3663
|
}
|
|
3351
3664
|
|
|
3352
3665
|
// src/scripts/checkCoverageWithRetry.ts
|
|
3666
|
+
init_prompt();
|
|
3353
3667
|
var checkCoverageWithRetry = async (ctx) => {
|
|
3354
3668
|
const reqs = ctx.data.coverageRules ?? [];
|
|
3355
3669
|
if (reqs.length === 0) {
|
|
@@ -3658,11 +3972,13 @@ function formatToolsUsage(profile) {
|
|
|
3658
3972
|
}
|
|
3659
3973
|
|
|
3660
3974
|
// src/scripts/createQaGoal.ts
|
|
3975
|
+
init_issue();
|
|
3661
3976
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
3662
3977
|
import * as fs16 from "fs";
|
|
3663
3978
|
import * as path16 from "path";
|
|
3664
3979
|
|
|
3665
3980
|
// src/scripts/postReviewResult.ts
|
|
3981
|
+
init_issue();
|
|
3666
3982
|
function detectVerdict(body) {
|
|
3667
3983
|
const m = body.match(/##\s*Verdict\s*:\s*(PASS|CONCERNS|FAIL)\b/i);
|
|
3668
3984
|
if (!m) return "UNKNOWN";
|
|
@@ -4201,6 +4517,9 @@ QA_GOAL_TARGETED=(no manifest issue) (id: ${goalId}, verdict: ${verdict})
|
|
|
4201
4517
|
ctx.output.exitCode = verdict === "FAIL" ? 1 : 0;
|
|
4202
4518
|
};
|
|
4203
4519
|
|
|
4520
|
+
// src/goal/operations.ts
|
|
4521
|
+
init_issue();
|
|
4522
|
+
|
|
4204
4523
|
// src/goal/labels.ts
|
|
4205
4524
|
function goalLabel(goalId) {
|
|
4206
4525
|
return `goal:${goalId}`;
|
|
@@ -5105,7 +5424,11 @@ function stripQuotes(value) {
|
|
|
5105
5424
|
return value;
|
|
5106
5425
|
}
|
|
5107
5426
|
|
|
5427
|
+
// src/scripts/jobState/contentsApiBackend.ts
|
|
5428
|
+
init_issue();
|
|
5429
|
+
|
|
5108
5430
|
// src/scripts/issueStateComment.ts
|
|
5431
|
+
init_issue();
|
|
5109
5432
|
function isStateEnvelope(x) {
|
|
5110
5433
|
if (x === null || typeof x !== "object") return false;
|
|
5111
5434
|
const o = x;
|
|
@@ -5569,6 +5892,7 @@ function listJobSlugs(absDir) {
|
|
|
5569
5892
|
}
|
|
5570
5893
|
|
|
5571
5894
|
// src/scripts/dispatchJobTicks.ts
|
|
5895
|
+
init_issue();
|
|
5572
5896
|
var dispatchJobTicks = async (ctx, _profile, args) => {
|
|
5573
5897
|
ctx.skipAgent = true;
|
|
5574
5898
|
const label = String(args?.label ?? "");
|
|
@@ -5656,6 +5980,7 @@ var dispatchNextTask = async (ctx) => {
|
|
|
5656
5980
|
};
|
|
5657
5981
|
|
|
5658
5982
|
// src/pr.ts
|
|
5983
|
+
init_issue();
|
|
5659
5984
|
function prMergeStatus(prNumber, cwd) {
|
|
5660
5985
|
try {
|
|
5661
5986
|
const out = gh(
|
|
@@ -6002,6 +6327,7 @@ var finalizeGoal = async (ctx) => {
|
|
|
6002
6327
|
};
|
|
6003
6328
|
|
|
6004
6329
|
// src/scripts/finishFlow.ts
|
|
6330
|
+
init_issue();
|
|
6005
6331
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
6006
6332
|
var TERMINAL_PHASE = {
|
|
6007
6333
|
"review-passed": { phase: "shipped", status: "succeeded" },
|
|
@@ -6290,6 +6616,9 @@ function sleepMs(ms) {
|
|
|
6290
6616
|
}
|
|
6291
6617
|
}
|
|
6292
6618
|
|
|
6619
|
+
// src/scripts/fixCiFlow.ts
|
|
6620
|
+
init_issue();
|
|
6621
|
+
|
|
6293
6622
|
// src/workflow.ts
|
|
6294
6623
|
import { execFileSync as execFileSync17 } from "child_process";
|
|
6295
6624
|
var GH_TIMEOUT_MS = 3e4;
|
|
@@ -6443,6 +6772,7 @@ function tryPostPr(prNumber, body, cwd) {
|
|
|
6443
6772
|
}
|
|
6444
6773
|
|
|
6445
6774
|
// src/scripts/fixFlow.ts
|
|
6775
|
+
init_issue();
|
|
6446
6776
|
var fixFlow = async (ctx) => {
|
|
6447
6777
|
const prNumber = ctx.args.pr;
|
|
6448
6778
|
const pr = getPr(prNumber, ctx.cwd);
|
|
@@ -6809,22 +7139,9 @@ Nothing to do. All files already present. (Use --force to overwrite.)
|
|
|
6809
7139
|
ctx.output.exitCode = 0;
|
|
6810
7140
|
};
|
|
6811
7141
|
|
|
6812
|
-
// src/scripts/
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
const conventions = loadProjectConventions(ctx.cwd);
|
|
6816
|
-
ctx.data.conventions = conventions;
|
|
6817
|
-
if (conventions.length > 0) {
|
|
6818
|
-
process.stderr.write(`[kody] loaded conventions: ${conventions.map((c) => c.path).join(", ")}
|
|
6819
|
-
`);
|
|
6820
|
-
}
|
|
6821
|
-
};
|
|
6822
|
-
|
|
6823
|
-
// src/scripts/loadCoverageRules.ts
|
|
6824
|
-
var loadCoverageRules = async (ctx) => {
|
|
6825
|
-
if (Array.isArray(ctx.data.coverageRules)) return;
|
|
6826
|
-
ctx.data.coverageRules = ctx.config.testRequirements ?? [];
|
|
6827
|
-
};
|
|
7142
|
+
// src/scripts/index.ts
|
|
7143
|
+
init_loadConventions();
|
|
7144
|
+
init_loadCoverageRules();
|
|
6828
7145
|
|
|
6829
7146
|
// src/goal/state.ts
|
|
6830
7147
|
import * as fs25 from "fs";
|
|
@@ -6941,6 +7258,7 @@ var loadGoalState = async (ctx) => {
|
|
|
6941
7258
|
};
|
|
6942
7259
|
|
|
6943
7260
|
// src/scripts/loadIssueContext.ts
|
|
7261
|
+
init_issue();
|
|
6944
7262
|
var DEFAULT_COMMENT_LIMIT = 12;
|
|
6945
7263
|
var DEFAULT_COMMENT_MAX_BYTES = 16e3;
|
|
6946
7264
|
var loadIssueContext = async (ctx) => {
|
|
@@ -6970,6 +7288,7 @@ var loadIssueContext = async (ctx) => {
|
|
|
6970
7288
|
};
|
|
6971
7289
|
|
|
6972
7290
|
// src/scripts/loadIssueStateComment.ts
|
|
7291
|
+
init_issue();
|
|
6973
7292
|
var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
6974
7293
|
const marker = String(args?.marker ?? "");
|
|
6975
7294
|
if (!marker) {
|
|
@@ -7040,237 +7359,9 @@ function humanizeSlug(slug) {
|
|
|
7040
7359
|
return slug.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
|
|
7041
7360
|
}
|
|
7042
7361
|
|
|
7043
|
-
// src/scripts/
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
var MEMORY_DIR_RELATIVE = ".kody/memory";
|
|
7047
|
-
var MAX_PAGES = 8;
|
|
7048
|
-
var PER_PAGE_MAX_BYTES = 4e3;
|
|
7049
|
-
var TOTAL_MAX_BYTES = 24e3;
|
|
7050
|
-
var TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
7051
|
-
var loadMemoryContext = async (ctx) => {
|
|
7052
|
-
if (typeof ctx.data.memoryContext === "string") return;
|
|
7053
|
-
const memoryAbs = path26.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
7054
|
-
if (!fs27.existsSync(memoryAbs)) {
|
|
7055
|
-
ctx.data.memoryContext = "";
|
|
7056
|
-
return;
|
|
7057
|
-
}
|
|
7058
|
-
let pages = [];
|
|
7059
|
-
try {
|
|
7060
|
-
pages = collectPages(memoryAbs);
|
|
7061
|
-
} catch {
|
|
7062
|
-
ctx.data.memoryContext = "";
|
|
7063
|
-
return;
|
|
7064
|
-
}
|
|
7065
|
-
if (pages.length === 0) {
|
|
7066
|
-
ctx.data.memoryContext = "";
|
|
7067
|
-
return;
|
|
7068
|
-
}
|
|
7069
|
-
const queryTerms = extractQueryTerms(ctx);
|
|
7070
|
-
const ranked = queryTerms.length > 0 ? scorePages(pages, queryTerms) : sortByRecency(pages);
|
|
7071
|
-
const top = ranked.slice(0, MAX_PAGES);
|
|
7072
|
-
ctx.data.memoryContext = formatBlock(top);
|
|
7073
|
-
};
|
|
7074
|
-
function collectPages(memoryAbs) {
|
|
7075
|
-
const out = [];
|
|
7076
|
-
walkMd(memoryAbs, (file) => {
|
|
7077
|
-
let stat;
|
|
7078
|
-
try {
|
|
7079
|
-
stat = fs27.statSync(file);
|
|
7080
|
-
} catch {
|
|
7081
|
-
return;
|
|
7082
|
-
}
|
|
7083
|
-
let raw;
|
|
7084
|
-
try {
|
|
7085
|
-
raw = fs27.readFileSync(file, "utf-8");
|
|
7086
|
-
} catch {
|
|
7087
|
-
return;
|
|
7088
|
-
}
|
|
7089
|
-
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
7090
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path26.basename(file, ".md");
|
|
7091
|
-
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
7092
|
-
out.push({
|
|
7093
|
-
relPath: path26.relative(memoryAbs, file),
|
|
7094
|
-
title,
|
|
7095
|
-
updated,
|
|
7096
|
-
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
7097
|
-
mtime: stat.mtimeMs
|
|
7098
|
-
});
|
|
7099
|
-
});
|
|
7100
|
-
return out;
|
|
7101
|
-
}
|
|
7102
|
-
function extractQueryTerms(ctx) {
|
|
7103
|
-
const terms = [];
|
|
7104
|
-
const issue = ctx.data.issue;
|
|
7105
|
-
const pr = ctx.data.pr;
|
|
7106
|
-
if (issue?.title) terms.push(...tokenize(issue.title));
|
|
7107
|
-
if (pr?.title) terms.push(...tokenize(pr.title));
|
|
7108
|
-
return Array.from(new Set(terms)).slice(0, 20);
|
|
7109
|
-
}
|
|
7110
|
-
function tokenize(s) {
|
|
7111
|
-
return s.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((t) => t.length >= 3);
|
|
7112
|
-
}
|
|
7113
|
-
function scorePages(pages, terms) {
|
|
7114
|
-
return pages.map((p) => {
|
|
7115
|
-
const haystack = `${p.title} ${p.content}`.toLowerCase();
|
|
7116
|
-
let score = 0;
|
|
7117
|
-
for (const t of terms) {
|
|
7118
|
-
if (haystack.includes(t)) score++;
|
|
7119
|
-
}
|
|
7120
|
-
return { p, score };
|
|
7121
|
-
}).sort((a, b) => {
|
|
7122
|
-
if (b.score !== a.score) return b.score - a.score;
|
|
7123
|
-
return b.p.mtime - a.p.mtime;
|
|
7124
|
-
}).map((x) => x.p);
|
|
7125
|
-
}
|
|
7126
|
-
function sortByRecency(pages) {
|
|
7127
|
-
return [...pages].sort((a, b) => {
|
|
7128
|
-
if (a.updated && b.updated && a.updated !== b.updated) {
|
|
7129
|
-
return b.updated.localeCompare(a.updated);
|
|
7130
|
-
}
|
|
7131
|
-
return b.mtime - a.mtime;
|
|
7132
|
-
});
|
|
7133
|
-
}
|
|
7134
|
-
function formatBlock(pages) {
|
|
7135
|
-
if (pages.length === 0) return "";
|
|
7136
|
-
const lines = [
|
|
7137
|
-
"# Project memory (`.kody/memory/`)",
|
|
7138
|
-
"",
|
|
7139
|
-
"Pages from prior memorize ticks. Treat as advisory context \u2014 confirm against the codebase before acting.",
|
|
7140
|
-
""
|
|
7141
|
-
];
|
|
7142
|
-
let total = 0;
|
|
7143
|
-
for (const p of pages) {
|
|
7144
|
-
const block = [`## ${p.title} \u2014 \`${p.relPath}\``, "", p.content].join("\n");
|
|
7145
|
-
if (total + block.length > TOTAL_MAX_BYTES) {
|
|
7146
|
-
lines.push("_\u2026 (further pages truncated to fit budget)_");
|
|
7147
|
-
break;
|
|
7148
|
-
}
|
|
7149
|
-
lines.push(block);
|
|
7150
|
-
lines.push("");
|
|
7151
|
-
total += block.length;
|
|
7152
|
-
}
|
|
7153
|
-
return lines.join("\n");
|
|
7154
|
-
}
|
|
7155
|
-
function walkMd(root, visit) {
|
|
7156
|
-
const stack = [root];
|
|
7157
|
-
while (stack.length > 0) {
|
|
7158
|
-
const dir = stack.pop();
|
|
7159
|
-
let names;
|
|
7160
|
-
try {
|
|
7161
|
-
names = fs27.readdirSync(dir);
|
|
7162
|
-
} catch {
|
|
7163
|
-
continue;
|
|
7164
|
-
}
|
|
7165
|
-
for (const name of names) {
|
|
7166
|
-
if (name.startsWith(".")) continue;
|
|
7167
|
-
const full = path26.join(dir, name);
|
|
7168
|
-
let stat;
|
|
7169
|
-
try {
|
|
7170
|
-
stat = fs27.statSync(full);
|
|
7171
|
-
} catch {
|
|
7172
|
-
continue;
|
|
7173
|
-
}
|
|
7174
|
-
if (stat.isDirectory()) {
|
|
7175
|
-
stack.push(full);
|
|
7176
|
-
continue;
|
|
7177
|
-
}
|
|
7178
|
-
if (stat.isFile() && full.endsWith(".md")) visit(full);
|
|
7179
|
-
}
|
|
7180
|
-
}
|
|
7181
|
-
}
|
|
7182
|
-
|
|
7183
|
-
// src/scripts/loadPriorArt.ts
|
|
7184
|
-
var PER_PR_DIFF_MAX_BYTES = 8e3;
|
|
7185
|
-
var TOTAL_MAX_BYTES2 = 3e4;
|
|
7186
|
-
var TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
7187
|
-
var loadPriorArt = async (ctx, _profile, args) => {
|
|
7188
|
-
if (typeof ctx.data.priorArt === "string") return;
|
|
7189
|
-
const artifactName = typeof args?.artifactName === "string" ? args.artifactName : "priorArt";
|
|
7190
|
-
const state = ctx.data.taskState;
|
|
7191
|
-
const artifact = state?.artifacts?.[artifactName];
|
|
7192
|
-
const prNumbers = parsePrNumbers(artifact?.content);
|
|
7193
|
-
if (prNumbers.length === 0) {
|
|
7194
|
-
ctx.data.priorArt = "";
|
|
7195
|
-
return;
|
|
7196
|
-
}
|
|
7197
|
-
const blocks = [];
|
|
7198
|
-
for (const n of prNumbers) {
|
|
7199
|
-
const block = fetchPrBlock(n, ctx.cwd);
|
|
7200
|
-
if (block) blocks.push(block);
|
|
7201
|
-
}
|
|
7202
|
-
const joined = blocks.join("\n\n---\n\n");
|
|
7203
|
-
ctx.data.priorArt = joined.length > TOTAL_MAX_BYTES2 ? joined.slice(0, TOTAL_MAX_BYTES2) + TRUNCATED_SUFFIX2 : joined;
|
|
7204
|
-
};
|
|
7205
|
-
function parsePrNumbers(raw) {
|
|
7206
|
-
if (!raw) return [];
|
|
7207
|
-
const trimmed = raw.trim();
|
|
7208
|
-
if (!trimmed) return [];
|
|
7209
|
-
try {
|
|
7210
|
-
const parsed = JSON.parse(trimmed);
|
|
7211
|
-
if (!Array.isArray(parsed)) return [];
|
|
7212
|
-
return parsed.filter((n) => typeof n === "number" && Number.isInteger(n) && n > 0);
|
|
7213
|
-
} catch {
|
|
7214
|
-
return [];
|
|
7215
|
-
}
|
|
7216
|
-
}
|
|
7217
|
-
function fetchPrBlock(prNumber, cwd) {
|
|
7218
|
-
try {
|
|
7219
|
-
const metaRaw = gh(["pr", "view", String(prNumber), "--json", "title,state,url,mergedAt,closedAt"], { cwd });
|
|
7220
|
-
const meta = JSON.parse(metaRaw);
|
|
7221
|
-
const diff = truncate3(safeGh(["pr", "diff", String(prNumber)], cwd), PER_PR_DIFF_MAX_BYTES);
|
|
7222
|
-
const commentsRaw = safeGh(["pr", "view", String(prNumber), "--json", "comments,reviews"], cwd);
|
|
7223
|
-
const commentsBlock = formatReviewComments(commentsRaw);
|
|
7224
|
-
const lines = [
|
|
7225
|
-
`## Prior art: PR #${prNumber} \u2014 ${meta.title ?? "(no title)"} [${meta.state ?? "unknown"}]`,
|
|
7226
|
-
meta.url ? meta.url : "",
|
|
7227
|
-
"",
|
|
7228
|
-
"### Diff",
|
|
7229
|
-
"```diff",
|
|
7230
|
-
diff || "(empty)",
|
|
7231
|
-
"```"
|
|
7232
|
-
];
|
|
7233
|
-
if (commentsBlock) {
|
|
7234
|
-
lines.push("");
|
|
7235
|
-
lines.push("### Review comments");
|
|
7236
|
-
lines.push(commentsBlock);
|
|
7237
|
-
}
|
|
7238
|
-
return lines.filter((l) => l !== "").join("\n");
|
|
7239
|
-
} catch (err) {
|
|
7240
|
-
return `## Prior art: PR #${prNumber}
|
|
7241
|
-
_Could not fetch \u2014 ${err instanceof Error ? err.message : String(err)}_`;
|
|
7242
|
-
}
|
|
7243
|
-
}
|
|
7244
|
-
function safeGh(args, cwd) {
|
|
7245
|
-
try {
|
|
7246
|
-
return gh(args, { cwd });
|
|
7247
|
-
} catch {
|
|
7248
|
-
return "";
|
|
7249
|
-
}
|
|
7250
|
-
}
|
|
7251
|
-
function truncate3(s, max) {
|
|
7252
|
-
return s.length <= max ? s : s.slice(0, max) + TRUNCATED_SUFFIX2;
|
|
7253
|
-
}
|
|
7254
|
-
function formatReviewComments(raw) {
|
|
7255
|
-
if (!raw) return "";
|
|
7256
|
-
try {
|
|
7257
|
-
const parsed = JSON.parse(raw);
|
|
7258
|
-
const out = [];
|
|
7259
|
-
for (const c of parsed.comments ?? []) {
|
|
7260
|
-
if (!c.body) continue;
|
|
7261
|
-
out.push(`- **${c.author?.login ?? "unknown"}**: ${c.body.replace(/\n/g, " ").slice(0, 500)}`);
|
|
7262
|
-
}
|
|
7263
|
-
for (const r of parsed.reviews ?? []) {
|
|
7264
|
-
if (!r.body && !r.state) continue;
|
|
7265
|
-
const state = r.state ? ` (${r.state})` : "";
|
|
7266
|
-
const body = r.body ? `: ${r.body.replace(/\n/g, " ").slice(0, 500)}` : "";
|
|
7267
|
-
out.push(`- **${r.author?.login ?? "unknown"}**${state}${body}`);
|
|
7268
|
-
}
|
|
7269
|
-
return out.join("\n");
|
|
7270
|
-
} catch {
|
|
7271
|
-
return "";
|
|
7272
|
-
}
|
|
7273
|
-
}
|
|
7362
|
+
// src/scripts/index.ts
|
|
7363
|
+
init_loadMemoryContext();
|
|
7364
|
+
init_loadPriorArt();
|
|
7274
7365
|
|
|
7275
7366
|
// src/scripts/loadTaskContext.ts
|
|
7276
7367
|
init_events();
|
|
@@ -7441,6 +7532,7 @@ function parsePrNumber3(prUrl) {
|
|
|
7441
7532
|
}
|
|
7442
7533
|
|
|
7443
7534
|
// src/scripts/notifyTerminal.ts
|
|
7535
|
+
init_issue();
|
|
7444
7536
|
var notifyTerminal = async (ctx, _profile, _agentResult, args) => {
|
|
7445
7537
|
const issueNumber = ctx.args.issue;
|
|
7446
7538
|
if (!issueNumber || issueNumber <= 0) return;
|
|
@@ -7472,6 +7564,7 @@ function composeBody({ label, exit, prUrl, reason, dryRun }) {
|
|
|
7472
7564
|
}
|
|
7473
7565
|
|
|
7474
7566
|
// src/scripts/openQaIssue.ts
|
|
7567
|
+
init_issue();
|
|
7475
7568
|
var QA_LABEL = "kody:qa-report";
|
|
7476
7569
|
function qaAction2(verdict, payload) {
|
|
7477
7570
|
const type = verdict === "PASS" ? "QA_PASS" : verdict === "CONCERNS" ? "QA_CONCERNS" : verdict === "FAIL" ? "QA_FAIL" : "QA_COMPLETED";
|
|
@@ -7573,6 +7666,7 @@ QA_REPORT_POSTED=${created.url} (verdict: ${verdict})
|
|
|
7573
7666
|
};
|
|
7574
7667
|
|
|
7575
7668
|
// src/scripts/parseAgentResult.ts
|
|
7669
|
+
init_prompt();
|
|
7576
7670
|
var parseAgentResult2 = async (ctx, profile, agentResult) => {
|
|
7577
7671
|
if (!agentResult) {
|
|
7578
7672
|
ctx.data.agentDone = false;
|
|
@@ -7815,6 +7909,9 @@ var persistFlowState = async (ctx) => {
|
|
|
7815
7909
|
}
|
|
7816
7910
|
};
|
|
7817
7911
|
|
|
7912
|
+
// src/scripts/postIssueComment.ts
|
|
7913
|
+
init_issue();
|
|
7914
|
+
|
|
7818
7915
|
// src/scripts/prOutcome.ts
|
|
7819
7916
|
function readPrOutcome(data) {
|
|
7820
7917
|
const raw = data.prResult;
|
|
@@ -7950,6 +8047,7 @@ function postWith(type, n, body, cwd) {
|
|
|
7950
8047
|
}
|
|
7951
8048
|
|
|
7952
8049
|
// src/scripts/postPlanComment.ts
|
|
8050
|
+
init_issue();
|
|
7953
8051
|
var postPlanComment = async (ctx) => {
|
|
7954
8052
|
if (!ctx.data.agentDone) return;
|
|
7955
8053
|
const targetType = ctx.data.commentTargetType;
|
|
@@ -7980,6 +8078,7 @@ Comment \`kody run\` (prefixed with \`@\`) to execute this plan.`;
|
|
|
7980
8078
|
}
|
|
7981
8079
|
|
|
7982
8080
|
// src/scripts/postResearchComment.ts
|
|
8081
|
+
init_issue();
|
|
7983
8082
|
var postResearchComment = async (ctx) => {
|
|
7984
8083
|
if (!ctx.data.agentDone) return;
|
|
7985
8084
|
const targetType = ctx.data.commentTargetType;
|
|
@@ -8161,6 +8260,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
8161
8260
|
|
|
8162
8261
|
// src/scripts/resolveFlow.ts
|
|
8163
8262
|
import { execFileSync as execFileSync21 } from "child_process";
|
|
8263
|
+
init_issue();
|
|
8164
8264
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
8165
8265
|
var resolveFlow = async (ctx) => {
|
|
8166
8266
|
const prNumber = ctx.args.pr;
|
|
@@ -8309,6 +8409,7 @@ function pushEmptyCommit(branch, cwd) {
|
|
|
8309
8409
|
}
|
|
8310
8410
|
|
|
8311
8411
|
// src/deployments.ts
|
|
8412
|
+
init_issue();
|
|
8312
8413
|
function findPreviewDeploymentUrl(prNumber, cwd) {
|
|
8313
8414
|
const sha = getPrHeadSha(prNumber, cwd);
|
|
8314
8415
|
if (!sha) return null;
|
|
@@ -8461,6 +8562,7 @@ var resolveQaUrl = async (ctx) => {
|
|
|
8461
8562
|
|
|
8462
8563
|
// src/scripts/revertFlow.ts
|
|
8463
8564
|
import { execFileSync as execFileSync23 } from "child_process";
|
|
8565
|
+
init_issue();
|
|
8464
8566
|
var SHA_RE = /^[0-9a-f]{4,40}$/i;
|
|
8465
8567
|
var revertFlow = async (ctx) => {
|
|
8466
8568
|
const prNumber = ctx.args.pr;
|
|
@@ -8573,6 +8675,7 @@ function tryPostPr4(prNumber, body, cwd) {
|
|
|
8573
8675
|
}
|
|
8574
8676
|
|
|
8575
8677
|
// src/scripts/reviewFlow.ts
|
|
8678
|
+
init_issue();
|
|
8576
8679
|
var reviewFlow = async (ctx) => {
|
|
8577
8680
|
const prNumber = ctx.args.pr;
|
|
8578
8681
|
const pr = getPr(prNumber, ctx.cwd);
|
|
@@ -8600,6 +8703,7 @@ function tryPostPr5(prNumber, body, cwd) {
|
|
|
8600
8703
|
}
|
|
8601
8704
|
|
|
8602
8705
|
// src/scripts/runFlow.ts
|
|
8706
|
+
init_issue();
|
|
8603
8707
|
var runFlow = async (ctx) => {
|
|
8604
8708
|
const issueNumber = ctx.args.issue;
|
|
8605
8709
|
const issue = getIssue(issueNumber, ctx.cwd);
|
|
@@ -8886,6 +8990,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
8886
8990
|
};
|
|
8887
8991
|
|
|
8888
8992
|
// src/scripts/startFlow.ts
|
|
8993
|
+
init_issue();
|
|
8889
8994
|
import { execFileSync as execFileSync25 } from "child_process";
|
|
8890
8995
|
var API_TIMEOUT_MS9 = 3e4;
|
|
8891
8996
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
@@ -8935,6 +9040,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
8935
9040
|
|
|
8936
9041
|
// src/scripts/syncFlow.ts
|
|
8937
9042
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
9043
|
+
init_issue();
|
|
8938
9044
|
var syncFlow = async (ctx, _profile, args) => {
|
|
8939
9045
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
8940
9046
|
const prNumber = ctx.args.pr;
|
|
@@ -9178,6 +9284,7 @@ function downgrade2(ctx, reason) {
|
|
|
9178
9284
|
}
|
|
9179
9285
|
|
|
9180
9286
|
// src/scripts/waitForCi.ts
|
|
9287
|
+
init_issue();
|
|
9181
9288
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
9182
9289
|
var API_TIMEOUT_MS10 = 3e4;
|
|
9183
9290
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
@@ -10190,6 +10297,32 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
10190
10297
|
const runChild = input.__runChild ?? ((name, opts) => runExecutable(name, opts));
|
|
10191
10298
|
const reader = input.__readTaskState ?? readTaskState;
|
|
10192
10299
|
const issueNumber = ctx.args.issue;
|
|
10300
|
+
let preloadedSnapshot;
|
|
10301
|
+
if (profile.preloadContext) {
|
|
10302
|
+
try {
|
|
10303
|
+
const { loadConventions: loadConventions2 } = await Promise.resolve().then(() => (init_loadConventions(), loadConventions_exports));
|
|
10304
|
+
const { loadPriorArt: loadPriorArt2 } = await Promise.resolve().then(() => (init_loadPriorArt(), loadPriorArt_exports));
|
|
10305
|
+
const { loadMemoryContext: loadMemoryContext2 } = await Promise.resolve().then(() => (init_loadMemoryContext(), loadMemoryContext_exports));
|
|
10306
|
+
const { loadCoverageRules: loadCoverageRules2 } = await Promise.resolve().then(() => (init_loadCoverageRules(), loadCoverageRules_exports));
|
|
10307
|
+
await loadConventions2(ctx, profile);
|
|
10308
|
+
await loadPriorArt2(ctx, profile);
|
|
10309
|
+
await loadMemoryContext2(ctx, profile);
|
|
10310
|
+
await loadCoverageRules2(ctx, profile);
|
|
10311
|
+
preloadedSnapshot = {};
|
|
10312
|
+
for (const k of ["issue", "conventions", "priorArt", "memoryContext", "coverageRules", "taskContext"]) {
|
|
10313
|
+
if (ctx.data[k] !== void 0) preloadedSnapshot[k] = ctx.data[k];
|
|
10314
|
+
}
|
|
10315
|
+
process.stderr.write(
|
|
10316
|
+
`[kody container] preloadContext: snapshot keys=${Object.keys(preloadedSnapshot).join(",")}
|
|
10317
|
+
`
|
|
10318
|
+
);
|
|
10319
|
+
} catch (err) {
|
|
10320
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10321
|
+
process.stderr.write(`[kody container] preloadContext failed (falling back to per-child loads): ${msg}
|
|
10322
|
+
`);
|
|
10323
|
+
preloadedSnapshot = void 0;
|
|
10324
|
+
}
|
|
10325
|
+
}
|
|
10193
10326
|
let currentIdx = 0;
|
|
10194
10327
|
let iteration = 0;
|
|
10195
10328
|
let knownPrUrl;
|
|
@@ -10271,7 +10404,10 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
10271
10404
|
config: input.config,
|
|
10272
10405
|
skipConfig: input.skipConfig,
|
|
10273
10406
|
verbose: input.verbose,
|
|
10274
|
-
quiet: input.quiet
|
|
10407
|
+
quiet: input.quiet,
|
|
10408
|
+
// Phase 5 in-process handoff — undefined when preloadContext
|
|
10409
|
+
// is off, so children fall back to their own loaders.
|
|
10410
|
+
preloadedData: preloadedSnapshot
|
|
10275
10411
|
});
|
|
10276
10412
|
emitEvent(input.cwd, {
|
|
10277
10413
|
executable: profile.name,
|
|
@@ -10437,6 +10573,7 @@ function flattenConfig(obj, prefix = "") {
|
|
|
10437
10573
|
}
|
|
10438
10574
|
|
|
10439
10575
|
// src/kody-cli.ts
|
|
10576
|
+
init_issue();
|
|
10440
10577
|
var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
|
|
10441
10578
|
|
|
10442
10579
|
Usage:
|