@xn-intenton-z2a/agentic-lib 7.4.16 → 7.4.18

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.
@@ -1199,6 +1199,7 @@ function initPurgeGitHub() {
1199
1199
  "cumulative-maintain-library = 0",
1200
1200
  "cumulative-nop-cycles = 0",
1201
1201
  "total-tokens = 0",
1202
+ "total-duration-ms = 0",
1202
1203
  "",
1203
1204
  "[budget]",
1204
1205
  "transformation-budget-used = 0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.4.16",
3
+ "version": "7.4.18",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -541,8 +541,17 @@ function parseReasoning(content) {
541
541
  async function executeDispatch(octokit, repo, actionName, params, ctx) {
542
542
  const workflowFile = actionName.replace("dispatch:", "") + ".yml";
543
543
  const inputs = {};
544
- if (params["pr-number"]) inputs["pr-number"] = params["pr-number"];
545
- if (params["issue-number"]) inputs["issue-number"] = params["issue-number"];
544
+ if (params["pr-number"]) inputs["pr-number"] = String(params["pr-number"]);
545
+ if (params["issue-number"]) {
546
+ const issueNum = String(params["issue-number"]);
547
+ // Guard: reject placeholder issue numbers from LLM (e.g. "<created_issue_number>")
548
+ if (/^\d+$/.test(issueNum)) {
549
+ inputs["issue-number"] = issueNum;
550
+ } else {
551
+ core.warning(`dispatch: ignoring non-numeric issue-number: ${issueNum}`);
552
+ }
553
+ }
554
+ if (params.mode) inputs.mode = String(params.mode);
546
555
 
547
556
  // Pass discussion-url when dispatching the bot
548
557
  if (workflowFile === "agentic-lib-bot.yml" && ctx?.activeDiscussionUrl) {
@@ -579,8 +588,61 @@ async function executeDispatch(octokit, repo, actionName, params, ctx) {
579
588
  }
580
589
 
581
590
  async function executeCreateIssue(octokit, repo, params, ctx) {
582
- const title = params.title || "Untitled issue";
583
- const labels = params.labels ? params.labels.split(",").map((l) => l.trim()) : ["automated"];
591
+ // Derive title: use params.title, fall back to first line of body, fall back to feature name
592
+ let title = (params.title || "").trim();
593
+ const bodyText = (params.body || params.details || "").trim();
594
+ const feature = (params.feature || "").trim();
595
+
596
+ if (!title && bodyText) {
597
+ // Extract title from body: use first heading or first non-empty line
598
+ const headingMatch = bodyText.match(/^#+ (.+)/m);
599
+ const titleMatch = bodyText.match(/^Title:\s*(.+)/im);
600
+ if (titleMatch) {
601
+ title = titleMatch[1].trim();
602
+ } else if (headingMatch) {
603
+ title = headingMatch[1].trim();
604
+ } else {
605
+ // Use first line, truncated
606
+ title = bodyText.split("\n")[0].substring(0, 120).trim();
607
+ }
608
+ }
609
+ if (!title && feature) {
610
+ title = `feat: implement ${feature}`;
611
+ }
612
+ if (!title) {
613
+ core.warning("create-issue: no title, body, or feature provided — skipping");
614
+ return "skipped:no-title";
615
+ }
616
+
617
+ const rawLabels = params.labels;
618
+ const labels = Array.isArray(rawLabels)
619
+ ? rawLabels.map((l) => String(l).trim()).filter(Boolean)
620
+ : typeof rawLabels === "string"
621
+ ? rawLabels.split(",").map((l) => l.trim()).filter(Boolean)
622
+ : ["automated"];
623
+ if (!labels.includes("automated")) labels.push("automated");
624
+
625
+ // Build rich issue body with context
626
+ const bodyParts = [];
627
+ if (bodyText) {
628
+ bodyParts.push(bodyText);
629
+ }
630
+ if (feature) {
631
+ bodyParts.push("");
632
+ bodyParts.push(`## Related Feature`);
633
+ bodyParts.push(`Feature spec: \`features/${feature}.md\``);
634
+ }
635
+ // Add mission context from MISSION.md
636
+ const missionText = ctx?.mission || "";
637
+ if (missionText) {
638
+ const missionHeading = missionText.match(/^#\s+(.+)/m);
639
+ const missionName = missionHeading ? missionHeading[1].trim() : "MISSION.md";
640
+ bodyParts.push("");
641
+ bodyParts.push(`## Context`);
642
+ bodyParts.push(`Mission: ${missionName}`);
643
+ bodyParts.push(`Created by: agentic-step supervisor`);
644
+ }
645
+ const body = bodyParts.join("\n");
584
646
 
585
647
  // Dedup guard: skip if a similarly-titled issue was closed in the last hour
586
648
  // Exclude issues closed before the init timestamp (cross-scenario protection)
@@ -610,13 +672,18 @@ async function executeCreateIssue(octokit, repo, params, ctx) {
610
672
  }
611
673
 
612
674
  core.info(`Creating issue: ${title}`);
613
- const { data: issue } = await octokit.rest.issues.create({ ...repo, title, labels });
675
+ const { data: issue } = await octokit.rest.issues.create({ ...repo, title, body, labels });
614
676
  return `created-issue:#${issue.number}`;
615
677
  }
616
678
 
617
679
  async function executeLabelIssue(octokit, repo, params) {
618
680
  const issueNumber = Number(params["issue-number"]);
619
- const labels = params.labels ? params.labels.split(",").map((l) => l.trim()) : [];
681
+ const rawLabels = params.labels;
682
+ const labels = Array.isArray(rawLabels)
683
+ ? rawLabels.map((l) => String(l).trim()).filter(Boolean)
684
+ : typeof rawLabels === "string"
685
+ ? rawLabels.split(",").map((l) => l.trim()).filter(Boolean)
686
+ : [];
620
687
  if (issueNumber && labels.length > 0) {
621
688
  core.info(`Labelling issue #${issueNumber}: ${labels.join(", ")}`);
622
689
  await octokit.rest.issues.addLabels({ ...repo, issue_number: issueNumber, labels });
@@ -677,13 +744,36 @@ async function executeSetSchedule(octokit, repo, frequency) {
677
744
  }
678
745
 
679
746
  async function executeAction(octokit, repo, action, params, ctx) {
680
- if (action.startsWith("dispatch:")) return executeDispatch(octokit, repo, action, params, ctx);
681
- if (action.startsWith("set-schedule:")) return executeSetSchedule(octokit, repo, action.replace("set-schedule:", ""));
682
- if (action === "nop") return "nop";
683
- const handler = ACTION_HANDLERS[action];
684
- if (handler) return handler(octokit, repo, params, ctx);
685
- core.debug(`Ignoring unrecognised action: ${action}`);
686
- return `unknown:${action}`;
747
+ // LLMs sometimes inline params in the action string as pipe-delimited key: value pairs
748
+ // e.g. "github:create-issue | title: foo | body: bar" instead of using the params object
749
+ let cleanAction = action;
750
+ let mergedParams = { ...params };
751
+ if (action.includes(" | ")) {
752
+ const parts = action.split(" | ");
753
+ cleanAction = parts[0].trim();
754
+ for (let i = 1; i < parts.length; i++) {
755
+ const colonIdx = parts[i].indexOf(":");
756
+ if (colonIdx > 0) {
757
+ const key = parts[i].substring(0, colonIdx).trim();
758
+ const value = parts[i].substring(colonIdx + 1).trim();
759
+ // Only add to params if not already set (explicit params take precedence)
760
+ if (!(key in mergedParams)) {
761
+ mergedParams[key] = value;
762
+ }
763
+ }
764
+ }
765
+ if (cleanAction !== action) {
766
+ core.info(`Parsed inline params from action string: ${cleanAction} + ${Object.keys(mergedParams).join(", ")}`);
767
+ }
768
+ }
769
+
770
+ if (cleanAction.startsWith("dispatch:")) return executeDispatch(octokit, repo, cleanAction, mergedParams, ctx);
771
+ if (cleanAction.startsWith("set-schedule:")) return executeSetSchedule(octokit, repo, cleanAction.replace("set-schedule:", ""));
772
+ if (cleanAction === "nop") return "nop";
773
+ const handler = ACTION_HANDLERS[cleanAction];
774
+ if (handler) return handler(octokit, repo, mergedParams, ctx);
775
+ core.debug(`Ignoring unrecognised action: ${cleanAction}`);
776
+ return `unknown:${cleanAction}`;
687
777
  }
688
778
 
689
779
  /**
@@ -58,6 +58,14 @@ runs:
58
58
  sleep $((attempt * 2))
59
59
  continue
60
60
  }
61
+ # After rebase, check if our changes survived (rebase may drop empty commits)
62
+ LOCAL_SHA=$(git rev-parse HEAD)
63
+ REMOTE_SHA=$(git rev-parse "origin/$REF" 2>/dev/null || echo "")
64
+ if [ "$LOCAL_SHA" = "$REMOTE_SHA" ]; then
65
+ echo "Rebase dropped local commit (already on remote) — nothing to push"
66
+ PUSH_SUCCESS="true"
67
+ break
68
+ fi
61
69
  fi
62
70
  # Use HEAD:refs/heads/$REF to handle detached HEAD state
63
71
  # (actions/checkout with a SHA puts us in detached HEAD, so
@@ -17,7 +17,7 @@
17
17
  "author": "",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "@xn-intenton-z2a/agentic-lib": "^7.4.16"
20
+ "@xn-intenton-z2a/agentic-lib": "^7.4.18"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@playwright/test": "^1.58.0",