thumbgate 1.26.7 → 1.27.2

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.
Files changed (50) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.well-known/agentic-verify.txt +1 -0
  4. package/.well-known/llms.txt +2 -0
  5. package/.well-known/mcp/server-card.json +1 -1
  6. package/README.md +20 -9
  7. package/adapters/claude/.mcp.json +2 -2
  8. package/adapters/gcp/dfcx-webhook-gate.js +295 -0
  9. package/adapters/mcp/server-stdio.js +28 -1
  10. package/adapters/opencode/opencode.json +1 -1
  11. package/bench/thumbgate-bench.json +2 -2
  12. package/bin/cli.js +147 -10
  13. package/bin/dashboard-cli.js +7 -0
  14. package/config/gate-classifier-routing.json +98 -0
  15. package/config/gate-templates.json +60 -0
  16. package/config/mcp-allowlists.json +8 -7
  17. package/config/model-candidates.json +71 -6
  18. package/package.json +26 -10
  19. package/public/chatgpt-app.html +330 -0
  20. package/public/codex-plugin.html +66 -14
  21. package/public/dashboard.html +203 -17
  22. package/public/index.html +79 -4
  23. package/public/learn.html +70 -0
  24. package/public/lessons.html +129 -6
  25. package/public/numbers.html +2 -2
  26. package/public/pricing.html +20 -2
  27. package/scripts/agent-operations-planner.js +621 -0
  28. package/scripts/agent-reward-model.js +53 -1
  29. package/scripts/ai-component-inventory.js +367 -0
  30. package/scripts/classifier-routing.js +130 -0
  31. package/scripts/cli-schema.js +26 -0
  32. package/scripts/dashboard-chat.js +64 -17
  33. package/scripts/feedback-sanitizer.js +105 -0
  34. package/scripts/gates-engine.js +258 -61
  35. package/scripts/hybrid-feedback-context.js +141 -7
  36. package/scripts/memory-scope-readiness.js +159 -0
  37. package/scripts/parallel-workflow-orchestrator.js +293 -0
  38. package/scripts/plausible-domain-config.js +86 -0
  39. package/scripts/plausible-server-events.js +4 -2
  40. package/scripts/proxy-pointer-rag-guardrails.js +42 -1
  41. package/scripts/qa-scenario-planner.js +136 -0
  42. package/scripts/repeat-metric.js +28 -12
  43. package/scripts/secret-fixture-tokens.js +61 -0
  44. package/scripts/secret-scanner.js +44 -5
  45. package/scripts/security-scanner.js +80 -0
  46. package/scripts/seo-gsd.js +53 -0
  47. package/scripts/thumbgate-bench.js +16 -1
  48. package/scripts/tool-registry.js +37 -0
  49. package/scripts/workflow-sentinel.js +189 -4
  50. package/src/api/server.js +276 -10
@@ -0,0 +1,621 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ function toList(value) {
5
+ if (Array.isArray(value)) return value.map(String).map((entry) => entry.trim()).filter(Boolean);
6
+ if (typeof value === 'string') return value.split(',').map((entry) => entry.trim()).filter(Boolean);
7
+ return [];
8
+ }
9
+
10
+ function buildLoopRoutinePlan(input = {}) {
11
+ const tasks = toList(input.tasks);
12
+ const cadenceMinutes = Number(input.cadenceMinutes || input.cadence || 60);
13
+ const serverHosted = Boolean(input.serverHosted || input.routine);
14
+ const highRiskTasks = tasks.filter((task) => /merge|deploy|publish|payment|email|post|send|delete|rebase/i.test(task));
15
+
16
+ return {
17
+ name: 'thumbgate-loop-routine-plan',
18
+ mode: serverHosted ? 'routine' : 'cron',
19
+ cadenceMinutes,
20
+ status: tasks.length ? 'actionable' : 'needs-tasks',
21
+ tasks,
22
+ gates: [
23
+ 'scope each loop to a named workflow before it runs',
24
+ 'require idempotency evidence for repeated execution',
25
+ 'write a run receipt with inputs, outputs, skipped items, and failures',
26
+ 'convert deterministic loop failures into regression tests before retrying automatically',
27
+ ],
28
+ approvalRequired: highRiskTasks.length > 0,
29
+ highRiskTasks,
30
+ nextActions: tasks.length
31
+ ? [
32
+ serverHosted ? 'Configure a hosted routine only after local cron proof is stable' : 'Start as local cron with dry-run output',
33
+ 'Add ThumbGate PreToolUse checks around every write/send/deploy action',
34
+ 'Review loop receipts before increasing cadence',
35
+ ]
36
+ : ['List the recurring task, owner, cadence, and maximum blast radius before scheduling'],
37
+ };
38
+ }
39
+
40
+ function buildSkillFirstAgentPlan(input = {}) {
41
+ const skills = toList(input.skills);
42
+ const allowedTools = new Set(toList(input.allowedTools));
43
+ const instructionFiles = toList(input.instructionFiles || input.instructions || ['CLAUDE.md', 'AGENTS.md', 'clauded.md']);
44
+ const autoLoadPaths = toList(input.autoLoadPaths || input.pluginPaths || ['.claude/skills']);
45
+ const needsWrite = Boolean(input.needsWrite || input.createFiles || input.editFiles);
46
+ const missingTools = ['read', 'edit', 'bash']
47
+ .concat(needsWrite ? ['write'] : [])
48
+ .filter((tool) => !allowedTools.has(tool));
49
+
50
+ return {
51
+ name: 'thumbgate-skill-first-agent-plan',
52
+ status: missingTools.length ? 'blocked' : 'ready',
53
+ instructionFiles,
54
+ autoLoadPaths,
55
+ skills,
56
+ allowedTools: Array.from(allowedTools),
57
+ missingTools,
58
+ gates: [
59
+ 'load project instructions before enabling skills',
60
+ 'map every skill to a bounded workflow and proof command',
61
+ 'verify plugin auto-load from project skill folders after session restart',
62
+ 'deny filesystem writes when write permission is absent',
63
+ 'record which skill produced each high-risk tool call',
64
+ ],
65
+ nextActions: missingTools.length
66
+ ? missingTools.map((tool) => `Grant or explicitly deny ${tool} before running the agent`)
67
+ : ['Run one dry-run with the selected skills', 'Promote stable skill failures into ThumbGate lessons'],
68
+ };
69
+ }
70
+
71
+ function buildContinuousBatchingPlan(input = {}) {
72
+ const concurrentUsers = Number(input.concurrentUsers || input.users || 1);
73
+ const gpuHosted = Boolean(input.gpuHosted || input.selfHosted);
74
+ const averageDecodeTokens = Number(input.averageDecodeTokens || input.decodeTokens || 256);
75
+ const needsBatching = gpuHosted && concurrentUsers >= 3;
76
+
77
+ return {
78
+ name: 'thumbgate-continuous-batching-plan',
79
+ status: needsBatching ? 'adopt-continuous-batching' : 'use-managed-or-simple-serving',
80
+ concurrentUsers,
81
+ averageDecodeTokens,
82
+ scheduling: needsBatching ? 'iteration-level scheduling with queued-request admission' : 'provider-managed or simple request queue',
83
+ guardrails: [
84
+ 'do not put LLM batching in the PreToolUse hot path unless latency proof exists',
85
+ 'cap queue wait time before admitting a request',
86
+ 'emit per-request receipts so one user cannot hide another user failure',
87
+ 'measure p95 latency, throughput, and timeout rate before and after batching',
88
+ ],
89
+ nextActions: needsBatching
90
+ ? ['Evaluate vLLM, SGLang, or provider-native continuous batching', 'Add backpressure and queue-time metrics', 'Keep deterministic gates outside the model batch']
91
+ : ['Stay with managed inference or deterministic local checks until concurrency justifies GPU scheduling'],
92
+ };
93
+ }
94
+
95
+ function buildLegalAgentGovernancePlan(input = {}) {
96
+ const actions = toList(input.actions);
97
+ const agents = toList(input.agents);
98
+ const privileged = Boolean(input.privileged || input.privilege);
99
+ const matterId = input.matterId || input.matter || 'matter-unscoped';
100
+ const externalActions = actions.filter((action) => /email|send|file|serve|submit|publish|client|court|opposing/i.test(action));
101
+
102
+ return {
103
+ name: 'thumbgate-legal-agent-governance-plan',
104
+ status: matterId === 'matter-unscoped' ? 'blocked' : 'ready-for-pilot',
105
+ matterId,
106
+ agents,
107
+ privileged,
108
+ approvalRequired: privileged || externalActions.length > 0,
109
+ gates: [
110
+ 'matter-scoped memory and retrieval before any legal-agent action',
111
+ 'privilege and confidentiality check before reading or writing documents',
112
+ 'unsupported-citation and hallucinated-authority check before legal output leaves draft mode',
113
+ 'human approval before external send, filing, client advice, or opposing-party communication',
114
+ 'audit trail with agent, matter, source pointers, approver, and final disposition',
115
+ ],
116
+ externalActions,
117
+ nextActions: matterId === 'matter-unscoped'
118
+ ? ['Assign a matter ID and allowed data boundary before enabling legal agents']
119
+ : ['Run in observe-only mode on one matter', 'Capture blocked unsupported claims as lessons', 'Export audit evidence for risk/pricing review'],
120
+ };
121
+ }
122
+
123
+ function buildDynamicWorkflowReadinessPlan(input = {}) {
124
+ const task = String(input.task || input.objective || '').trim();
125
+ const successCriteria = toList(input.successCriteria || input.criteria || input.oracles);
126
+ const parallelAgents = Number(input.parallelAgents || input.agents || 1);
127
+ const tokenBudget = Number(input.tokenBudget || input.maxTokens || 0);
128
+ const needsVerifier = Boolean(input.needsVerifier || input.securitySweep || input.migration || input.research);
129
+ const objectiveOracle = successCriteria.length > 0;
130
+ const enoughScale = parallelAgents >= 4;
131
+ const budgeted = tokenBudget > 0;
132
+ const approved = objectiveOracle && enoughScale && budgeted && needsVerifier;
133
+
134
+ return {
135
+ name: 'thumbgate-dynamic-workflow-readiness-plan',
136
+ status: approved ? 'ready-for-human-plan-review' : 'use-single-agent-or-subagent',
137
+ task,
138
+ successCriteria,
139
+ parallelAgents,
140
+ tokenBudget,
141
+ gates: [
142
+ 'require an objective success oracle before dynamic workflow execution',
143
+ 'require a versioned script plan before spawning concurrent agents',
144
+ 'require human review of the generated workflow plan before spend begins',
145
+ 'track stage status, verifier results, and token usage in the workflow receipt',
146
+ ],
147
+ missingEvidence: [
148
+ objectiveOracle ? null : 'objective success criteria',
149
+ enoughScale ? null : 'parallel scale requirement',
150
+ budgeted ? null : 'token or cost budget',
151
+ needsVerifier ? null : 'independent verifier or adversarial check',
152
+ ].filter(Boolean),
153
+ nextActions: approved
154
+ ? ['Generate a versioned workflow script', 'Review plan before launch', 'Run with token/cost dashboard open']
155
+ : ['Keep this in a cheaper single-agent or focused subagent path until the missing evidence exists'],
156
+ };
157
+ }
158
+
159
+ function buildOpenModelCustomizationPlan(input = {}) {
160
+ const workload = String(input.workload || input.task || 'unspecified-workload').trim();
161
+ const proprietarySignals = toList(input.proprietarySignals || input.signals || input.embeddings);
162
+ const runtimeEncoding = Boolean(input.runtimeEncoding || input.encodeAtRuntime);
163
+ const baselineCost = Number(input.baselineCost || input.currentCost || 0);
164
+ const hasBenchmark = Boolean(input.hasBenchmark || input.benchmark || input.accuracyBaseline);
165
+ const shouldCustomize = proprietarySignals.length > 0 && baselineCost > 0 && hasBenchmark;
166
+
167
+ return {
168
+ name: 'thumbgate-open-model-customization-plan',
169
+ status: shouldCustomize ? 'customize-and-benchmark' : 'measure-before-customizing',
170
+ workload,
171
+ proprietarySignals,
172
+ runtimeEncodingRisk: runtimeEncoding ? 'high-latency-runtime-encoding' : 'precomputed-or-not-applicable',
173
+ gates: [
174
+ 'prove task-specific data quality before replacing a frontier model lane',
175
+ 'precompute proprietary embeddings when runtime encoding would add latency',
176
+ 'benchmark accuracy, p95 latency, and cost per request against the current model',
177
+ 'keep fallback routing to the frontier model for low-confidence or unsupported cases',
178
+ ],
179
+ nextActions: shouldCustomize
180
+ ? ['Build an offline embedding/index job', 'Run side-by-side eval against the frontier baseline', 'Route only the passing workload slice to the customized model']
181
+ : ['Capture current cost, latency, accuracy, and proprietary signal inventory before changing the model stack'],
182
+ };
183
+ }
184
+
185
+ function buildDigitalPrCitationPlan(input = {}) {
186
+ const audiences = toList(input.audiences || input.buyers || ['engineering leaders', 'legal innovation teams']);
187
+ const proofAssets = toList(input.proofAssets || input.assets);
188
+ const earnedMentions = toList(input.earnedMentions || input.mentions);
189
+ const seasonalHook = input.seasonalHook || input.newsHook || 'agentic AI governance';
190
+ const hasCitationProof = proofAssets.length > 0 && earnedMentions.length > 0;
191
+
192
+ return {
193
+ name: 'thumbgate-digital-pr-citation-plan',
194
+ status: hasCitationProof ? 'ready-for-outreach' : 'build-citation-proof-first',
195
+ audiences,
196
+ seasonalHook,
197
+ proofAssets,
198
+ earnedMentions,
199
+ gates: [
200
+ 'publish one canonical proof asset before pitching',
201
+ 'make claims quotable with current dates, evidence links, and machine-readable context',
202
+ 'track citation share of voice across Google AI Overviews, ChatGPT, Perplexity, and Claude',
203
+ 'prefer earned expert citations over thin AI-search tactics',
204
+ ],
205
+ nextActions: hasCitationProof
206
+ ? ['Pitch the proof asset to targeted journalists and directory editors', 'Monitor AI citation share, not only clicks']
207
+ : ['Ship proof asset, comparison page, and llm-context update before outreach'],
208
+ };
209
+ }
210
+
211
+ function buildServerlessVectorPlan(input = {}) {
212
+ const bursty = Boolean(input.bursty || input.agentic || input.unpredictableTraffic);
213
+ const idleHours = Number(input.idleHoursPerDay || input.idleHours || 0);
214
+ const managedEndpoint = Boolean(input.managedEndpoint || input.vercelMarketplace || input.opensearchServerless);
215
+ const sensitiveLocalData = Boolean(input.sensitiveLocalData || input.localOnly);
216
+ const serverlessFit = bursty && idleHours >= 6 && managedEndpoint && !sensitiveLocalData;
217
+
218
+ return {
219
+ name: 'thumbgate-serverless-vector-plan',
220
+ status: serverlessFit ? 'use-serverless-vector-burst-lane' : 'keep-local-or-prove-managed-fit',
221
+ gates: [
222
+ 'use local SQLite/FTS/vector stores for private hot-path enforcement',
223
+ 'route bursty non-sensitive search to serverless only with cost and latency receipts',
224
+ 'require scale-to-zero and decoupled compute/storage evidence before replacing local storage',
225
+ 'keep export/import portability so a marketplace provisioner is not lock-in',
226
+ ],
227
+ nextActions: serverlessFit
228
+ ? ['Provision a managed endpoint in a sandbox project', 'Run burst replay and idle-cost proof', 'Keep ThumbGate local enforcement as fallback']
229
+ : ['Measure traffic burstiness, privacy boundary, idle cost, and latency before adopting serverless search'],
230
+ };
231
+ }
232
+
233
+ function buildMemoryModelPlan(input = {}) {
234
+ const memories = toList(input.memories || input.memoryTypes || ['facts', 'lessons', 'source pointers']);
235
+ const retrainingAvoided = input.retrainingAvoided !== false;
236
+ const sourcePointers = Boolean(input.sourcePointers || input.pointerFirst);
237
+
238
+ return {
239
+ name: 'thumbgate-memory-model-plan',
240
+ status: retrainingAvoided && sourcePointers ? 'upgrade-with-memory-not-retraining' : 'add-source-grounding-first',
241
+ memories,
242
+ gates: [
243
+ 'store durable facts, lessons, and source pointers outside the model weights',
244
+ 'score memory freshness, trust, and matter/workspace scope before retrieval',
245
+ 'inject only the minimum relevant memory into the agent harness',
246
+ 'promote repeated memory-backed failures into PreToolUse prevention rules',
247
+ ],
248
+ nextActions: retrainingAvoided && sourcePointers
249
+ ? ['Run memory retrieval eval before model routing changes', 'Track blocked repeats as the outcome metric']
250
+ : ['Add pointer-first source grounding before treating memory as a model upgrade'],
251
+ };
252
+ }
253
+
254
+ function buildSandboxManifestPlan(input = {}) {
255
+ const manifestEntries = toList(input.manifestEntries || input.mounts || input.entries);
256
+ const outputDirs = toList(input.outputDirs || input.outputs);
257
+ const sandboxProvider = input.sandboxProvider || input.provider || 'local';
258
+ const checkpointing = Boolean(input.checkpointing || input.rehydration || input.longRunning);
259
+ const secretsInSandbox = Boolean(input.secretsInSandbox || input.credentialsInCompute);
260
+ const ready = manifestEntries.length > 0 && outputDirs.length > 0 && !secretsInSandbox;
261
+
262
+ return {
263
+ name: 'thumbgate-sandbox-manifest-plan',
264
+ status: ready ? 'ready-for-sandboxed-agent-run' : 'blocked-until-manifest-safe',
265
+ sandboxProvider,
266
+ manifestEntries,
267
+ outputDirs,
268
+ checkpointing,
269
+ gates: [
270
+ 'describe all readable inputs and writable outputs in a manifest before the run',
271
+ 'separate harness credentials from sandbox compute where model-generated code executes',
272
+ 'enable checkpoint or rehydration proof for long-running work',
273
+ 'route subagents to isolated environments instead of sharing a broad workspace',
274
+ ],
275
+ missingEvidence: [
276
+ manifestEntries.length ? null : 'manifest entries',
277
+ outputDirs.length ? null : 'output directories',
278
+ secretsInSandbox ? 'credentials must stay outside sandbox compute' : null,
279
+ ].filter(Boolean),
280
+ nextActions: ready
281
+ ? ['Run one sandbox smoke test with mounted inputs and output receipt', 'Attach manifest and checkpoint evidence to the audit trail']
282
+ : ['Define manifest, outputs, and credential boundary before letting the agent inspect files or run commands'],
283
+ };
284
+ }
285
+
286
+ function buildNetworkEgressFirewallPlan(input = {}) {
287
+ const allowedDomains = toList(input.allowedDomains || input.allowlist || input.allowedHosts);
288
+ const observedRequests = toList(input.observedRequests || input.requests);
289
+ const secretPatterns = toList(input.secretPatterns || input.secrets || ['api_key', 'token', 'Authorization']);
290
+ const liveDashboard = Boolean(input.liveDashboard || input.dashboard);
291
+ const unknownRequests = observedRequests.filter((request) => {
292
+ const target = String(request).replace(/^https?:\/\//, '').split('/')[0];
293
+ return target && !allowedDomains.some((domain) => target === domain || target.endsWith(`.${domain}`));
294
+ });
295
+
296
+ return {
297
+ name: 'thumbgate-network-egress-firewall-plan',
298
+ status: unknownRequests.length ? 'block-unknown-egress' : 'ready-for-egress-observe-mode',
299
+ allowedDomains,
300
+ observedRequests,
301
+ unknownRequests,
302
+ secretPatterns,
303
+ gates: [
304
+ 'proxy outbound agent HTTP requests through a policy check before network egress',
305
+ 'block requests that target domains outside the workflow allowlist',
306
+ 'scan request headers and bodies for credential-shaped values before sending',
307
+ 'record method, target, status, latency, policy decision, and matching rule in a live request ledger',
308
+ ],
309
+ dashboard: liveDashboard
310
+ ? 'show live request table with method, target, latency, decision, and rule'
311
+ : 'write request receipts first; add live dashboard after the ledger is stable',
312
+ nextActions: unknownRequests.length
313
+ ? ['Deny unknown egress', 'Ask for workflow-scoped approval', 'Record blocked target in the audit trail']
314
+ : ['Run in observe mode for one workflow', 'Promote repeated risky egress into deny rules'],
315
+ };
316
+ }
317
+
318
+ function buildSupplyChainVettingPlan(input = {}) {
319
+ const sources = toList(input.sources || input.packages || input.repositories);
320
+ const sandbox = Boolean(input.sandbox || input.vm || input.goldImage);
321
+ const aiAudit = Boolean(input.aiAudit || input.audit);
322
+ const autoUpdatesDisabled = Boolean(input.autoUpdatesDisabled || input.disableAutoUpdates);
323
+ const installScriptsDisabled = Boolean(input.installScriptsDisabled || input.ignoreScripts);
324
+ const exfilTargets = toList(input.exfilTargets || input.exfiltrationTargets || ['GitHub Contents API', 'HuggingFace datasets', 'attacker HTTP endpoint']);
325
+ const ready = sources.length > 0 && sandbox && aiAudit && autoUpdatesDisabled && installScriptsDisabled;
326
+
327
+ return {
328
+ name: 'thumbgate-supply-chain-vetting-plan',
329
+ status: ready ? 'ready-to-promote-dependency' : 'quarantine-before-use',
330
+ sources,
331
+ exfilTargets,
332
+ gates: [
333
+ 'download GitHub, npm, and PyPI artifacts into a disposable sandbox or gold-image VM first',
334
+ 'run AI-assisted code audit plus deterministic scanners before execution on the primary machine',
335
+ 'scan install hooks, postinstall scripts, binary droppers, credential reads, recursive file walkers, and network uploads',
336
+ 'disable auto-update paths unless the updater is separately reviewed and pinned',
337
+ 'prefer ignore-scripts or equivalent install-script quarantine until package provenance and maintainer account risk are reviewed',
338
+ 'promote only with source, hash, scanner output, and reviewer receipt',
339
+ ],
340
+ missingEvidence: [
341
+ sources.length ? null : 'artifact source list',
342
+ sandbox ? null : 'sandbox or disposable VM proof',
343
+ aiAudit ? null : 'AI/code audit receipt',
344
+ autoUpdatesDisabled ? null : 'auto-update disabled or pinned proof',
345
+ installScriptsDisabled ? null : 'install scripts disabled or reviewed proof',
346
+ ].filter(Boolean),
347
+ nextActions: ready
348
+ ? ['Promote dependency into the allowed catalog with hash evidence', 'Monitor updates through the same quarantine path']
349
+ : ['Keep the artifact quarantined and do not run install scripts on the primary machine'],
350
+ };
351
+ }
352
+
353
+ function buildMarketingAgencyGtmPlan(input = {}) {
354
+ const channels = toList(input.channels || ['lead-generation', 'social', 'seo']);
355
+ const monthlyRetainer = Number(input.monthlyRetainer || input.retainer || 750);
356
+ const proofAssets = toList(input.proofAssets || input.assets);
357
+ const crmAutomation = Boolean(input.crmAutomation || input.highLevel || input.leadNurture);
358
+ const scheduledContent = Boolean(input.scheduledContent || input.metricool || input.scheduler);
359
+ const qualityReview = Boolean(input.qualityReview || input.writerReview || input.claimReview);
360
+ const ready = proofAssets.length > 0 && crmAutomation && scheduledContent && qualityReview;
361
+
362
+ return {
363
+ name: 'thumbgate-marketing-agency-gtm-plan',
364
+ status: ready ? 'ready-to-sell-workflow-sprint' : 'build-gtm-proof-first',
365
+ channels,
366
+ monthlyRetainer,
367
+ offer: 'AI Agent Governance Workflow Hardening Sprint',
368
+ gates: [
369
+ 'target lead generation around one expensive repeated AI-agent mistake, not generic AI automation',
370
+ 'use CRM automation only after consent, attribution, and unsubscribe paths are configured',
371
+ 'schedule social and SEO content from proof assets, not unsupported claims',
372
+ 'quality-review ads, translations, captions, and long-form SEO copy against pricing and capability truth',
373
+ ],
374
+ nextActions: ready
375
+ ? ['Launch a narrow lead-gen campaign for engineering and legal innovation buyers', 'Track replies, booked calls, and proof-page citations']
376
+ : ['Create one proof asset, one lead-nurture sequence, one scheduled content batch, and one quality review checklist'],
377
+ };
378
+ }
379
+
380
+ function buildBedrockAgentCorePlan(input = {}) {
381
+ const frameworks = toList(input.frameworks || ['LangGraph']);
382
+ const serverless = Boolean(input.serverless || input.agentCoreRuntime);
383
+ const memory = Boolean(input.memory || input.agentCoreMemory);
384
+ const observability = Boolean(input.observability || input.agentCoreObservability);
385
+ const identity = Boolean(input.identity || input.agentCoreIdentity);
386
+ const ready = serverless && memory && observability && identity;
387
+
388
+ return {
389
+ name: 'thumbgate-bedrock-agentcore-plan',
390
+ status: ready ? 'ready-for-agentcore-pilot' : 'missing-production-agent-controls',
391
+ frameworks,
392
+ gates: [
393
+ 'treat every AWS agent and subagent as a first-class identity with scoped permissions',
394
+ 'externalize memory and checkpoints so serverless agent restarts do not lose state',
395
+ 'emit OpenTelemetry-compatible traces for every tool call, handoff, and policy decision',
396
+ 'canary and quarantine new agent versions before expanding traffic',
397
+ 'keep ThumbGate pre-action gates in front of Bedrock/LangGraph tool writes and customer-system actions',
398
+ ],
399
+ missingEvidence: [
400
+ serverless ? null : 'serverless runtime proof',
401
+ memory ? null : 'AgentCore Memory or equivalent checkpoint proof',
402
+ observability ? null : 'AgentCore Observability/OpenTelemetry proof',
403
+ identity ? null : 'AgentCore Identity or equivalent scoped credentials proof',
404
+ ].filter(Boolean),
405
+ nextActions: ready
406
+ ? ['Pilot one LangGraph workflow on Bedrock AgentCore with ThumbGate tool-call receipts', 'Compare blocked actions, latency, and trace completeness against local mode']
407
+ : ['Do not promote multi-agent AWS deployment until identity, memory, observability, and runtime controls are proven'],
408
+ };
409
+ }
410
+
411
+ function buildCodeQualityEnablementPlan(input = {}) {
412
+ const owner = input.owner || 'IgorGanapolsky';
413
+ const repo = input.repo || 'ThumbGate';
414
+ const languages = toList(input.languages || ['javascript-typescript', 'python']);
415
+ const runnerType = input.runnerType || input.runner || 'github-hosted';
416
+ const previewAvailable = input.previewAvailable !== false;
417
+
418
+ return {
419
+ name: 'thumbgate-github-code-quality-enablement-plan',
420
+ status: previewAvailable ? 'enable-or-confirm-code-quality' : 'preview-unavailable',
421
+ owner,
422
+ repo,
423
+ languages,
424
+ runnerType,
425
+ endpoints: [
426
+ `GET /repos/${owner}/${repo}/code-quality/setup`,
427
+ `PATCH /repos/${owner}/${repo}/code-quality/setup`,
428
+ ],
429
+ gates: [
430
+ 'retrieve current GitHub Code Quality setup before changing repository settings',
431
+ 'enable only supported repository languages and runner type',
432
+ 'keep CodeQL, Sonar, and ThumbGate pre-action gates as complementary controls',
433
+ 'record API response, schedule, and status-check evidence after enablement',
434
+ ],
435
+ nextActions: previewAvailable
436
+ ? ['GET current Code Quality setup', 'PATCH default setup if disabled', 'Verify status checks appear on the next PR']
437
+ : ['Keep existing CodeQL/Sonar checks and retry when GitHub preview is available to the account'],
438
+ };
439
+ }
440
+
441
+ function buildMediaAssetGovernancePlan(input = {}) {
442
+ const assets = toList(input.assets || input.prompts || input.campaigns);
443
+ const assetTypes = toList(input.assetTypes || input.types || input.formats);
444
+ const brandKit = Boolean(input.brandKit || input.brand);
445
+ const rightsProof = Boolean(input.rightsProof || input.licensedInputs);
446
+ const claimReview = Boolean(input.claimReview || input.proofReview);
447
+ const dynamicSubtitles = Boolean(input.dynamicSubtitles || input.subtitles || input.captions);
448
+ const ready = assets.length > 0 && brandKit && rightsProof && claimReview;
449
+
450
+ return {
451
+ name: 'thumbgate-media-asset-governance-plan',
452
+ status: ready ? 'ready-to-generate-assets' : 'block-unreviewed-media-generation',
453
+ assets,
454
+ assetTypes,
455
+ dynamicSubtitles,
456
+ gates: [
457
+ 'require brand kit, audience, and campaign goal before image or video generation',
458
+ 'require rights proof for inputs, logos, screenshots, voices, and music',
459
+ 'block unsupported product claims in generated ad copy or captions',
460
+ 'require caption/subtitle review for short-form video before publishing',
461
+ 'keep product mockups and ad variants tied to the same source-of-truth pricing and capability claims',
462
+ 'store prompt, model, output asset path, approver, and publish destination in an asset receipt',
463
+ ],
464
+ nextActions: ready
465
+ ? ['Generate draft assets in a sandbox workspace', 'Human-review claims and rights before publishing']
466
+ : ['Add brand, rights, and claim-review evidence before using Runway or other media MCPs'],
467
+ };
468
+ }
469
+
470
+ function buildOutputFormatPlan(input = {}) {
471
+ const audience = input.audience || 'operator';
472
+ const artifactType = input.artifactType || input.type || 'report';
473
+ const interactive = Boolean(input.interactive || input.dashboard || input.comparison);
474
+ const repoNative = Boolean(input.repoNative || input.markdown || input.auditLog);
475
+ const format = interactive && !repoNative ? 'html' : 'markdown';
476
+
477
+ return {
478
+ name: 'thumbgate-output-format-plan',
479
+ status: `use-${format}`,
480
+ audience,
481
+ artifactType,
482
+ format,
483
+ gates: [
484
+ 'use HTML for dense decision reports, comparison grids, dashboards, visual QA, and interactive review surfaces',
485
+ 'use Markdown for repo-native docs, audit logs, READMEs, commit notes, and text pipelines',
486
+ 'keep a text/Markdown source or export path when HTML becomes the human-facing artifact',
487
+ 'test generated HTML for broken links, overflow, accessibility labels, and stale claims before sharing',
488
+ ],
489
+ nextActions: format === 'html'
490
+ ? ['Render an HTML preview and run link/claim checks before sending']
491
+ : ['Keep output as Markdown and avoid decorative HTML that hurts diffs'],
492
+ };
493
+ }
494
+
495
+ function parseArgs(argv = process.argv.slice(2)) {
496
+ const args = {};
497
+ for (const arg of argv) {
498
+ if (arg === '--json') args.json = true;
499
+ else if (['loop', 'skills', 'batching', 'legal', 'dynamic', 'customize', 'pr', 'vector', 'memory', 'sandbox', 'egress', 'supply-chain', 'code-quality', 'media', 'format', 'gtm-agency', 'bedrock'].includes(arg)) args.command = arg;
500
+ else if (arg.startsWith('--tasks=')) args.tasks = arg.slice('--tasks='.length);
501
+ else if (arg.startsWith('--cadence-minutes=')) args.cadenceMinutes = arg.slice('--cadence-minutes='.length);
502
+ else if (arg === '--routine' || arg === '--server-hosted') args.serverHosted = true;
503
+ else if (arg.startsWith('--skills=')) args.skills = arg.slice('--skills='.length);
504
+ else if (arg.startsWith('--allowed-tools=')) args.allowedTools = arg.slice('--allowed-tools='.length);
505
+ else if (arg === '--needs-write') args.needsWrite = true;
506
+ else if (arg.startsWith('--users=')) args.concurrentUsers = arg.slice('--users='.length);
507
+ else if (arg === '--gpu-hosted') args.gpuHosted = true;
508
+ else if (arg.startsWith('--matter=')) args.matterId = arg.slice('--matter='.length);
509
+ else if (arg.startsWith('--agents=')) args.agents = arg.slice('--agents='.length);
510
+ else if (arg.startsWith('--actions=')) args.actions = arg.slice('--actions='.length);
511
+ else if (arg === '--privileged') args.privileged = true;
512
+ else if (arg.startsWith('--task=')) args.task = arg.slice('--task='.length);
513
+ else if (arg.startsWith('--success-criteria=')) args.successCriteria = arg.slice('--success-criteria='.length);
514
+ else if (arg.startsWith('--parallel-agents=')) args.parallelAgents = arg.slice('--parallel-agents='.length);
515
+ else if (arg.startsWith('--token-budget=')) args.tokenBudget = arg.slice('--token-budget='.length);
516
+ else if (arg === '--needs-verifier') args.needsVerifier = true;
517
+ else if (arg.startsWith('--workload=')) args.workload = arg.slice('--workload='.length);
518
+ else if (arg.startsWith('--signals=')) args.proprietarySignals = arg.slice('--signals='.length);
519
+ else if (arg === '--runtime-encoding') args.runtimeEncoding = true;
520
+ else if (arg.startsWith('--baseline-cost=')) args.baselineCost = arg.slice('--baseline-cost='.length);
521
+ else if (arg === '--has-benchmark') args.hasBenchmark = true;
522
+ else if (arg.startsWith('--proof-assets=')) args.proofAssets = arg.slice('--proof-assets='.length);
523
+ else if (arg.startsWith('--earned-mentions=')) args.earnedMentions = arg.slice('--earned-mentions='.length);
524
+ else if (arg === '--bursty') args.bursty = true;
525
+ else if (arg.startsWith('--idle-hours=')) args.idleHours = arg.slice('--idle-hours='.length);
526
+ else if (arg === '--managed-endpoint') args.managedEndpoint = true;
527
+ else if (arg === '--sensitive-local-data') args.sensitiveLocalData = true;
528
+ else if (arg.startsWith('--memories=')) args.memories = arg.slice('--memories='.length);
529
+ else if (arg === '--source-pointers') args.sourcePointers = true;
530
+ else if (arg.startsWith('--manifest-entries=')) args.manifestEntries = arg.slice('--manifest-entries='.length);
531
+ else if (arg.startsWith('--output-dirs=')) args.outputDirs = arg.slice('--output-dirs='.length);
532
+ else if (arg.startsWith('--sandbox-provider=')) args.sandboxProvider = arg.slice('--sandbox-provider='.length);
533
+ else if (arg === '--checkpointing') args.checkpointing = true;
534
+ else if (arg === '--secrets-in-sandbox') args.secretsInSandbox = true;
535
+ else if (arg.startsWith('--allowed-domains=')) args.allowedDomains = arg.slice('--allowed-domains='.length);
536
+ else if (arg.startsWith('--observed-requests=')) args.observedRequests = arg.slice('--observed-requests='.length);
537
+ else if (arg === '--live-dashboard') args.liveDashboard = true;
538
+ else if (arg.startsWith('--sources=')) args.sources = arg.slice('--sources='.length);
539
+ else if (arg === '--sandbox') args.sandbox = true;
540
+ else if (arg === '--ai-audit') args.aiAudit = true;
541
+ else if (arg === '--auto-updates-disabled') args.autoUpdatesDisabled = true;
542
+ else if (arg === '--install-scripts-disabled' || arg === '--ignore-scripts') args.installScriptsDisabled = true;
543
+ else if (arg.startsWith('--exfil-targets=')) args.exfilTargets = arg.slice('--exfil-targets='.length);
544
+ else if (arg.startsWith('--owner=')) args.owner = arg.slice('--owner='.length);
545
+ else if (arg.startsWith('--repo=')) args.repo = arg.slice('--repo='.length);
546
+ else if (arg.startsWith('--languages=')) args.languages = arg.slice('--languages='.length);
547
+ else if (arg.startsWith('--runner-type=')) args.runnerType = arg.slice('--runner-type='.length);
548
+ else if (arg.startsWith('--assets=')) args.assets = arg.slice('--assets='.length);
549
+ else if (arg.startsWith('--asset-types=')) args.assetTypes = arg.slice('--asset-types='.length);
550
+ else if (arg === '--brand-kit') args.brandKit = true;
551
+ else if (arg === '--rights-proof') args.rightsProof = true;
552
+ else if (arg === '--claim-review') args.claimReview = true;
553
+ else if (arg === '--dynamic-subtitles' || arg === '--captions') args.dynamicSubtitles = true;
554
+ else if (arg.startsWith('--artifact-type=')) args.artifactType = arg.slice('--artifact-type='.length);
555
+ else if (arg === '--interactive') args.interactive = true;
556
+ else if (arg === '--repo-native') args.repoNative = true;
557
+ else if (arg.startsWith('--channels=')) args.channels = arg.slice('--channels='.length);
558
+ else if (arg.startsWith('--monthly-retainer=')) args.monthlyRetainer = arg.slice('--monthly-retainer='.length);
559
+ else if (arg === '--crm-automation') args.crmAutomation = true;
560
+ else if (arg === '--scheduled-content') args.scheduledContent = true;
561
+ else if (arg === '--quality-review') args.qualityReview = true;
562
+ else if (arg.startsWith('--frameworks=')) args.frameworks = arg.slice('--frameworks='.length);
563
+ else if (arg === '--serverless') args.serverless = true;
564
+ else if (arg === '--agentcore-memory') args.memory = true;
565
+ else if (arg === '--agentcore-observability') args.observability = true;
566
+ else if (arg === '--agentcore-identity') args.identity = true;
567
+ }
568
+ return args;
569
+ }
570
+
571
+ function runCli(args) {
572
+ const command = args.command || 'loop';
573
+ const builders = {
574
+ skills: buildSkillFirstAgentPlan,
575
+ batching: buildContinuousBatchingPlan,
576
+ legal: buildLegalAgentGovernancePlan,
577
+ dynamic: buildDynamicWorkflowReadinessPlan,
578
+ customize: buildOpenModelCustomizationPlan,
579
+ pr: buildDigitalPrCitationPlan,
580
+ vector: buildServerlessVectorPlan,
581
+ memory: buildMemoryModelPlan,
582
+ sandbox: buildSandboxManifestPlan,
583
+ egress: buildNetworkEgressFirewallPlan,
584
+ 'supply-chain': buildSupplyChainVettingPlan,
585
+ 'code-quality': buildCodeQualityEnablementPlan,
586
+ media: buildMediaAssetGovernancePlan,
587
+ format: buildOutputFormatPlan,
588
+ 'gtm-agency': buildMarketingAgencyGtmPlan,
589
+ bedrock: buildBedrockAgentCorePlan,
590
+ loop: buildLoopRoutinePlan,
591
+ };
592
+ const report = (builders[command] || buildLoopRoutinePlan)(args);
593
+
594
+ if (args.json) console.log(JSON.stringify(report, null, 2));
595
+ else {
596
+ console.log(`${report.name}: ${report.status}`);
597
+ for (const action of report.nextActions || []) console.log(`- ${action}`);
598
+ }
599
+ }
600
+
601
+ if (require.main === module) runCli(parseArgs());
602
+
603
+ module.exports = {
604
+ buildLoopRoutinePlan,
605
+ buildSkillFirstAgentPlan,
606
+ buildContinuousBatchingPlan,
607
+ buildOpenModelCustomizationPlan,
608
+ buildDigitalPrCitationPlan,
609
+ buildServerlessVectorPlan,
610
+ buildMemoryModelPlan,
611
+ buildSandboxManifestPlan,
612
+ buildNetworkEgressFirewallPlan,
613
+ buildSupplyChainVettingPlan,
614
+ buildCodeQualityEnablementPlan,
615
+ buildMediaAssetGovernancePlan,
616
+ buildOutputFormatPlan,
617
+ buildMarketingAgencyGtmPlan,
618
+ buildBedrockAgentCorePlan,
619
+ buildLegalAgentGovernancePlan,
620
+ buildDynamicWorkflowReadinessPlan,
621
+ };