@defend-tech/opencode-optima 0.1.74 → 0.1.75

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,209 @@ 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
+ }
10169
10306
  function createGitHubApiClient(config = {}, fetchImpl = globalThis.fetch) {
10170
- const token = resolveSecretReference(config?.apiToken);
10307
+ const staticToken = resolveSecretReference(config?.apiToken);
10308
+ const appConfig = isPlainObject(config?.app) ? config.app : {};
10309
+ const appEnabled = appConfig.enabled === true || Boolean(appConfig.appId && appConfig.installationId && (appConfig.privateKey || appConfig.privateKeyFile));
10310
+ const privateKey = resolveGitHubAppPrivateKey(appConfig);
10171
10311
  const owner = String(config?.owner || "").trim();
10172
10312
  const repo = String(config?.repo || "").trim();
10173
- const request = async (pathname) => {
10313
+ let installationToken = null;
10314
+ let installationTokenExpiresAt = 0;
10315
+ const requestJson = async (url, { method = "GET", headers = {}, body = void 0 } = {}) => {
10174
10316
  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}`, {
10317
+ const response = await fetchImpl(url, {
10318
+ method,
10177
10319
  headers: {
10178
10320
  Accept: "application/vnd.github+json",
10179
10321
  "X-GitHub-Api-Version": "2022-11-28",
10180
- ...token ? { Authorization: `Bearer ${token}` } : {}
10181
- }
10322
+ ...body === void 0 ? {} : { "Content-Type": "application/json" },
10323
+ ...headers
10324
+ },
10325
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
10182
10326
  });
10183
10327
  if (!response.ok) throw new Error(`GitHub API request failed: ${response.status}`);
10184
- return response.json();
10328
+ return response.status === 204 ? null : response.json();
10329
+ };
10330
+ const getAuthToken = async () => {
10331
+ if (!appEnabled) return staticToken || "";
10332
+ const nowMs = Date.now();
10333
+ if (installationToken && installationTokenExpiresAt - 6e4 > nowMs) return installationToken;
10334
+ const jwt = createGitHubAppJwt({ appId: appConfig.appId, privateKey });
10335
+ const installation = await requestJson(`https://api.github.com/app/installations/${encodeURIComponent(appConfig.installationId)}/access_tokens`, {
10336
+ method: "POST",
10337
+ headers: { Authorization: `Bearer ${jwt}` }
10338
+ });
10339
+ installationToken = String(installation?.token || "").trim();
10340
+ installationTokenExpiresAt = Date.parse(installation?.expires_at || "") || nowMs + 3e6;
10341
+ if (!installationToken) throw new Error("GitHub App installation token response did not include a token");
10342
+ return installationToken;
10185
10343
  };
10344
+ const request = async (pathname, { method = "GET", body = void 0 } = {}) => {
10345
+ if (!owner || !repo) throw new Error("GitHub repository owner/repo is not configured");
10346
+ const token = await getAuthToken();
10347
+ return requestJson(`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}${pathname}`, {
10348
+ method,
10349
+ body,
10350
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
10351
+ });
10352
+ };
10353
+ const getRef = async (branch) => request(`/git/ref/heads/${encodeGitHubBranchRef(branch)}`);
10354
+ const getCommitObject = async (sha) => request(`/git/commits/${encodeURIComponent(sha)}`);
10355
+ const createBlob = async ({ content, encoding }) => request("/git/blobs", { method: "POST", body: { content, encoding } });
10356
+ const createTree = async ({ baseTree, tree }) => request("/git/trees", { method: "POST", body: { base_tree: baseTree, tree } });
10357
+ const createCommit = async ({ message, treeSha, parents }) => request("/git/commits", { method: "POST", body: { message, tree: treeSha, parents } });
10358
+ const updateRef = async ({ branch, sha, force = false }) => request(`/git/refs/heads/${encodeGitHubBranchRef(branch)}`, { method: "PATCH", body: { sha, force } });
10186
10359
  return {
10187
10360
  async getPullRequest(number) {
10188
10361
  return request(`/pulls/${encodeURIComponent(number)}`);
10362
+ },
10363
+ async createPullRequest({ title, head, base, body = "", draft = false, maintainerCanModify = true }) {
10364
+ return request("/pulls", {
10365
+ method: "POST",
10366
+ body: {
10367
+ title: String(title || ""),
10368
+ head: String(head || ""),
10369
+ base: String(base || ""),
10370
+ body: String(body || ""),
10371
+ draft: draft === true,
10372
+ maintainer_can_modify: maintainerCanModify !== false
10373
+ }
10374
+ });
10375
+ },
10376
+ async createIssueComment({ issueNumber, body }) {
10377
+ return request(`/issues/${encodeURIComponent(issueNumber)}/comments`, { method: "POST", body: { body: String(body || "") } });
10378
+ },
10379
+ async replyToReviewComment({ commentId, body }) {
10380
+ return request(`/pulls/comments/${encodeURIComponent(commentId)}/replies`, { method: "POST", body: { body: String(body || "") } });
10381
+ },
10382
+ async createPullRequestReview({ pullNumber, body, event = "COMMENT" }) {
10383
+ return request(`/pulls/${encodeURIComponent(pullNumber)}/reviews`, { method: "POST", body: { body: String(body || ""), event: String(event || "COMMENT").toUpperCase() } });
10384
+ },
10385
+ async mergePullRequest({ pullNumber, commitTitle = "", commitMessage = "", mergeMethod = "squash" }) {
10386
+ const body = {
10387
+ merge_method: String(mergeMethod || "squash")
10388
+ };
10389
+ if (commitTitle) body.commit_title = String(commitTitle);
10390
+ if (commitMessage) body.commit_message = String(commitMessage);
10391
+ return request(`/pulls/${encodeURIComponent(pullNumber)}/merge`, { method: "PUT", body });
10392
+ },
10393
+ async commitWorktree({ worktree, branch = "", message = "", runGitFn = runGit, syncLocal = false } = {}) {
10394
+ const directory = path6.resolve(String(worktree || ""));
10395
+ if (!directory || !fs5.existsSync(directory)) throw new Error("worktree does not exist");
10396
+ const targetBranch = String(branch || currentGitBranch(directory, runGitFn)).trim();
10397
+ if (!targetBranch || targetBranch === "HEAD") throw new Error("target branch is required for GitHub API commit");
10398
+ const commitMessage = String(message || "").trim();
10399
+ if (!commitMessage) throw new Error("commit message is required");
10400
+ const changedPaths = listWorktreeChangedPaths(directory, runGitFn);
10401
+ if (changedPaths.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
10402
+ const ref = await getRef(targetBranch);
10403
+ const headSha = ref?.object?.sha;
10404
+ if (!headSha) throw new Error(`GitHub ref for ${targetBranch} did not include a head sha`);
10405
+ const headCommit = await getCommitObject(headSha);
10406
+ const baseTree = headCommit?.tree?.sha;
10407
+ if (!baseTree) throw new Error(`GitHub commit ${headSha} did not include a tree sha`);
10408
+ const tree = [];
10409
+ for (const gitPath of changedPaths) {
10410
+ const entry = treeEntryForWorktreePath(directory, gitPath);
10411
+ if (!entry) continue;
10412
+ if (entry.deleted) {
10413
+ tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: null });
10414
+ continue;
10415
+ }
10416
+ const blob = await createBlob({ content: entry.content, encoding: entry.encoding });
10417
+ if (!blob?.sha) throw new Error(`GitHub blob creation failed for ${gitPath}`);
10418
+ tree.push({ path: entry.path, mode: entry.mode, type: entry.type, sha: blob.sha });
10419
+ }
10420
+ if (tree.length === 0) return { ok: true, action: "no_changes", branch: targetBranch, changedPaths: [] };
10421
+ const nextTree = await createTree({ baseTree, tree });
10422
+ const nextCommit = await createCommit({ message: commitMessage, treeSha: nextTree.sha, parents: [headSha] });
10423
+ if (!nextCommit?.sha) throw new Error("GitHub commit creation did not return a sha");
10424
+ const updatedRef = await updateRef({ branch: targetBranch, sha: nextCommit.sha, force: false });
10425
+ if (syncLocal) {
10426
+ runGitFn(directory, ["fetch", "origin", targetBranch]);
10427
+ runGitFn(directory, ["reset", "--hard", "FETCH_HEAD"]);
10428
+ }
10429
+ return {
10430
+ ok: true,
10431
+ action: "committed",
10432
+ branch: targetBranch,
10433
+ before: headSha,
10434
+ after: nextCommit.sha,
10435
+ changedPaths,
10436
+ treeEntries: tree.length,
10437
+ verification: nextCommit.verification || null,
10438
+ ref: updatedRef
10439
+ };
10440
+ },
10441
+ async authMode() {
10442
+ if (appEnabled) return { mode: "github_app", appId: String(appConfig.appId || ""), installationId: String(appConfig.installationId || "") };
10443
+ if (staticToken) return { mode: "token" };
10444
+ return { mode: "anonymous" };
10189
10445
  }
10190
10446
  };
10191
10447
  }
@@ -11765,7 +12021,22 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
11765
12021
  const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
11766
12022
  let taskRoute;
11767
12023
  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 });
12024
+ taskRoute = await ensureTaskWorktree({
12025
+ baseWorktree: config.basePath,
12026
+ taskId,
12027
+ taskType,
12028
+ parentTaskId,
12029
+ subtaskId,
12030
+ existingMetadata: metadata,
12031
+ allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true,
12032
+ opencodeBaseUrl: config.opencode?.baseUrl,
12033
+ opencodeBaseUrlConfigured: config.opencode?.baseUrlConfigured === true,
12034
+ openchamberBaseUrl: config.openchamber?.baseUrl,
12035
+ openchamberBaseUrlConfigured: config.openchamber?.baseUrlConfigured === true,
12036
+ clickupClient,
12037
+ webhookWorktree: worktree,
12038
+ gitIdentity: config.github?.committer
12039
+ });
11769
12040
  } catch (error) {
11770
12041
  const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
11771
12042
  appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
@@ -13637,6 +13908,14 @@ async function OptimaPlugin(input = {}, pluginOptions = {}) {
13637
13908
  }
13638
13909
  });
13639
13910
  };
13911
+ const requireGitHubAppClient = async () => {
13912
+ if (!runtimeGitHubClient?.authMode) return { ok: false, error: "github_client_unavailable" };
13913
+ const auth = await runtimeGitHubClient.authMode();
13914
+ if (auth.mode !== "github_app") {
13915
+ return { ok: false, error: "github_app_auth_required", auth };
13916
+ }
13917
+ return { ok: true, auth };
13918
+ };
13640
13919
  const tools = {
13641
13920
  optima_init: tool({
13642
13921
  description: "Initialize the Optima workflow and CodeMap in the current repository",
@@ -13890,6 +14169,152 @@ Restart or reload OpenCode manually if the newly scaffolded config or agents are
13890
14169
  return JSON.stringify(result, null, 2);
13891
14170
  }
13892
14171
  }),
14172
+ optima_github_auth_mode: tool({
14173
+ description: "Report whether Optima GitHub operations use anonymous, user token, or GitHub App installation authentication",
14174
+ args: {},
14175
+ async execute() {
14176
+ try {
14177
+ if (!runtimeGitHubClient?.authMode) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14178
+ return JSON.stringify({ ok: true, ...await runtimeGitHubClient.authMode() }, null, 2);
14179
+ } catch (error) {
14180
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14181
+ }
14182
+ }
14183
+ }),
14184
+ optima_github_create_pr: tool({
14185
+ description: "Create a GitHub PR through the Optima GitHub App identity so human owners can approve it",
14186
+ args: {
14187
+ title: tool.schema.string().describe("Pull request title"),
14188
+ head: tool.schema.string().describe("Source branch"),
14189
+ base: tool.schema.string().describe("Target branch"),
14190
+ body: tool.schema.string().describe("Pull request body"),
14191
+ draft: tool.schema.string().describe("Set to 'true' to create a draft PR")
14192
+ },
14193
+ async execute(args) {
14194
+ try {
14195
+ const auth = await requireGitHubAppClient();
14196
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14197
+ if (!runtimeGitHubClient?.createPullRequest) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14198
+ const result = await runtimeGitHubClient.createPullRequest({
14199
+ title: args.title,
14200
+ head: args.head,
14201
+ base: args.base,
14202
+ body: args.body,
14203
+ draft: String(args.draft || "").toLowerCase() === "true"
14204
+ });
14205
+ return JSON.stringify({ ok: true, pull_request: result }, null, 2);
14206
+ } catch (error) {
14207
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14208
+ }
14209
+ }
14210
+ }),
14211
+ optima_github_comment_pr: tool({
14212
+ description: "Post a PR conversation comment through the Optima GitHub App identity",
14213
+ args: {
14214
+ pr_number: tool.schema.number().describe("Pull request number"),
14215
+ body: tool.schema.string().describe("Markdown comment body")
14216
+ },
14217
+ async execute(args) {
14218
+ try {
14219
+ const auth = await requireGitHubAppClient();
14220
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14221
+ if (!runtimeGitHubClient?.createIssueComment) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14222
+ const result = await runtimeGitHubClient.createIssueComment({ issueNumber: args.pr_number, body: args.body });
14223
+ return JSON.stringify({ ok: true, comment: result }, null, 2);
14224
+ } catch (error) {
14225
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14226
+ }
14227
+ }
14228
+ }),
14229
+ optima_github_reply_review_comment: tool({
14230
+ description: "Reply to a GitHub PR review comment through the Optima GitHub App identity",
14231
+ args: {
14232
+ comment_id: tool.schema.number().describe("GitHub review comment id to reply to"),
14233
+ body: tool.schema.string().describe("Markdown reply body")
14234
+ },
14235
+ async execute(args) {
14236
+ try {
14237
+ const auth = await requireGitHubAppClient();
14238
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14239
+ if (!runtimeGitHubClient?.replyToReviewComment) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14240
+ const result = await runtimeGitHubClient.replyToReviewComment({ commentId: args.comment_id, body: args.body });
14241
+ return JSON.stringify({ ok: true, comment: result }, null, 2);
14242
+ } catch (error) {
14243
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14244
+ }
14245
+ }
14246
+ }),
14247
+ optima_github_review_pr: tool({
14248
+ description: "Create a GitHub PR review through the Optima GitHub App identity",
14249
+ args: {
14250
+ pr_number: tool.schema.number().describe("Pull request number"),
14251
+ body: tool.schema.string().describe("Markdown review body"),
14252
+ event: tool.schema.string().describe("Review event: COMMENT, APPROVE, or REQUEST_CHANGES")
14253
+ },
14254
+ async execute(args) {
14255
+ try {
14256
+ const auth = await requireGitHubAppClient();
14257
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14258
+ if (!runtimeGitHubClient?.createPullRequestReview) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14259
+ const result = await runtimeGitHubClient.createPullRequestReview({ pullNumber: args.pr_number, body: args.body, event: args.event || "COMMENT" });
14260
+ return JSON.stringify({ ok: true, review: result }, null, 2);
14261
+ } catch (error) {
14262
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14263
+ }
14264
+ }
14265
+ }),
14266
+ optima_github_merge_pr: tool({
14267
+ description: "Merge a GitHub PR through the Optima GitHub App identity after required human approval gates pass",
14268
+ args: {
14269
+ pr_number: tool.schema.number().describe("Pull request number"),
14270
+ commit_title: tool.schema.string().describe("Optional merge commit title"),
14271
+ commit_message: tool.schema.string().describe("Optional merge commit message"),
14272
+ merge_method: tool.schema.string().describe("merge, squash, or rebase; defaults to squash")
14273
+ },
14274
+ async execute(args) {
14275
+ try {
14276
+ const auth = await requireGitHubAppClient();
14277
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14278
+ if (!runtimeGitHubClient?.mergePullRequest) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14279
+ const result = await runtimeGitHubClient.mergePullRequest({
14280
+ pullNumber: args.pr_number,
14281
+ commitTitle: args.commit_title,
14282
+ commitMessage: args.commit_message,
14283
+ mergeMethod: args.merge_method || "squash"
14284
+ });
14285
+ return JSON.stringify({ ok: true, merge: result }, null, 2);
14286
+ } catch (error) {
14287
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14288
+ }
14289
+ }
14290
+ }),
14291
+ optima_github_commit_worktree: tool({
14292
+ 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",
14293
+ args: {
14294
+ directory: tool.schema.string().describe("Task worktree directory containing the file changes"),
14295
+ branch: tool.schema.string().describe("Target branch to update; defaults to the current local branch"),
14296
+ message: tool.schema.string().describe("Commit message"),
14297
+ sync_local: tool.schema.string().describe("Set to 'true' to fetch/reset the local worktree to the API-created commit after success")
14298
+ },
14299
+ async execute(args, context) {
14300
+ try {
14301
+ const auth = await requireGitHubAppClient();
14302
+ if (!auth.ok) return JSON.stringify(auth, null, 2);
14303
+ if (!runtimeGitHubClient?.commitWorktree) return JSON.stringify({ ok: false, error: "github_client_unavailable" }, null, 2);
14304
+ const directory = resolveSessionToolDirectory({ requestedDirectory: args.directory, context, pluginWorktree: worktree, clickUpWebhookValidation });
14305
+ if (!directory.ok) return JSON.stringify({ ok: false, error: directory.error }, null, 2);
14306
+ const result = await runtimeGitHubClient.commitWorktree({
14307
+ worktree: directory.directory,
14308
+ branch: args.branch,
14309
+ message: args.message,
14310
+ syncLocal: String(args.sync_local || "").toLowerCase() === "true"
14311
+ });
14312
+ return JSON.stringify(result, null, 2);
14313
+ } catch (error) {
14314
+ return JSON.stringify({ ok: false, error: error.message }, null, 2);
14315
+ }
14316
+ }
14317
+ }),
13893
14318
  optima_validate: tool({
13894
14319
  description: "Validate Optima workflow artifacts and CodeMap integrity",
13895
14320
  args: {},
@@ -26,6 +26,7 @@
26
26
  - Human approval assignment is prohibited except for the strict allowlist: parent `plan` with clear questions already posted in ClickUp comments; `in progress` blocked by missing credentials, permissions, external tools, or access; or parent `validation` with a functional preview URL such as `https://<taskid>-preview.defend.tech`. Do not assign `CTO`/`PO` for generic handoff, routine validation, cleanup, subtask planning/validation, or partial-phase stops.
27
27
  - Store ClickUp `agent_metadata` JSON with session IDs per agent/type/task/subtask; keep `Definition` as the plan contract and final Documentation as delivered behavior docs.
28
28
  - Validation is not complete until the model leaves the current GitHub PR link visible in ClickUp with source branch, target branch, and validation owner; Optima runtime must still limit its own ClickUp writes to metadata.
29
+ - PR creation, PR comments, reviews, merges, and commits must use the Optima GitHub App/API identity, not a human token. Final commits for ClickUp/GitHub delivery are created with `optima_github_commit_worktree`; human approval must not be requested until GitHub reports those commits as Verified.
29
30
  - `workflow_product_manager` is registered only when explicit ClickUp webhook mode is configured and the local webhook subscription state is active/valid.
30
31
  - Webhook mode is opt-in: Optima validates signed `X-Signature` HMAC SHA-256 ClickUp requests, routes status/assignee events only for Product Manager-assigned non-terminal tasks, routes comments only when they mention `@Defend Tech Product Manager`, and stores new `ses_...` ids in ClickUp `agent_metadata`. Runtime/process failures stay in local logs and must not create ClickUp comments or tags.
31
32
  - The same gated in-process listener can also accept signed GitHub `X-Hub-Signature-256` PR/review/comment events at `/optima/github/webhook` when `clickup.github.webhook` is enabled. GitHub events resolve the ClickUp task from the PR source branch, update only `agent_metadata.task.github`, and steer the existing `workflow_product_manager` session; they do not create replacement sessions or ClickUp runtime comments.
@@ -19,6 +19,7 @@
19
19
  - Git rules: principal workspace stays on `dev`, never `main`; parent branches use `<type>/<parent-id>`; subtask branches use non-nested `<type>/<parent-id>-subtask-<subtask-id>` and pending subtasks use `<type>/<parent-id>-pending-<title-slug>`; parent task pulls remote once at start; subtasks start from and PR to the parent local branch, bootstrapping the parent from `dev`/`origin/dev` first when missing; PoC branches stay `poc/<clickup-task-id>`; parents PR to `dev`, releases PR `dev` -> `main`; failed/conflicted subtask or parent merges return the affected item to `in progress` for the coding owner; no direct `main` pushes.
20
20
  - Store `agent_metadata` session JSON; `Definition` is the plan contract, final Documentation is delivered behavior docs.
21
21
  - Validation requires a model-owned ClickUp status comment with the current GitHub PR link, source branch, target branch, and validation owner; Optima runtime itself writes only metadata/logs.
22
+ - PRs, GitHub comments/reviews/merges, and final commits use Optima GitHub App/API identity; create final commits with `optima_github_commit_worktree` and require GitHub `Verified` before human approval.
22
23
  - `workflow_product_manager` is registered only when opt-in ClickUp webhook mode is complete and active/valid.
23
24
  - Webhook mode validates ClickUp `X-Signature` HMAC SHA-256, routes only PM-assigned non-terminal status/assignee events, routes comments only on `@Defend Tech Product Manager`, writes new `ses_...` ids to `agent_metadata`, and keeps runtime/process failures in local logs, never ClickUp comments/tags.
24
25
  - The listener is gated in-process local runtime; production needs a stable public URL/tunnel and ignored `.optima/.config/runtime/` webhook state. It can also accept GitHub `X-Hub-Signature-256` PR/review/comment webhooks at `/optima/github/webhook`; these update only `agent_metadata.task.github` and steer the existing WPM session resolved from the PR source branch.
@@ -68,6 +68,7 @@ For ClickUp-first delivery, Validation is a GitHub PR state, not a comment-only
68
68
 
69
69
  - Subtasks open/update a PR from the subtask branch into the parent task branch before entering Validation.
70
70
  - Parent tasks open/update a PR from the task branch into `dev` before entering Validation.
71
+ - Final commits must be created with `optima_github_commit_worktree`, not local `git commit`, so GitHub attributes them to the Optima App/bot and can mark them Verified. If GitHub does not show the commits as Verified, do not request human approval.
71
72
  - The model must leave the current PR link visible in ClickUp when it moves a task/subtask to Validation, including source/target branch and validation owner. Optima runtime still writes only metadata/logs; the ClickUp comment is model-owned work status.
72
73
  - GitHub review/comment webhooks wake the workflow owner. The agent replies in GitHub; if a comment requires a change, it moves ClickUp back to `in progress`, fixes/pushes the same branch, returns ClickUp to `validation`, updates the PR, and replies again with the result.
73
74
  - The configured final approver/CTO approving the parent PR is the merge trigger. After merge to `dev`, Vercel preproduction must deploy automatically and pass a small smoke/regression check before cleanup and ClickUp `completed`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.74",
3
+ "version": "0.1.75",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"