@defend-tech/opencode-optima 0.1.74 → 0.1.76

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.
@@ -8635,6 +8635,8 @@ var activeClickUpWebhookListeners = /* @__PURE__ */ new Map();
8635
8635
  var activeClickUpWebhookLifecycleRegistry = /* @__PURE__ */ new Map();
8636
8636
  var activeClickUpTaskRoutes = /* @__PURE__ */ new Map();
8637
8637
  var GITHUB_WEBHOOK_EVENTS = ["pull_request", "pull_request_review", "pull_request_review_comment", "issue_comment"];
8638
+ var OPTIMA_GITHUB_COMMITTER_NAME = "Optima Product Manager";
8639
+ var OPTIMA_GITHUB_COMMITTER_EMAIL = "optima-product-manager[bot]@users.noreply.github.com";
8638
8640
  function isRootDirectory(candidate) {
8639
8641
  const resolved = path6.resolve(candidate);
8640
8642
  return resolved === path6.parse(resolved).root;
@@ -9295,6 +9297,40 @@ function addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, start
9295
9297
  runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
9296
9298
  }
9297
9299
  }
9300
+ function normalizeOptimaGitIdentity(identity = {}) {
9301
+ return {
9302
+ name: String(identity.name || OPTIMA_GITHUB_COMMITTER_NAME).trim(),
9303
+ email: String(identity.email || OPTIMA_GITHUB_COMMITTER_EMAIL).trim(),
9304
+ sign: identity.sign === true,
9305
+ signingKey: String(identity.signingKey || identity.signing_key || "").trim(),
9306
+ signingKeyFile: String(identity.signingKeyFile || identity.signing_key_file || "").trim(),
9307
+ signingFormat: String(identity.signingFormat || identity.signing_format || "ssh").trim().toLowerCase()
9308
+ };
9309
+ }
9310
+ function configureOptimaWorktreeGitIdentity({ worktreePath, identity = {}, runGitFn = runGit } = {}) {
9311
+ if (!worktreePath || !fs5.existsSync(worktreePath)) return { configured: false, reason: "worktree_missing" };
9312
+ const normalized = normalizeOptimaGitIdentity(identity);
9313
+ if (!normalized.name || !normalized.email) return { configured: false, reason: "identity_missing" };
9314
+ try {
9315
+ runGitFn(worktreePath, ["config", "user.name", normalized.name]);
9316
+ runGitFn(worktreePath, ["config", "user.email", normalized.email]);
9317
+ } catch (error) {
9318
+ return { configured: false, reason: "git_config_failed", message: error.message };
9319
+ }
9320
+ let signingKey = normalized.signingKey;
9321
+ if (!signingKey && normalized.signingKeyFile) signingKey = expandHomePath(normalized.signingKeyFile);
9322
+ if (normalized.sign) {
9323
+ if (!signingKey) return { configured: false, reason: "signing_key_missing", name: normalized.name, email: normalized.email, sign: true };
9324
+ try {
9325
+ runGitFn(worktreePath, ["config", "commit.gpgsign", "true"]);
9326
+ runGitFn(worktreePath, ["config", "gpg.format", normalized.signingFormat || "ssh"]);
9327
+ runGitFn(worktreePath, ["config", "user.signingkey", signingKey]);
9328
+ } catch (error) {
9329
+ return { configured: false, reason: "git_signing_config_failed", message: error.message, name: normalized.name, email: normalized.email, sign: true };
9330
+ }
9331
+ }
9332
+ return { configured: true, name: normalized.name, email: normalized.email, sign: normalized.sign, signingFormat: normalized.signingFormat, signingKeyConfigured: Boolean(signingKey) };
9333
+ }
9298
9334
  function clickUpOpenChamberWorktreeName(branch = "") {
9299
9335
  return String(branch || "").trim().replace(/\//g, "-");
9300
9336
  }
@@ -9445,7 +9481,7 @@ async function registerOpenChamberClickUpWorktree({ openchamberBaseUrl, opencode
9445
9481
  const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: openchamberBaseUrl || baseUrl, opencodeBaseUrl: opencodeBaseUrl || baseUrl, baseWorktree, worktreePath, branch, fetchImpl });
9446
9482
  return { branch, worktree: path6.resolve(worktreePath), reused: true, provider: "openchamber", openChamber: { source, visibility } };
9447
9483
  }
9448
- async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, openchamberBaseUrl = "", opencodeBaseUrl = "", baseUrl = "", fetchImpl = globalThis.fetch, log = null } = {}) {
9484
+ async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, openchamberBaseUrl = "", opencodeBaseUrl = "", baseUrl = "", fetchImpl = globalThis.fetch, log = null, gitIdentity = {} } = {}) {
9449
9485
  const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
9450
9486
  const effectiveOpenCodeBaseUrl = opencodeBaseUrl || baseUrl;
9451
9487
  const effectiveParent = parentTaskId || taskId;
@@ -9456,27 +9492,33 @@ async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId,
9456
9492
  const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
9457
9493
  if (existing) {
9458
9494
  const registered = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch, worktreePath: existing.worktree, fetchImpl, source: "metadata_reuse" });
9495
+ const identity2 = configureOptimaWorktreeGitIdentity({ worktreePath: registered.worktree, identity: gitIdentity, runGitFn });
9459
9496
  log?.({ type: "openchamber_worktree_registered", taskId, branch, worktree: registered.worktree, source: "metadata_reuse", visibility: registered.openChamber.visibility });
9460
- return { ...registered, parentBranch: parentBranch || void 0, prTarget };
9497
+ return { ...registered, parentBranch: parentBranch || void 0, prTarget, gitIdentity: identity2 };
9461
9498
  }
9462
9499
  const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
9463
9500
  if (fs5.existsSync(worktreePath)) {
9464
9501
  const registered = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch, worktreePath, fetchImpl, source: "existing_directory" });
9502
+ const identity2 = configureOptimaWorktreeGitIdentity({ worktreePath: registered.worktree, identity: gitIdentity, runGitFn });
9465
9503
  log?.({ type: "openchamber_worktree_registered", taskId, branch, worktree: registered.worktree, source: "existing_directory", visibility: registered.openChamber.visibility });
9466
- return { ...registered, parentBranch: parentBranch || void 0, prTarget };
9504
+ return { ...registered, parentBranch: parentBranch || void 0, prTarget, gitIdentity: identity2 };
9467
9505
  }
9468
9506
  let parentBootstrap = null;
9469
9507
  if (isSubtask) {
9470
9508
  const parentWorktree = deriveClickUpWorktree({ baseWorktree, taskId: effectiveParent, taskType, parentTaskId: effectiveParent });
9471
9509
  if (fs5.existsSync(parentWorktree)) {
9472
9510
  const registeredParent = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch: parentBranch, worktreePath: parentWorktree, fetchImpl, source: "parent_existing_directory" });
9511
+ const parentIdentity = configureOptimaWorktreeGitIdentity({ worktreePath: registeredParent.worktree, identity: gitIdentity, runGitFn });
9473
9512
  parentBootstrap = { branch: parentBranch, worktree: registeredParent.worktree, reused: true, provider: "openchamber", visibility: registeredParent.openChamber.visibility };
9513
+ if (parentIdentity.configured) parentBootstrap.gitIdentity = parentIdentity;
9474
9514
  } else {
9475
9515
  const parentStartPoint = resolveClickUpDevStartPoint(baseWorktree, runGitFn);
9476
9516
  const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
9477
9517
  const createdParent = await createOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, branchExists: parentBranchExists, fetchImpl });
9478
9518
  const parentVisibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, worktreePath: createdParent.worktree, branch: parentBranch, fetchImpl });
9479
9519
  parentBootstrap = { branch: parentBranch, worktree: createdParent.worktree, startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists, provider: "openchamber", visibility: parentVisibility };
9520
+ const parentIdentity = configureOptimaWorktreeGitIdentity({ worktreePath: parentBootstrap.worktree, identity: gitIdentity, runGitFn });
9521
+ if (parentIdentity.configured) parentBootstrap.gitIdentity = parentIdentity;
9480
9522
  log?.({ type: "openchamber_worktree_created", taskId: effectiveParent, branch: parentBranch, worktree: parentBootstrap.worktree, startPoint: parentBootstrap.startPoint, visibility: parentVisibility });
9481
9523
  }
9482
9524
  }
@@ -9484,8 +9526,9 @@ async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId,
9484
9526
  const branchExists = clickUpGitRefExists(baseWorktree, branch, runGitFn);
9485
9527
  const created = await createOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, branch, worktreePath, startPoint, branchExists, fetchImpl });
9486
9528
  const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, worktreePath: created.worktree, branch, fetchImpl });
9529
+ const identity = configureOptimaWorktreeGitIdentity({ worktreePath: created.worktree, identity: gitIdentity, runGitFn });
9487
9530
  log?.({ type: "openchamber_worktree_created", taskId, branch, worktree: created.worktree, startPoint: branchExists ? branch : startPoint, visibility });
9488
- return { branch, worktree: created.worktree, reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget, parentBootstrap: parentBootstrap || void 0, provider: "openchamber", openChamber: { source: "created", visibility } };
9531
+ return { branch, worktree: created.worktree, reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget, parentBootstrap: parentBootstrap || void 0, provider: "openchamber", openChamber: { source: "created", visibility }, gitIdentity: identity };
9489
9532
  }
9490
9533
  async function ensureClickUpTaskWorktreeForWebhook({ opencodeBaseUrl = "", opencodeBaseUrlConfigured = false, openchamberBaseUrl = "", openchamberBaseUrlConfigured = false, clickupClient = null, webhookWorktree = process.cwd(), fetchImpl = globalThis.fetch, ...options } = {}) {
9491
9534
  void opencodeBaseUrlConfigured;
@@ -9498,15 +9541,18 @@ async function ensureClickUpTaskWorktreeForWebhook({ opencodeBaseUrl = "", openc
9498
9541
  throw new Error(message);
9499
9542
  }
9500
9543
  }
9501
- function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false } = {}) {
9544
+ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false, gitIdentity = {} } = {}) {
9502
9545
  const effectiveParent = parentTaskId || taskId;
9503
9546
  const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
9504
9547
  const parentBranch = isSubtask ? deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : "";
9505
9548
  const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
9506
9549
  const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
9507
- if (existing) return { ...existing, branch, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
9550
+ if (existing) return { ...existing, branch, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", gitIdentity: configureOptimaWorktreeGitIdentity({ worktreePath: existing.worktree, identity: gitIdentity, runGitFn }) };
9508
9551
  const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
9509
- if (fs5.existsSync(worktreePath)) return { branch, worktree: path6.resolve(worktreePath), reused: true, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev" };
9552
+ if (fs5.existsSync(worktreePath)) {
9553
+ const resolvedWorktree2 = path6.resolve(worktreePath);
9554
+ return { branch, worktree: resolvedWorktree2, reused: true, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", gitIdentity: configureOptimaWorktreeGitIdentity({ worktreePath: resolvedWorktree2, identity: gitIdentity, runGitFn }) };
9555
+ }
9510
9556
  if (allowNonGitFallback) {
9511
9557
  fs5.mkdirSync(worktreePath, { recursive: true });
9512
9558
  return { branch, worktree: path6.resolve(worktreePath), reused: false, fallback: "non_git", parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", startPoint: parentBranch || "dev" };
@@ -9519,13 +9565,18 @@ function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tare
9519
9565
  const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
9520
9566
  addClickUpWorktreeForBranch({ baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, runGitFn });
9521
9567
  parentBootstrap = { branch: parentBranch, worktree: path6.resolve(parentWorktree), startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists };
9568
+ const parentIdentity = configureOptimaWorktreeGitIdentity({ worktreePath: parentBootstrap.worktree, identity: gitIdentity, runGitFn });
9569
+ if (parentIdentity.configured) parentBootstrap.gitIdentity = parentIdentity;
9522
9570
  } else {
9523
9571
  parentBootstrap = { branch: parentBranch, worktree: path6.resolve(parentWorktree), reused: true };
9572
+ const parentIdentity = configureOptimaWorktreeGitIdentity({ worktreePath: parentBootstrap.worktree, identity: gitIdentity, runGitFn });
9573
+ if (parentIdentity.configured) parentBootstrap.gitIdentity = parentIdentity;
9524
9574
  }
9525
9575
  }
9526
9576
  const startPoint = isSubtask ? parentBranch : resolveClickUpDevStartPoint(baseWorktree, runGitFn);
9527
9577
  addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, startPoint, runGitFn });
9528
- return { branch, worktree: path6.resolve(worktreePath), reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", parentBootstrap: parentBootstrap || void 0 };
9578
+ const resolvedWorktree = path6.resolve(worktreePath);
9579
+ return { branch, worktree: resolvedWorktree, reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget: parentBranch || "dev", parentBootstrap: parentBootstrap || void 0, gitIdentity: configureOptimaWorktreeGitIdentity({ worktreePath: resolvedWorktree, identity: gitIdentity, runGitFn }) };
9529
9580
  }
9530
9581
  function normalizeClickUpDefinitionDocParent(parent = {}) {
9531
9582
  const docId = String(parent.doc_id || parent.docId || CLICKUP_DEFINITION_DOC_PARENT.doc_id).trim();
@@ -9904,6 +9955,8 @@ function defaultGitHubWebhookPublicUrl(clickupPublicUrl = "") {
9904
9955
  function normalizeGitHubWebhookConfig(rawGithub = {}, clickupWebhook = {}) {
9905
9956
  const github = isPlainObject(rawGithub) ? rawGithub : {};
9906
9957
  const webhook = isPlainObject(github.webhook) ? github.webhook : {};
9958
+ const app = isPlainObject(github.app) ? github.app : {};
9959
+ const committer = isPlainObject(github.committer) ? github.committer : {};
9907
9960
  const enabled = github.enabled === true || webhook.enabled === true;
9908
9961
  const events = Array.isArray(webhook.events) && webhook.events.length > 0 ? [...new Set(webhook.events.map((event) => String(event || "").trim()).filter(Boolean))] : [...GITHUB_WEBHOOK_EVENTS];
9909
9962
  const publicUrl = String(webhook.public_url || webhook.publicUrl || github.public_url || github.publicUrl || defaultGitHubWebhookPublicUrl(clickupWebhook.publicUrl)).trim();
@@ -9917,6 +9970,21 @@ function normalizeGitHubWebhookConfig(rawGithub = {}, clickupWebhook = {}) {
9917
9970
  repository,
9918
9971
  owner: String(github.owner || ownerFromRepo || "").trim(),
9919
9972
  repo: String(github.repo_name || github.repoName || nameFromRepo || "").trim(),
9973
+ app: {
9974
+ enabled: github.app_enabled === true || github.appEnabled === true || app.enabled === true,
9975
+ appId: String(app.app_id || app.appId || github.app_id || github.appId || "").trim(),
9976
+ installationId: String(app.installation_id || app.installationId || github.installation_id || github.installationId || "").trim(),
9977
+ privateKey: String(app.private_key || app.privateKey || github.private_key || github.privateKey || "").trim(),
9978
+ privateKeyFile: String(app.private_key_file || app.privateKeyFile || github.private_key_file || github.privateKeyFile || "").trim()
9979
+ },
9980
+ committer: {
9981
+ name: String(committer.name || github.committer_name || github.committerName || OPTIMA_GITHUB_COMMITTER_NAME).trim(),
9982
+ email: String(committer.email || github.committer_email || github.committerEmail || OPTIMA_GITHUB_COMMITTER_EMAIL).trim(),
9983
+ sign: committer.sign === true || github.committer_sign === true || github.committerSign === true,
9984
+ signingKey: String(committer.signing_key || committer.signingKey || github.signing_key || github.signingKey || "").trim(),
9985
+ signingKeyFile: String(committer.signing_key_file || committer.signingKeyFile || github.signing_key_file || github.signingKeyFile || "").trim(),
9986
+ signingFormat: String(committer.signing_format || committer.signingFormat || github.signing_format || github.signingFormat || "ssh").trim().toLowerCase()
9987
+ },
9920
9988
  webhook: {
9921
9989
  enabled,
9922
9990
  publicUrl,
@@ -10011,6 +10079,11 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
10011
10079
  const missingGitHubEvents = GITHUB_WEBHOOK_EVENTS.filter((event) => !config.github.webhook.events.includes(event));
10012
10080
  if (missingGitHubEvents.length > 0) errors.push(`clickup.github.webhook.events missing: ${missingGitHubEvents.join(", ")}`);
10013
10081
  }
10082
+ if (config.github.app.enabled) {
10083
+ if (!config.github.app.appId) errors.push("clickup.github.app.app_id is required when GitHub App auth is enabled");
10084
+ if (!config.github.app.installationId) errors.push("clickup.github.app.installation_id is required when GitHub App auth is enabled");
10085
+ if (!config.github.app.privateKey && !config.github.app.privateKeyFile) errors.push("clickup.github.app.private_key_file is required when GitHub App auth is enabled");
10086
+ }
10014
10087
  if (config.opencode.baseUrl) {
10015
10088
  try {
10016
10089
  new URL(config.opencode.baseUrl);
@@ -10166,26 +10239,392 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
10166
10239
  }
10167
10240
  };
10168
10241
  }
10242
+ function base64UrlJson(value) {
10243
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
10244
+ }
10245
+ function resolveGitHubAppPrivateKey(app = {}) {
10246
+ const direct = resolveSecretReference(app.privateKey || "");
10247
+ if (direct) return direct.replace(/\\n/g, "\n");
10248
+ const file = expandHomePath(app.privateKeyFile || "");
10249
+ if (!file) return "";
10250
+ try {
10251
+ return fs5.readFileSync(file, "utf8").trim().replace(/\\n/g, "\n");
10252
+ } catch {
10253
+ return "";
10254
+ }
10255
+ }
10256
+ function createGitHubAppJwt({ appId, privateKey, now = () => /* @__PURE__ */ new Date() } = {}) {
10257
+ const key = String(privateKey || "").trim();
10258
+ const issuer = String(appId || "").trim();
10259
+ if (!issuer || !key) throw new Error("GitHub App app_id and private key are required");
10260
+ const nowSeconds = Math.floor(now().getTime() / 1e3);
10261
+ const header = base64UrlJson({ alg: "RS256", typ: "JWT" });
10262
+ const payload = base64UrlJson({ iat: nowSeconds - 60, exp: nowSeconds + 540, iss: issuer });
10263
+ const unsigned = `${header}.${payload}`;
10264
+ const signature = crypto.createSign("RSA-SHA256").update(unsigned).end().sign(key, "base64url");
10265
+ return `${unsigned}.${signature}`;
10266
+ }
10267
+ function encodeGitHubPathSegment(value) {
10268
+ return encodeURIComponent(String(value || ""));
10269
+ }
10270
+ function encodeGitHubBranchRef(branch = "") {
10271
+ return String(branch || "").split("/").map(encodeGitHubPathSegment).join("/");
10272
+ }
10273
+ function parseNullSeparatedGitOutput(output = "") {
10274
+ return String(output || "").split("\0").map((item) => item.trim()).filter(Boolean);
10275
+ }
10276
+ function listWorktreeChangedPaths(worktree, runGitFn = runGit) {
10277
+ const tracked = parseNullSeparatedGitOutput(runGitFn(worktree, ["diff", "--name-only", "-z", "HEAD", "--"]));
10278
+ const untracked = parseNullSeparatedGitOutput(runGitFn(worktree, ["ls-files", "--others", "--exclude-standard", "-z", "--"]));
10279
+ return [.../* @__PURE__ */ new Set([...tracked, ...untracked])].filter((item) => item && !item.startsWith("../") && !path6.isAbsolute(item));
10280
+ }
10281
+ function currentGitBranch(worktree, runGitFn = runGit) {
10282
+ return String(runGitFn(worktree, ["rev-parse", "--abbrev-ref", "HEAD"]) || "").trim();
10283
+ }
10284
+ function treeEntryForWorktreePath(worktree, gitPath) {
10285
+ const absolutePath = path6.join(worktree, ...String(gitPath || "").split("/"));
10286
+ if (!fs5.existsSync(absolutePath)) return { path: gitPath, mode: "100644", type: "blob", sha: null, deleted: true };
10287
+ const stat = fs5.lstatSync(absolutePath);
10288
+ if (stat.isDirectory()) return null;
10289
+ if (stat.isSymbolicLink()) {
10290
+ return {
10291
+ path: gitPath,
10292
+ mode: "120000",
10293
+ type: "blob",
10294
+ content: fs5.readlinkSync(absolutePath),
10295
+ encoding: "utf-8"
10296
+ };
10297
+ }
10298
+ return {
10299
+ path: gitPath,
10300
+ mode: stat.mode & 73 ? "100755" : "100644",
10301
+ type: "blob",
10302
+ content: fs5.readFileSync(absolutePath).toString("base64"),
10303
+ encoding: "base64"
10304
+ };
10305
+ }
10306
+ function appendGitHubQuery(pathname, params = {}) {
10307
+ const query = new URLSearchParams();
10308
+ for (const [key, value] of Object.entries(params || {})) {
10309
+ if (value === void 0 || value === null || value === "") continue;
10310
+ query.set(key, String(value));
10311
+ }
10312
+ const suffix = query.toString();
10313
+ return suffix ? `${pathname}?${suffix}` : pathname;
10314
+ }
10315
+ function timestampMs(value = "") {
10316
+ const parsed = Date.parse(String(value || ""));
10317
+ return Number.isFinite(parsed) ? parsed : 0;
10318
+ }
10319
+ function sortNewestFirst(items = []) {
10320
+ return [...items].sort((a, b) => {
10321
+ const bTime = Math.max(timestampMs(b?.updated_at), timestampMs(b?.created_at));
10322
+ const aTime = Math.max(timestampMs(a?.updated_at), timestampMs(a?.created_at));
10323
+ return bTime - aTime;
10324
+ });
10325
+ }
10326
+ function matchesVercelStatusContext(context = "", expected = "") {
10327
+ const actual = String(context || "").trim();
10328
+ const wanted = String(expected || "").trim();
10329
+ if (wanted && actual === wanted) return true;
10330
+ const lower = actual.toLowerCase();
10331
+ if (!lower.includes("vercel")) return false;
10332
+ if (!wanted) return lower.includes("preproduction") || lower.includes("defend-preproduction");
10333
+ const wantedLower = wanted.toLowerCase();
10334
+ return wantedLower.split(/\s+|–|-/).filter((part) => part && part !== "vercel").every((part) => lower.includes(part));
10335
+ }
10336
+ function selectFunctionalDeploymentUrl(status = {}, deployment = {}) {
10337
+ for (const candidate of [
10338
+ status?.environment_url,
10339
+ deployment?.environment_url,
10340
+ status?.target_url,
10341
+ deployment?.target_url
10342
+ ]) {
10343
+ const value = String(candidate || "").trim();
10344
+ if (!value) continue;
10345
+ try {
10346
+ const url = new URL(value);
10347
+ if (url.protocol === "http:" || url.protocol === "https:") return value;
10348
+ } catch {
10349
+ }
10350
+ }
10351
+ return "";
10352
+ }
10353
+ async function checkFunctionalDeploymentUrl(url, fetchImpl = globalThis.fetch) {
10354
+ const target = String(url || "").trim();
10355
+ if (!target) return { ok: false, reason: "url_missing" };
10356
+ if (typeof fetchImpl !== "function") return { ok: false, reason: "fetch_unavailable", url: target };
10357
+ const signal = typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(8e3) : void 0;
10358
+ const probe = async (method) => {
10359
+ const response = await fetchImpl(target, { method, redirect: "follow", signal });
10360
+ return {
10361
+ ok: response.status >= 200 && response.status < 400,
10362
+ status: response.status,
10363
+ statusText: response.statusText || "",
10364
+ url: response.url || target,
10365
+ method
10366
+ };
10367
+ };
10368
+ try {
10369
+ const head = await probe("HEAD");
10370
+ if (head.ok) return head;
10371
+ const get = await probe("GET");
10372
+ return get.ok ? get : { ...get, reason: "http_not_ok" };
10373
+ } catch (error) {
10374
+ try {
10375
+ const get = await probe("GET");
10376
+ return get.ok ? get : { ...get, reason: "http_not_ok" };
10377
+ } catch (secondError) {
10378
+ return { ok: false, reason: "request_failed", url: target, error: secondError.message || error.message };
10379
+ }
10380
+ }
10381
+ }
10169
10382
  function createGitHubApiClient(config = {}, fetchImpl = globalThis.fetch) {
10170
- const token = resolveSecretReference(config?.apiToken);
10383
+ const staticToken = resolveSecretReference(config?.apiToken);
10384
+ const appConfig = isPlainObject(config?.app) ? config.app : {};
10385
+ const appId = resolveSecretReference(appConfig.appId || appConfig.app_id || "");
10386
+ const installationId = resolveSecretReference(appConfig.installationId || appConfig.installation_id || "");
10387
+ const appEnabled = appConfig.enabled === true || Boolean(appId && installationId && (appConfig.privateKey || appConfig.privateKeyFile));
10388
+ const privateKey = resolveGitHubAppPrivateKey(appConfig);
10171
10389
  const owner = String(config?.owner || "").trim();
10172
10390
  const repo = String(config?.repo || "").trim();
10173
- const request = async (pathname) => {
10391
+ let installationToken = null;
10392
+ let installationTokenExpiresAt = 0;
10393
+ const requestJson = async (url, { method = "GET", headers = {}, body = void 0 } = {}) => {
10174
10394
  if (typeof fetchImpl !== "function") throw new Error("fetch is unavailable; inject a GitHub client for live PR lookup");
10175
- if (!owner || !repo) throw new Error("GitHub repository owner/repo is not configured");
10176
- const response = await fetchImpl(`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}${pathname}`, {
10395
+ const response = await fetchImpl(url, {
10396
+ method,
10177
10397
  headers: {
10178
10398
  Accept: "application/vnd.github+json",
10179
10399
  "X-GitHub-Api-Version": "2022-11-28",
10180
- ...token ? { Authorization: `Bearer ${token}` } : {}
10181
- }
10400
+ ...body === void 0 ? {} : { "Content-Type": "application/json" },
10401
+ ...headers
10402
+ },
10403
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
10182
10404
  });
10183
10405
  if (!response.ok) throw new Error(`GitHub API request failed: ${response.status}`);
10184
- return response.json();
10406
+ return response.status === 204 ? null : response.json();
10407
+ };
10408
+ const getAuthToken = async () => {
10409
+ if (!appEnabled) return staticToken || "";
10410
+ const nowMs = Date.now();
10411
+ if (installationToken && installationTokenExpiresAt - 6e4 > nowMs) return installationToken;
10412
+ const jwt = createGitHubAppJwt({ appId, privateKey });
10413
+ const installation = await requestJson(`https://api.github.com/app/installations/${encodeURIComponent(installationId)}/access_tokens`, {
10414
+ method: "POST",
10415
+ headers: { Authorization: `Bearer ${jwt}` }
10416
+ });
10417
+ installationToken = String(installation?.token || "").trim();
10418
+ installationTokenExpiresAt = Date.parse(installation?.expires_at || "") || nowMs + 3e6;
10419
+ if (!installationToken) throw new Error("GitHub App installation token response did not include a token");
10420
+ return installationToken;
10185
10421
  };
10422
+ const request = async (pathname, { method = "GET", body = void 0 } = {}) => {
10423
+ if (!owner || !repo) throw new Error("GitHub repository owner/repo is not configured");
10424
+ const token = await getAuthToken();
10425
+ return requestJson(`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}${pathname}`, {
10426
+ method,
10427
+ body,
10428
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
10429
+ });
10430
+ };
10431
+ const getRef = async (branch) => request(`/git/ref/heads/${encodeGitHubBranchRef(branch)}`);
10432
+ const getCommitObject = async (sha) => request(`/git/commits/${encodeURIComponent(sha)}`);
10433
+ const createBlob = async ({ content, encoding }) => request("/git/blobs", { method: "POST", body: { content, encoding } });
10434
+ const createTree = async ({ baseTree, tree }) => request("/git/trees", { method: "POST", body: { base_tree: baseTree, tree } });
10435
+ const createCommit = async ({ message, treeSha, parents }) => request("/git/commits", { method: "POST", body: { message, tree: treeSha, parents } });
10436
+ const updateRef = async ({ branch, sha, force = false }) => request(`/git/refs/heads/${encodeGitHubBranchRef(branch)}`, { method: "PATCH", body: { sha, force } });
10186
10437
  return {
10187
10438
  async getPullRequest(number) {
10188
10439
  return request(`/pulls/${encodeURIComponent(number)}`);
10440
+ },
10441
+ async createPullRequest({ title, head, base, body = "", draft = false, maintainerCanModify = true }) {
10442
+ return request("/pulls", {
10443
+ method: "POST",
10444
+ body: {
10445
+ title: String(title || ""),
10446
+ head: String(head || ""),
10447
+ base: String(base || ""),
10448
+ body: String(body || ""),
10449
+ draft: draft === true,
10450
+ maintainer_can_modify: maintainerCanModify !== false
10451
+ }
10452
+ });
10453
+ },
10454
+ async createIssueComment({ issueNumber, body }) {
10455
+ return request(`/issues/${encodeURIComponent(issueNumber)}/comments`, { method: "POST", body: { body: String(body || "") } });
10456
+ },
10457
+ async replyToReviewComment({ commentId, body }) {
10458
+ return request(`/pulls/comments/${encodeURIComponent(commentId)}/replies`, { method: "POST", body: { body: String(body || "") } });
10459
+ },
10460
+ async createPullRequestReview({ pullNumber, body, event = "COMMENT" }) {
10461
+ return request(`/pulls/${encodeURIComponent(pullNumber)}/reviews`, { method: "POST", body: { body: String(body || ""), event: String(event || "COMMENT").toUpperCase() } });
10462
+ },
10463
+ async mergePullRequest({ pullNumber, commitTitle = "", commitMessage = "", mergeMethod = "squash" }) {
10464
+ const body = {
10465
+ merge_method: String(mergeMethod || "squash")
10466
+ };
10467
+ if (commitTitle) body.commit_title = String(commitTitle);
10468
+ if (commitMessage) body.commit_message = String(commitMessage);
10469
+ return request(`/pulls/${encodeURIComponent(pullNumber)}/merge`, { method: "PUT", body });
10470
+ },
10471
+ async getCombinedStatus(ref) {
10472
+ return request(`/commits/${encodeURIComponent(String(ref || ""))}/status`);
10473
+ },
10474
+ async listDeployments({ sha = "", ref = "", environment = "", perPage = 30 } = {}) {
10475
+ return request(appendGitHubQuery("/deployments", {
10476
+ sha,
10477
+ ref,
10478
+ environment,
10479
+ per_page: perPage
10480
+ }));
10481
+ },
10482
+ async listDeploymentStatuses(deploymentId) {
10483
+ return request(`/deployments/${encodeURIComponent(deploymentId)}/statuses`);
10484
+ },
10485
+ async verifyVercelPullRequestDeployment({
10486
+ pullNumber,
10487
+ context = "Vercel \u2013 defend-preproduction",
10488
+ environment = "Preview \u2013 defend-preproduction",
10489
+ requireFunctionalUrl = true
10490
+ } = {}) {
10491
+ const pr = await this.getPullRequest(pullNumber);
10492
+ const headSha = String(pr?.head?.sha || "").trim();
10493
+ if (!headSha) return { ok: true, ready: false, reason: "pr_head_sha_missing", pull_request: { number: pullNumber } };
10494
+ const combined = await this.getCombinedStatus(headSha);
10495
+ const statuses = Array.isArray(combined?.statuses) ? combined.statuses : [];
10496
+ const matchingStatuses = statuses.filter((status) => matchesVercelStatusContext(status?.context, context));
10497
+ const selectedStatus = sortNewestFirst(matchingStatuses)[0] || null;
10498
+ if (!selectedStatus) {
10499
+ return {
10500
+ ok: true,
10501
+ ready: false,
10502
+ reason: "vercel_status_missing",
10503
+ pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
10504
+ required_context: context,
10505
+ combined_state: combined?.state || null
10506
+ };
10507
+ }
10508
+ if (String(selectedStatus.state || "").toLowerCase() !== "success") {
10509
+ return {
10510
+ ok: true,
10511
+ ready: false,
10512
+ reason: "vercel_status_not_success",
10513
+ pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
10514
+ required_context: context,
10515
+ status: selectedStatus,
10516
+ combined_state: combined?.state || null
10517
+ };
10518
+ }
10519
+ let deployments = await this.listDeployments({ sha: headSha, environment });
10520
+ if (!Array.isArray(deployments) || deployments.length === 0) deployments = await this.listDeployments({ sha: headSha });
10521
+ const selectedDeployment = sortNewestFirst(Array.isArray(deployments) ? deployments : []).find((deployment) => !environment || String(deployment?.environment || "") === environment) || sortNewestFirst(Array.isArray(deployments) ? deployments : [])[0] || null;
10522
+ if (!selectedDeployment?.id) {
10523
+ return {
10524
+ ok: true,
10525
+ ready: false,
10526
+ reason: "vercel_deployment_missing",
10527
+ pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
10528
+ required_environment: environment,
10529
+ status: selectedStatus
10530
+ };
10531
+ }
10532
+ const deploymentStatuses = await this.listDeploymentStatuses(selectedDeployment.id);
10533
+ const selectedDeploymentStatus = sortNewestFirst(Array.isArray(deploymentStatuses) ? deploymentStatuses : [])[0] || null;
10534
+ if (!selectedDeploymentStatus || String(selectedDeploymentStatus.state || "").toLowerCase() !== "success") {
10535
+ return {
10536
+ ok: true,
10537
+ ready: false,
10538
+ reason: "vercel_deployment_not_success",
10539
+ pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
10540
+ required_environment: environment,
10541
+ status: selectedStatus,
10542
+ deployment: selectedDeployment,
10543
+ deployment_status: selectedDeploymentStatus
10544
+ };
10545
+ }
10546
+ const url = selectFunctionalDeploymentUrl(selectedDeploymentStatus, selectedDeployment);
10547
+ const urlCheck = requireFunctionalUrl ? await checkFunctionalDeploymentUrl(url, fetchImpl) : { ok: Boolean(url), reason: url ? void 0 : "url_missing", url };
10548
+ if (requireFunctionalUrl && !urlCheck.ok) {
10549
+ return {
10550
+ ok: true,
10551
+ ready: false,
10552
+ reason: "vercel_url_not_functional",
10553
+ pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
10554
+ required_environment: environment,
10555
+ status: selectedStatus,
10556
+ deployment: selectedDeployment,
10557
+ deployment_status: selectedDeploymentStatus,
10558
+ url,
10559
+ url_check: urlCheck
10560
+ };
10561
+ }
10562
+ return {
10563
+ ok: true,
10564
+ ready: true,
10565
+ reason: "vercel_pr_deployment_ready",
10566
+ pull_request: { number: pr.number, url: pr.html_url, head_sha: headSha },
10567
+ required_context: context,
10568
+ required_environment: environment,
10569
+ status: selectedStatus,
10570
+ deployment: selectedDeployment,
10571
+ deployment_status: selectedDeploymentStatus,
10572
+ url,
10573
+ url_check: urlCheck
10574
+ };
10575
+ },
10576
+ async commitWorktree({ worktree, branch = "", message = "", runGitFn = runGit, syncLocal = false } = {}) {
10577
+ const directory = path6.resolve(String(worktree || ""));
10578
+ if (!directory || !fs5.existsSync(directory)) throw new Error("worktree does not exist");
10579
+ const targetBranch = String(branch || currentGitBranch(directory, runGitFn)).trim();
10580
+ if (!targetBranch || targetBranch === "HEAD") throw new Error("target branch is required for GitHub API commit");
10581
+ const commitMessage = String(message || "").trim();
10582
+ if (!commitMessage) throw new Error("commit message is required");
10583
+ const changedPaths = listWorktreeChangedPaths(directory, runGitFn);
10584
+ if (changedPaths.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
10585
+ const ref = await getRef(targetBranch);
10586
+ const headSha = ref?.object?.sha;
10587
+ if (!headSha) throw new Error(`GitHub ref for ${targetBranch} did not include a head sha`);
10588
+ const headCommit = await getCommitObject(headSha);
10589
+ const baseTree = headCommit?.tree?.sha;
10590
+ if (!baseTree) throw new Error(`GitHub commit ${headSha} did not include a tree sha`);
10591
+ const tree = [];
10592
+ for (const gitPath of changedPaths) {
10593
+ const entry = treeEntryForWorktreePath(directory, gitPath);
10594
+ if (!entry) continue;
10595
+ if (entry.deleted) {
10596
+ tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: null });
10597
+ continue;
10598
+ }
10599
+ const blob = await createBlob({ content: entry.content, encoding: entry.encoding });
10600
+ if (!blob?.sha) throw new Error(`GitHub blob creation failed for ${gitPath}`);
10601
+ tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: blob.sha });
10602
+ }
10603
+ if (tree.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
10604
+ const nextTree = await createTree({ baseTree, tree });
10605
+ const nextCommit = await createCommit({ message: commitMessage, treeSha: nextTree.sha, parents: [headSha] });
10606
+ if (!nextCommit?.sha) throw new Error("GitHub commit creation did not return a sha");
10607
+ const updatedRef = await updateRef({ branch: targetBranch, sha: nextCommit.sha, force: false });
10608
+ if (syncLocal) {
10609
+ runGitFn(directory, ["fetch", "origin", targetBranch]);
10610
+ runGitFn(directory, ["reset", "--hard", "FETCH_HEAD"]);
10611
+ }
10612
+ return {
10613
+ ok: true,
10614
+ action: "committed",
10615
+ branch: targetBranch,
10616
+ before: headSha,
10617
+ after: nextCommit.sha,
10618
+ changedPaths,
10619
+ treeEntries: tree.length,
10620
+ verification: nextCommit.verification || null,
10621
+ ref: updatedRef
10622
+ };
10623
+ },
10624
+ async authMode() {
10625
+ if (appEnabled) return { mode: "github_app", appId, installationId };
10626
+ if (staticToken) return { mode: "token" };
10627
+ return { mode: "anonymous" };
10189
10628
  }
10190
10629
  };
10191
10630
  }
@@ -11765,7 +12204,22 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
11765
12204
  const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
11766
12205
  let taskRoute;
11767
12206
  try {
11768
- taskRoute = await ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true, opencodeBaseUrl: config.opencode?.baseUrl, opencodeBaseUrlConfigured: config.opencode?.baseUrlConfigured === true, openchamberBaseUrl: config.openchamber?.baseUrl, openchamberBaseUrlConfigured: config.openchamber?.baseUrlConfigured === true, clickupClient, webhookWorktree: worktree });
12207
+ taskRoute = await ensureTaskWorktree({
12208
+ baseWorktree: config.basePath,
12209
+ taskId,
12210
+ taskType,
12211
+ parentTaskId,
12212
+ subtaskId,
12213
+ existingMetadata: metadata,
12214
+ allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true,
12215
+ opencodeBaseUrl: config.opencode?.baseUrl,
12216
+ opencodeBaseUrlConfigured: config.opencode?.baseUrlConfigured === true,
12217
+ openchamberBaseUrl: config.openchamber?.baseUrl,
12218
+ openchamberBaseUrlConfigured: config.openchamber?.baseUrlConfigured === true,
12219
+ clickupClient,
12220
+ webhookWorktree: worktree,
12221
+ gitIdentity: config.github?.committer
12222
+ });
11769
12223
  } catch (error) {
11770
12224
  const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
11771
12225
  appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
@@ -13637,6 +14091,14 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
13637
14091
  }
13638
14092
  });
13639
14093
  };
14094
+ const requireGitHubAppClient = async () => {
14095
+ if (!runtimeGitHubClient?.authMode) return { ok: false, error: "github_client_unavailable" };
14096
+ const auth = await runtimeGitHubClient.authMode();
14097
+ if (auth.mode !== "github_app") {
14098
+ return { ok: false, error: "github_app_auth_required", auth };
14099
+ }
14100
+ return { ok: true, auth };
14101
+ };
13640
14102
  const tools = {
13641
14103
  optima_init: tool({
13642
14104
  description: "Initialize the Optima workflow and CodeMap in the current repository",
@@ -13890,6 +14352,177 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
13890
14352
  return JSON.stringify(result, null, 2);
13891
14353
  }
13892
14354
  }),
14355
+ optima_github_auth_mode: tool({
14356
+ description: "Report whether Optima GitHub operations use anonymous, user token, or GitHub App installation authentication",
14357
+ args: {},
14358
+ async execute() {
14359
+ try {
14360
+ if (!runtimeGitHubClient?.authMode) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14361
+ return JSON.stringify({ ok: true, ...await runtimeGitHubClient.authMode() }, null, 2);
14362
+ } catch (error) {
14363
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14364
+ }
14365
+ }
14366
+ }),
14367
+ optima_github_create_pr: tool({
14368
+ description: "Create a GitHub PR through the Optima GitHub App identity so human owners can approve it",
14369
+ args: {
14370
+ title: tool.schema.string().describe("Pull request title"),
14371
+ head: tool.schema.string().describe("Source branch"),
14372
+ base: tool.schema.string().describe("Target branch"),
14373
+ body: tool.schema.string().describe("Pull request body"),
14374
+ draft: tool.schema.string().describe("Set to 'true' to create a draft PR")
14375
+ },
14376
+ async execute(args) {
14377
+ try {
14378
+ const auth = await requireGitHubAppClient();
14379
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14380
+ if (!runtimeGitHubClient?.createPullRequest) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14381
+ const result = await runtimeGitHubClient.createPullRequest({
14382
+ title: args.title,
14383
+ head: args.head,
14384
+ base: args.base,
14385
+ body: args.body,
14386
+ draft: String(args.draft || "").toLowerCase() === "true"
14387
+ });
14388
+ return JSON.stringify({ ok: true, pull_request: result }, null, 2);
14389
+ } catch (error) {
14390
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14391
+ }
14392
+ }
14393
+ }),
14394
+ optima_github_comment_pr: tool({
14395
+ description: "Post a PR conversation comment through the Optima GitHub App identity",
14396
+ args: {
14397
+ pr_number: tool.schema.number().describe("Pull request number"),
14398
+ body: tool.schema.string().describe("Markdown comment body")
14399
+ },
14400
+ async execute(args) {
14401
+ try {
14402
+ const auth = await requireGitHubAppClient();
14403
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14404
+ if (!runtimeGitHubClient?.createIssueComment) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14405
+ const result = await runtimeGitHubClient.createIssueComment({ issueNumber: args.pr_number, body: args.body });
14406
+ return JSON.stringify({ ok: true, comment: result }, null, 2);
14407
+ } catch (error) {
14408
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14409
+ }
14410
+ }
14411
+ }),
14412
+ optima_github_reply_review_comment: tool({
14413
+ description: "Reply to a GitHub PR review comment through the Optima GitHub App identity",
14414
+ args: {
14415
+ comment_id: tool.schema.number().describe("GitHub review comment id to reply to"),
14416
+ body: tool.schema.string().describe("Markdown reply body")
14417
+ },
14418
+ async execute(args) {
14419
+ try {
14420
+ const auth = await requireGitHubAppClient();
14421
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14422
+ if (!runtimeGitHubClient?.replyToReviewComment) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14423
+ const result = await runtimeGitHubClient.replyToReviewComment({ commentId: args.comment_id, body: args.body });
14424
+ return JSON.stringify({ ok: true, comment: result }, null, 2);
14425
+ } catch (error) {
14426
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14427
+ }
14428
+ }
14429
+ }),
14430
+ optima_github_review_pr: tool({
14431
+ description: "Create a GitHub PR review through the Optima GitHub App identity",
14432
+ args: {
14433
+ pr_number: tool.schema.number().describe("Pull request number"),
14434
+ body: tool.schema.string().describe("Markdown review body"),
14435
+ event: tool.schema.string().describe("Review event: COMMENT, APPROVE, or REQUEST_CHANGES")
14436
+ },
14437
+ async execute(args) {
14438
+ try {
14439
+ const auth = await requireGitHubAppClient();
14440
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14441
+ if (!runtimeGitHubClient?.createPullRequestReview) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14442
+ const result = await runtimeGitHubClient.createPullRequestReview({ pullNumber: args.pr_number, body: args.body, event: args.event || "COMMENT" });
14443
+ return JSON.stringify({ ok: true, review: result }, null, 2);
14444
+ } catch (error) {
14445
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14446
+ }
14447
+ }
14448
+ }),
14449
+ optima_github_verify_vercel_pr: tool({
14450
+ description: "Verify that a PR head commit has a successful Vercel preproduction deployment and a functional deployment URL before validation/handoff",
14451
+ args: {
14452
+ pr_number: tool.schema.number().describe("Pull request number"),
14453
+ context: tool.schema.string().describe("Required GitHub status context; defaults to 'Vercel \u2013 defend-preproduction'"),
14454
+ environment: tool.schema.string().describe("Required GitHub deployment environment; defaults to 'Preview \u2013 defend-preproduction'"),
14455
+ require_functional_url: tool.schema.string().describe("Set to 'false' to skip HTTP probing of the deployment URL")
14456
+ },
14457
+ async execute(args) {
14458
+ try {
14459
+ const auth = await requireGitHubAppClient();
14460
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14461
+ if (!runtimeGitHubClient?.verifyVercelPullRequestDeployment) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14462
+ const result = await runtimeGitHubClient.verifyVercelPullRequestDeployment({
14463
+ pullNumber: args.pr_number,
14464
+ context: args.context || "Vercel \u2013 defend-preproduction",
14465
+ environment: args.environment || "Preview \u2013 defend-preproduction",
14466
+ requireFunctionalUrl: String(args.require_functional_url || "true").toLowerCase() !== "false"
14467
+ });
14468
+ return JSON.stringify(result, null, 2);
14469
+ } catch (error) {
14470
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14471
+ }
14472
+ }
14473
+ }),
14474
+ optima_github_merge_pr: tool({
14475
+ description: "Merge a GitHub PR through the Optima GitHub App identity after required human approval gates pass",
14476
+ args: {
14477
+ pr_number: tool.schema.number().describe("Pull request number"),
14478
+ commit_title: tool.schema.string().describe("Optional merge commit title"),
14479
+ commit_message: tool.schema.string().describe("Optional merge commit message"),
14480
+ merge_method: tool.schema.string().describe("merge, squash, or rebase; defaults to squash")
14481
+ },
14482
+ async execute(args) {
14483
+ try {
14484
+ const auth = await requireGitHubAppClient();
14485
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14486
+ if (!runtimeGitHubClient?.mergePullRequest) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14487
+ const result = await runtimeGitHubClient.mergePullRequest({
14488
+ pullNumber: args.pr_number,
14489
+ commitTitle: args.commit_title,
14490
+ commitMessage: args.commit_message,
14491
+ mergeMethod: args.merge_method || "squash"
14492
+ });
14493
+ return JSON.stringify({ ok: true, merge: result }, null, 2);
14494
+ } catch (error) {
14495
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14496
+ }
14497
+ }
14498
+ }),
14499
+ optima_github_commit_worktree: tool({
14500
+ description: "Commit current worktree changes directly through the Optima GitHub App API so commits are authored by the App/bot and can be GitHub-verified",
14501
+ args: {
14502
+ directory: tool.schema.string().describe("Task worktree directory containing the file changes"),
14503
+ branch: tool.schema.string().describe("Target branch to update; defaults to the current local branch"),
14504
+ message: tool.schema.string().describe("Commit message"),
14505
+ sync_local: tool.schema.string().describe("Set to 'true' to fetch/reset the local worktree to the API-created commit after success")
14506
+ },
14507
+ async execute(args, context) {
14508
+ try {
14509
+ const auth = await requireGitHubAppClient();
14510
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14511
+ if (!runtimeGitHubClient?.commitWorktree) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14512
+ const directory = resolveSessionToolDirectory({ requestedDirectory: args.directory, context, pluginWorktree: worktree, clickUpWebhookValidation });
14513
+ if (!directory.ok) return JSON.stringify({ ok: false, error: directory.error }, null, 2);
14514
+ const result = await runtimeGitHubClient.commitWorktree({
14515
+ worktree: directory.directory,
14516
+ branch: args.branch,
14517
+ message: args.message,
14518
+ syncLocal: String(args.sync_local || "").toLowerCase() === "true"
14519
+ });
14520
+ return JSON.stringify(result, null, 2);
14521
+ } catch (error) {
14522
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14523
+ }
14524
+ }
14525
+ }),
13893
14526
  optima_validate: tool({
13894
14527
  description: "Validate Optima workflow artifacts and CodeMap integrity",
13895
14528
  args: {},