@defend-tech/opencode-optima 0.1.24 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/{agents → assets/agents}/codemap.yml +1 -1
- package/{policies → assets/policies}/README.md +2 -2
- package/{policies → assets/policies}/codemap.yml +1 -1
- package/dist/index.js +91 -7
- package/dist/sanitize_cli.js +91 -7
- package/docs/setup/CONFIGURATION.md +1 -1
- package/package.json +2 -3
- /package/{agents → assets/agents}/business_analyst.md +0 -0
- /package/{agents → assets/agents}/developer.md +0 -0
- /package/{agents → assets/agents}/ops_product_manager.md +0 -0
- /package/{agents → assets/agents}/product_manager.md +0 -0
- /package/{agents → assets/agents}/qa_engineer.md +0 -0
- /package/{agents → assets/agents}/tech_lead.md +0 -0
- /package/{agents → assets/agents}/technical_architect.md +0 -0
- /package/{agents → assets/agents}/ui_ux_designer.md +0 -0
- /package/{agents → assets/agents}/workflow_product_manager.md +0 -0
- /package/{agents → assets/agents}/workflow_runner.md +0 -0
- /package/{policies → assets/policies}/definition-of-done.md +0 -0
- /package/{policies → assets/policies}/definition-of-ready.md +0 -0
- /package/{policies → assets/policies}/development-guidelines.md +0 -0
- /package/{policies → assets/policies}/documentation-guidelines.md +0 -0
- /package/{policies → assets/policies}/git-commit-messaging.md +0 -0
- /package/{policies → assets/policies}/product-guidelines.md +0 -0
- /package/{policies → assets/policies}/testing-guidelines.md +0 -0
- /package/{policies → assets/policies}/ui-ux-guidelines.md +0 -0
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ PMA will guide the repository setup flow and, when needed, initialize Optima ins
|
|
|
25
25
|
|
|
26
26
|
During setup, PMA can initialize the repository and create `.optima/.config/optima.yaml`. Optima reads this file for repository-local defaults, feature flags, policy extraction settings, and per-agent config overrides.
|
|
27
27
|
|
|
28
|
-
Repository-local policy overrides live in `.optima/policies/`. If a policy file is not present there, Optima falls back to the bundled plugin default automatically.
|
|
28
|
+
Repository-local policy overrides live in `.optima/policies/`. If a policy file is not present there, Optima falls back to the bundled plugin default from package `assets/policies/` automatically.
|
|
29
29
|
|
|
30
30
|
Repository-local full agent definitions can live in `.optima/agents/`. Use this folder to override a bundled agent's base prompt or define a brand new custom repository agent.
|
|
31
31
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Optima Bundled Policies
|
|
2
2
|
|
|
3
|
-
This package
|
|
3
|
+
This package asset directory contains Optima's bundled fallback policy assets. Target repositories should put repo-local policy overrides in `.optima/policies/`, not in a package-root `policies/` directory.
|
|
4
4
|
|
|
5
5
|
## How Policy Resolution Works
|
|
6
6
|
|
|
7
7
|
For any `<include:policy:<file>.md>` include, Optima resolves policy files in this order:
|
|
8
8
|
|
|
9
9
|
1. `.optima/policies/<file>.md`
|
|
10
|
-
2. bundled plugin default `policies/<file>.md`
|
|
10
|
+
2. bundled plugin default from `assets/policies/<file>.md`
|
|
11
11
|
|
|
12
12
|
Files under `.optima/.config/generated/policies/` are reference copies only. They are not read directly at runtime.
|
|
13
13
|
|
package/dist/index.js
CHANGED
|
@@ -7900,8 +7900,9 @@ async function optima_validate_logic(worktree) {
|
|
|
7900
7900
|
|
|
7901
7901
|
// src/index.js
|
|
7902
7902
|
var PKG_ROOT = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7903
|
-
var
|
|
7904
|
-
var
|
|
7903
|
+
var BUNDLE_ASSETS_DIR = path2.join(PKG_ROOT, "assets");
|
|
7904
|
+
var BUNDLE_AGENTS_DIR = path2.join(BUNDLE_ASSETS_DIR, "agents");
|
|
7905
|
+
var BUNDLE_POLICIES_DIR = path2.join(BUNDLE_ASSETS_DIR, "policies");
|
|
7905
7906
|
var TEMPLATES_DIR = path2.join(PKG_ROOT, "templates");
|
|
7906
7907
|
var HUMANS_REGISTRY_PATH = path2.join(PKG_ROOT, "docs", "core", "humans.md");
|
|
7907
7908
|
var MANDATORY_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
@@ -7965,7 +7966,7 @@ var REPO_LOCAL_POLICIES_README = [
|
|
|
7965
7966
|
"For any `<include:policy:<file>.md>` include, Optima resolves policy files in this order:",
|
|
7966
7967
|
"",
|
|
7967
7968
|
"1. `.optima/policies/<file>.md`",
|
|
7968
|
-
"2. bundled plugin default `policies/<file>.md`",
|
|
7969
|
+
"2. bundled plugin default from package assets (`assets/policies/<file>.md`)",
|
|
7969
7970
|
"",
|
|
7970
7971
|
"Files under `.optima/.config/generated/policies/` are reference copies only. They are not read directly at runtime.",
|
|
7971
7972
|
"",
|
|
@@ -8853,6 +8854,9 @@ function optimaRuntimeDir(worktree) {
|
|
|
8853
8854
|
function clickUpWebhookStatePath(worktree) {
|
|
8854
8855
|
return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
|
|
8855
8856
|
}
|
|
8857
|
+
function clickUpCommentLedgerPath(worktree) {
|
|
8858
|
+
return path2.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
|
|
8859
|
+
}
|
|
8856
8860
|
function clickUpWebhookLogPath(worktree) {
|
|
8857
8861
|
return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
8858
8862
|
}
|
|
@@ -9206,6 +9210,71 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
|
|
|
9206
9210
|
if (recent.includes(eventKey)) return { duplicate: true, state };
|
|
9207
9211
|
return { duplicate: false, state: { ...state, recentEventKeys: [...recent, eventKey].slice(-limit) } };
|
|
9208
9212
|
}
|
|
9213
|
+
function readClickUpCommentLedger(ledgerPath) {
|
|
9214
|
+
const processed = /* @__PURE__ */ new Set();
|
|
9215
|
+
if (!ledgerPath || !fs2.existsSync(ledgerPath)) return processed;
|
|
9216
|
+
try {
|
|
9217
|
+
const raw = fs2.readFileSync(ledgerPath, "utf8");
|
|
9218
|
+
for (const [index, line] of raw.split(/\r?\n/).entries()) {
|
|
9219
|
+
if (!line.trim()) continue;
|
|
9220
|
+
try {
|
|
9221
|
+
const entry = JSON.parse(line);
|
|
9222
|
+
if (entry?.key) processed.add(String(entry.key));
|
|
9223
|
+
} catch (error) {
|
|
9224
|
+
throw new Error(`malformed ledger row ${index + 1}: ${error.message}`);
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
} catch (error) {
|
|
9228
|
+
throw new Error(`ClickUp comment ledger unavailable: ${error.message}`);
|
|
9229
|
+
}
|
|
9230
|
+
return processed;
|
|
9231
|
+
}
|
|
9232
|
+
function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
|
|
9233
|
+
if (!ledgerPath || !entry.key) return;
|
|
9234
|
+
fs2.mkdirSync(path2.dirname(ledgerPath), { recursive: true, mode: 448 });
|
|
9235
|
+
fs2.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
9236
|
+
`, { encoding: "utf8", mode: 384 });
|
|
9237
|
+
try {
|
|
9238
|
+
fs2.chmodSync(ledgerPath, 384);
|
|
9239
|
+
} catch {
|
|
9240
|
+
}
|
|
9241
|
+
}
|
|
9242
|
+
function clickUpCommentLedgerKey({ taskId, eventType, payload }) {
|
|
9243
|
+
const history = Array.isArray(payload?.history_items) ? payload.history_items[0] : payload?.history_item;
|
|
9244
|
+
const comment = clickUpCommentFromPayload(payload);
|
|
9245
|
+
const commentId = String(comment?.id || payload?.comment_id || payload?.commentId || history?.comment_id || history?.commentId || "").trim();
|
|
9246
|
+
if (!commentId) return "";
|
|
9247
|
+
const explicitVersion = String(
|
|
9248
|
+
comment?.date_updated || comment?.dateUpdated || comment?.updated_at || comment?.updatedAt || comment?._version_vector || comment?.version_vector || comment?.versionVector || comment?.version || comment?.revision || comment?.modified_at || comment?.modifiedAt || ""
|
|
9249
|
+
).trim();
|
|
9250
|
+
const contentVersion = crypto.createHash("sha256").update(JSON.stringify({ text: clickUpCommentText(comment), parts: Array.isArray(comment.comment) ? comment.comment : null })).digest("hex").slice(0, 16);
|
|
9251
|
+
return [String(taskId || "").trim(), "comment", commentId, explicitVersion || `sha256-${contentVersion}`].filter(Boolean).join(":");
|
|
9252
|
+
}
|
|
9253
|
+
function isClickUpCommentVersionProcessed({ ledgerPath, key, ledger = null, worktree = process.cwd() } = {}) {
|
|
9254
|
+
if (!key) return false;
|
|
9255
|
+
try {
|
|
9256
|
+
return (ledger || readClickUpCommentLedger(ledgerPath)).has(key);
|
|
9257
|
+
} catch (error) {
|
|
9258
|
+
try {
|
|
9259
|
+
appendClickUpWebhookLocalLog(worktree, { type: "comment_ledger_read_failed", ledgerPath, message: error.message });
|
|
9260
|
+
} catch {
|
|
9261
|
+
}
|
|
9262
|
+
throw error;
|
|
9263
|
+
}
|
|
9264
|
+
}
|
|
9265
|
+
function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventType, payload, result, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9266
|
+
if (!key) return;
|
|
9267
|
+
const comment = clickUpCommentFromPayload(payload);
|
|
9268
|
+
appendClickUpCommentLedgerEntry(ledgerPath, {
|
|
9269
|
+
key,
|
|
9270
|
+
taskId,
|
|
9271
|
+
eventType,
|
|
9272
|
+
commentId: comment?.id || payload?.comment_id || payload?.commentId || null,
|
|
9273
|
+
action: result?.action || null,
|
|
9274
|
+
sessionId: result?.sessionId || null,
|
|
9275
|
+
recordedAt: at.toISOString()
|
|
9276
|
+
});
|
|
9277
|
+
}
|
|
9209
9278
|
function clickUpTaskIdFromPayload(payload = {}) {
|
|
9210
9279
|
return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
|
|
9211
9280
|
}
|
|
@@ -9235,12 +9304,15 @@ function clickUpCommentAuthorId(comment = {}) {
|
|
|
9235
9304
|
return String(comment.user?.id || comment.user?.userid || comment.author?.id || comment.author_id || comment.user_id || "").trim();
|
|
9236
9305
|
}
|
|
9237
9306
|
function clickUpCommentText(comment = {}) {
|
|
9238
|
-
|
|
9307
|
+
if (Array.isArray(comment.comment)) return comment.comment.map((part) => String(part?.text || "")).join("");
|
|
9308
|
+
return String(comment.comment_text || comment.text || comment.comment || comment.plain_text || comment.text_content || "");
|
|
9239
9309
|
}
|
|
9240
9310
|
function clickUpCommentMentionsProductManager(comment = {}, routing = {}) {
|
|
9241
9311
|
const mentionUserId = String(routing.productManagerMentionUserId || "").trim();
|
|
9242
9312
|
const mentions = Array.isArray(comment.mentions) ? comment.mentions : [];
|
|
9243
9313
|
if (mentionUserId && mentions.some((mention) => String(mention?.id || mention?.userid || mention?.user?.id || mention).trim() === mentionUserId)) return true;
|
|
9314
|
+
const richText = Array.isArray(comment.comment) ? comment.comment : [];
|
|
9315
|
+
if (mentionUserId && richText.some((part) => part?.type === "tag" && String(part?.user?.id || part?.user?.userid || "").trim() === mentionUserId)) return true;
|
|
9244
9316
|
const fallback = String(routing.productManagerMentionName || CLICKUP_PM_MENTION_NAME).trim().toLowerCase();
|
|
9245
9317
|
return fallback ? clickUpCommentText(comment).toLowerCase().includes(`@${fallback}`) : false;
|
|
9246
9318
|
}
|
|
@@ -9558,17 +9630,21 @@ async function withClickUpTaskRouteLock(taskId, operation) {
|
|
|
9558
9630
|
if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
|
|
9559
9631
|
}
|
|
9560
9632
|
}
|
|
9561
|
-
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname() } = {}) {
|
|
9633
|
+
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
|
|
9562
9634
|
const eventType = clickUpEventType(payload);
|
|
9563
9635
|
const eventKey = clickUpWebhookEventKey(payload);
|
|
9564
9636
|
const remembered = rememberClickUpWebhookEvent(state, eventKey);
|
|
9565
9637
|
if (remembered.duplicate) return { ok: true, action: "ignored", reason: "duplicate", eventKey };
|
|
9566
9638
|
let stateToPersist = remembered.state;
|
|
9639
|
+
let commentLedgerKey = "";
|
|
9567
9640
|
const persistState = (nextState) => {
|
|
9568
9641
|
stateToPersist = nextState;
|
|
9569
9642
|
if (saveState) saveState(nextState);
|
|
9570
9643
|
};
|
|
9571
9644
|
const finish = (result) => {
|
|
9645
|
+
if (result?.ok && commentLedgerKey && ["created_session", "sent_to_existing_session", "missing_session_reported"].includes(result.action)) {
|
|
9646
|
+
recordClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, taskId: result.taskId, eventType, payload, result, at: now() });
|
|
9647
|
+
}
|
|
9572
9648
|
if (result?.ok && saveState) saveState(stateToPersist);
|
|
9573
9649
|
return result;
|
|
9574
9650
|
};
|
|
@@ -9583,8 +9659,16 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9583
9659
|
const authorId = clickUpCommentAuthorId(comment);
|
|
9584
9660
|
if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
|
|
9585
9661
|
if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
|
|
9662
|
+
commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
|
|
9663
|
+
try {
|
|
9664
|
+
if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
|
|
9665
|
+
return finish({ ok: true, action: "ignored", reason: "comment_version_already_processed", taskId, eventKey, commentLedgerKey });
|
|
9666
|
+
}
|
|
9667
|
+
} catch (error) {
|
|
9668
|
+
return { ok: false, action: "error", reason: "comment_ledger_unavailable", taskId, eventKey, message: error.message };
|
|
9669
|
+
}
|
|
9586
9670
|
}
|
|
9587
|
-
if (!isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
|
|
9671
|
+
if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
|
|
9588
9672
|
if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
|
|
9589
9673
|
if (clickupClient?.getTask) {
|
|
9590
9674
|
const latestTask = await clickupClient.getTask(taskId);
|
|
@@ -11394,7 +11478,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11394
11478
|
}
|
|
11395
11479
|
};
|
|
11396
11480
|
}
|
|
11397
|
-
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11481
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11398
11482
|
export {
|
|
11399
11483
|
OptimaPlugin as default
|
|
11400
11484
|
};
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -7907,8 +7907,9 @@ async function optima_validate_logic(worktree) {
|
|
|
7907
7907
|
|
|
7908
7908
|
// src/index.js
|
|
7909
7909
|
var PKG_ROOT = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7910
|
-
var
|
|
7911
|
-
var
|
|
7910
|
+
var BUNDLE_ASSETS_DIR = path2.join(PKG_ROOT, "assets");
|
|
7911
|
+
var BUNDLE_AGENTS_DIR = path2.join(BUNDLE_ASSETS_DIR, "agents");
|
|
7912
|
+
var BUNDLE_POLICIES_DIR = path2.join(BUNDLE_ASSETS_DIR, "policies");
|
|
7912
7913
|
var TEMPLATES_DIR = path2.join(PKG_ROOT, "templates");
|
|
7913
7914
|
var HUMANS_REGISTRY_PATH = path2.join(PKG_ROOT, "docs", "core", "humans.md");
|
|
7914
7915
|
var MANDATORY_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
|
|
@@ -7972,7 +7973,7 @@ var REPO_LOCAL_POLICIES_README = [
|
|
|
7972
7973
|
"For any `<include:policy:<file>.md>` include, Optima resolves policy files in this order:",
|
|
7973
7974
|
"",
|
|
7974
7975
|
"1. `.optima/policies/<file>.md`",
|
|
7975
|
-
"2. bundled plugin default `policies/<file>.md`",
|
|
7976
|
+
"2. bundled plugin default from package assets (`assets/policies/<file>.md`)",
|
|
7976
7977
|
"",
|
|
7977
7978
|
"Files under `.optima/.config/generated/policies/` are reference copies only. They are not read directly at runtime.",
|
|
7978
7979
|
"",
|
|
@@ -8860,6 +8861,9 @@ function optimaRuntimeDir(worktree) {
|
|
|
8860
8861
|
function clickUpWebhookStatePath(worktree) {
|
|
8861
8862
|
return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
|
|
8862
8863
|
}
|
|
8864
|
+
function clickUpCommentLedgerPath(worktree) {
|
|
8865
|
+
return path2.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
|
|
8866
|
+
}
|
|
8863
8867
|
function clickUpWebhookLogPath(worktree) {
|
|
8864
8868
|
return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
8865
8869
|
}
|
|
@@ -9213,6 +9217,71 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
|
|
|
9213
9217
|
if (recent.includes(eventKey)) return { duplicate: true, state };
|
|
9214
9218
|
return { duplicate: false, state: { ...state, recentEventKeys: [...recent, eventKey].slice(-limit) } };
|
|
9215
9219
|
}
|
|
9220
|
+
function readClickUpCommentLedger(ledgerPath) {
|
|
9221
|
+
const processed = /* @__PURE__ */ new Set();
|
|
9222
|
+
if (!ledgerPath || !fs2.existsSync(ledgerPath)) return processed;
|
|
9223
|
+
try {
|
|
9224
|
+
const raw = fs2.readFileSync(ledgerPath, "utf8");
|
|
9225
|
+
for (const [index, line] of raw.split(/\r?\n/).entries()) {
|
|
9226
|
+
if (!line.trim()) continue;
|
|
9227
|
+
try {
|
|
9228
|
+
const entry = JSON.parse(line);
|
|
9229
|
+
if (entry?.key) processed.add(String(entry.key));
|
|
9230
|
+
} catch (error) {
|
|
9231
|
+
throw new Error(`malformed ledger row ${index + 1}: ${error.message}`);
|
|
9232
|
+
}
|
|
9233
|
+
}
|
|
9234
|
+
} catch (error) {
|
|
9235
|
+
throw new Error(`ClickUp comment ledger unavailable: ${error.message}`);
|
|
9236
|
+
}
|
|
9237
|
+
return processed;
|
|
9238
|
+
}
|
|
9239
|
+
function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
|
|
9240
|
+
if (!ledgerPath || !entry.key) return;
|
|
9241
|
+
fs2.mkdirSync(path2.dirname(ledgerPath), { recursive: true, mode: 448 });
|
|
9242
|
+
fs2.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
|
|
9243
|
+
`, { encoding: "utf8", mode: 384 });
|
|
9244
|
+
try {
|
|
9245
|
+
fs2.chmodSync(ledgerPath, 384);
|
|
9246
|
+
} catch {
|
|
9247
|
+
}
|
|
9248
|
+
}
|
|
9249
|
+
function clickUpCommentLedgerKey({ taskId, eventType, payload }) {
|
|
9250
|
+
const history = Array.isArray(payload?.history_items) ? payload.history_items[0] : payload?.history_item;
|
|
9251
|
+
const comment = clickUpCommentFromPayload(payload);
|
|
9252
|
+
const commentId = String(comment?.id || payload?.comment_id || payload?.commentId || history?.comment_id || history?.commentId || "").trim();
|
|
9253
|
+
if (!commentId) return "";
|
|
9254
|
+
const explicitVersion = String(
|
|
9255
|
+
comment?.date_updated || comment?.dateUpdated || comment?.updated_at || comment?.updatedAt || comment?._version_vector || comment?.version_vector || comment?.versionVector || comment?.version || comment?.revision || comment?.modified_at || comment?.modifiedAt || ""
|
|
9256
|
+
).trim();
|
|
9257
|
+
const contentVersion = crypto.createHash("sha256").update(JSON.stringify({ text: clickUpCommentText(comment), parts: Array.isArray(comment.comment) ? comment.comment : null })).digest("hex").slice(0, 16);
|
|
9258
|
+
return [String(taskId || "").trim(), "comment", commentId, explicitVersion || `sha256-${contentVersion}`].filter(Boolean).join(":");
|
|
9259
|
+
}
|
|
9260
|
+
function isClickUpCommentVersionProcessed({ ledgerPath, key, ledger = null, worktree = process.cwd() } = {}) {
|
|
9261
|
+
if (!key) return false;
|
|
9262
|
+
try {
|
|
9263
|
+
return (ledger || readClickUpCommentLedger(ledgerPath)).has(key);
|
|
9264
|
+
} catch (error) {
|
|
9265
|
+
try {
|
|
9266
|
+
appendClickUpWebhookLocalLog(worktree, { type: "comment_ledger_read_failed", ledgerPath, message: error.message });
|
|
9267
|
+
} catch {
|
|
9268
|
+
}
|
|
9269
|
+
throw error;
|
|
9270
|
+
}
|
|
9271
|
+
}
|
|
9272
|
+
function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventType, payload, result, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9273
|
+
if (!key) return;
|
|
9274
|
+
const comment = clickUpCommentFromPayload(payload);
|
|
9275
|
+
appendClickUpCommentLedgerEntry(ledgerPath, {
|
|
9276
|
+
key,
|
|
9277
|
+
taskId,
|
|
9278
|
+
eventType,
|
|
9279
|
+
commentId: comment?.id || payload?.comment_id || payload?.commentId || null,
|
|
9280
|
+
action: result?.action || null,
|
|
9281
|
+
sessionId: result?.sessionId || null,
|
|
9282
|
+
recordedAt: at.toISOString()
|
|
9283
|
+
});
|
|
9284
|
+
}
|
|
9216
9285
|
function clickUpTaskIdFromPayload(payload = {}) {
|
|
9217
9286
|
return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
|
|
9218
9287
|
}
|
|
@@ -9242,12 +9311,15 @@ function clickUpCommentAuthorId(comment = {}) {
|
|
|
9242
9311
|
return String(comment.user?.id || comment.user?.userid || comment.author?.id || comment.author_id || comment.user_id || "").trim();
|
|
9243
9312
|
}
|
|
9244
9313
|
function clickUpCommentText(comment = {}) {
|
|
9245
|
-
|
|
9314
|
+
if (Array.isArray(comment.comment)) return comment.comment.map((part) => String(part?.text || "")).join("");
|
|
9315
|
+
return String(comment.comment_text || comment.text || comment.comment || comment.plain_text || comment.text_content || "");
|
|
9246
9316
|
}
|
|
9247
9317
|
function clickUpCommentMentionsProductManager(comment = {}, routing = {}) {
|
|
9248
9318
|
const mentionUserId = String(routing.productManagerMentionUserId || "").trim();
|
|
9249
9319
|
const mentions = Array.isArray(comment.mentions) ? comment.mentions : [];
|
|
9250
9320
|
if (mentionUserId && mentions.some((mention) => String(mention?.id || mention?.userid || mention?.user?.id || mention).trim() === mentionUserId)) return true;
|
|
9321
|
+
const richText = Array.isArray(comment.comment) ? comment.comment : [];
|
|
9322
|
+
if (mentionUserId && richText.some((part) => part?.type === "tag" && String(part?.user?.id || part?.user?.userid || "").trim() === mentionUserId)) return true;
|
|
9251
9323
|
const fallback = String(routing.productManagerMentionName || CLICKUP_PM_MENTION_NAME).trim().toLowerCase();
|
|
9252
9324
|
return fallback ? clickUpCommentText(comment).toLowerCase().includes(`@${fallback}`) : false;
|
|
9253
9325
|
}
|
|
@@ -9565,17 +9637,21 @@ async function withClickUpTaskRouteLock(taskId, operation) {
|
|
|
9565
9637
|
if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
|
|
9566
9638
|
}
|
|
9567
9639
|
}
|
|
9568
|
-
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname() } = {}) {
|
|
9640
|
+
async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
|
|
9569
9641
|
const eventType = clickUpEventType(payload);
|
|
9570
9642
|
const eventKey = clickUpWebhookEventKey(payload);
|
|
9571
9643
|
const remembered = rememberClickUpWebhookEvent(state, eventKey);
|
|
9572
9644
|
if (remembered.duplicate) return { ok: true, action: "ignored", reason: "duplicate", eventKey };
|
|
9573
9645
|
let stateToPersist = remembered.state;
|
|
9646
|
+
let commentLedgerKey = "";
|
|
9574
9647
|
const persistState = (nextState) => {
|
|
9575
9648
|
stateToPersist = nextState;
|
|
9576
9649
|
if (saveState) saveState(nextState);
|
|
9577
9650
|
};
|
|
9578
9651
|
const finish = (result) => {
|
|
9652
|
+
if (result?.ok && commentLedgerKey && ["created_session", "sent_to_existing_session", "missing_session_reported"].includes(result.action)) {
|
|
9653
|
+
recordClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, taskId: result.taskId, eventType, payload, result, at: now() });
|
|
9654
|
+
}
|
|
9579
9655
|
if (result?.ok && saveState) saveState(stateToPersist);
|
|
9580
9656
|
return result;
|
|
9581
9657
|
};
|
|
@@ -9590,8 +9666,16 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9590
9666
|
const authorId = clickUpCommentAuthorId(comment);
|
|
9591
9667
|
if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
|
|
9592
9668
|
if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
|
|
9669
|
+
commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
|
|
9670
|
+
try {
|
|
9671
|
+
if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
|
|
9672
|
+
return finish({ ok: true, action: "ignored", reason: "comment_version_already_processed", taskId, eventKey, commentLedgerKey });
|
|
9673
|
+
}
|
|
9674
|
+
} catch (error) {
|
|
9675
|
+
return { ok: false, action: "error", reason: "comment_ledger_unavailable", taskId, eventKey, message: error.message };
|
|
9676
|
+
}
|
|
9593
9677
|
}
|
|
9594
|
-
if (!isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
|
|
9678
|
+
if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
|
|
9595
9679
|
if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
|
|
9596
9680
|
if (clickupClient?.getTask) {
|
|
9597
9681
|
const latestTask = await clickupClient.getTask(taskId);
|
|
@@ -11401,7 +11485,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11401
11485
|
}
|
|
11402
11486
|
};
|
|
11403
11487
|
}
|
|
11404
|
-
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11488
|
+
OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11405
11489
|
|
|
11406
11490
|
// src/sanitize_cli.js
|
|
11407
11491
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|
|
@@ -90,7 +90,7 @@ policies:
|
|
|
90
90
|
extract_defaults: all
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
This writes the bundled default policy files to `.optima/.config/generated/policies/` for reference. Those generated files are not used directly at runtime. To customize one, copy it into `.optima/policies/` and edit the copy.
|
|
93
|
+
This writes the bundled default policy files from package `assets/policies/` to `.optima/.config/generated/policies/` for reference. Those generated files are not used directly at runtime. To customize one, copy it into `.optima/policies/` and edit the copy.
|
|
94
94
|
|
|
95
95
|
### Add repository-specific agent instructions
|
|
96
96
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defend-tech/opencode-optima",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"
|
|
@@ -25,9 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
|
-
"
|
|
28
|
+
"assets",
|
|
29
29
|
"docs",
|
|
30
|
-
"policies",
|
|
31
30
|
"templates",
|
|
32
31
|
"scripts/optima-sanitize-host.js",
|
|
33
32
|
"Agents_Common.md",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|