@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.65",
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/issue.ts
1779
- import { execFileSync as execFileSync3 } from "child_process";
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 extractBlock(text, startMarker, endMarker) {
3344
- const startIdx = text.search(startMarker);
3345
- if (startIdx === -1) return "";
3346
- const afterStart = text.slice(startIdx).replace(startMarker, "");
3347
- const endIdx = afterStart.search(endMarker);
3348
- const body = endIdx === -1 ? afterStart : afterStart.slice(0, endIdx);
3349
- return body.replace(/\n\s*```\s*$/g, "").trim();
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/loadConventions.ts
6813
- var loadConventions = async (ctx) => {
6814
- if (Array.isArray(ctx.data.conventions)) return;
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/loadMemoryContext.ts
7044
- import * as fs27 from "fs";
7045
- import * as path26 from "path";
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: