thumbgate 0.9.10

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 (364) hide show
  1. package/.claude-plugin/README.md +134 -0
  2. package/.claude-plugin/bundle/icon.png +0 -0
  3. package/.claude-plugin/bundle/icon.svg +18 -0
  4. package/.claude-plugin/bundle/server/index.js +24 -0
  5. package/.claude-plugin/marketplace.json +36 -0
  6. package/.claude-plugin/plugin.json +21 -0
  7. package/.well-known/mcp/server-card.json +231 -0
  8. package/LICENSE +21 -0
  9. package/README.md +375 -0
  10. package/adapters/README.md +9 -0
  11. package/adapters/amp/skills/thumbgate-feedback/SKILL.md +22 -0
  12. package/adapters/chatgpt/INSTALL.md +83 -0
  13. package/adapters/chatgpt/openapi.yaml +1281 -0
  14. package/adapters/claude/.mcp.json +14 -0
  15. package/adapters/codex/config.toml +9 -0
  16. package/adapters/gemini/function-declarations.json +224 -0
  17. package/adapters/mcp/server-stdio.js +788 -0
  18. package/adapters/opencode/opencode.json +15 -0
  19. package/bin/cli.js +1484 -0
  20. package/bin/memory.sh +64 -0
  21. package/bin/obsidian-sync.sh +20 -0
  22. package/bin/postinstall.js +37 -0
  23. package/config/build-metadata.json +4 -0
  24. package/config/e2e-critical-flows.json +45 -0
  25. package/config/gate-templates.json +77 -0
  26. package/config/gates/claim-verification.json +29 -0
  27. package/config/gates/computer-use.json +39 -0
  28. package/config/gates/default.json +117 -0
  29. package/config/github-about.json +25 -0
  30. package/config/mcp-allowlists.json +135 -0
  31. package/config/model-tiers.json +33 -0
  32. package/config/partner-routing.json +132 -0
  33. package/config/policy-bundles/constrained-v1.json +64 -0
  34. package/config/policy-bundles/default-v1.json +91 -0
  35. package/config/rubrics/default-v1.json +52 -0
  36. package/config/skill-packs/react-testing.json +23 -0
  37. package/config/skill-packs/stripe-integration/references/api-spec.json +1 -0
  38. package/config/skill-packs/stripe-integration/references/webhook-guide.md +3 -0
  39. package/config/skill-specs/pr-reviewer.json +9 -0
  40. package/config/skill-specs/release-status.json +9 -0
  41. package/config/skill-specs/ticket-triage.json +9 -0
  42. package/config/subagent-profiles.json +32 -0
  43. package/config/tessl-tiles.json +29 -0
  44. package/config/thumbgate-settings.managed.json +12 -0
  45. package/openapi/openapi.yaml +1281 -0
  46. package/package.json +283 -0
  47. package/plugins/amp-skill/INSTALL.md +52 -0
  48. package/plugins/amp-skill/SKILL.md +64 -0
  49. package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +22 -0
  50. package/plugins/claude-codex-bridge/.mcp.json +12 -0
  51. package/plugins/claude-codex-bridge/INSTALL.md +43 -0
  52. package/plugins/claude-codex-bridge/README.md +46 -0
  53. package/plugins/claude-codex-bridge/scripts/codex-bridge.js +288 -0
  54. package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +24 -0
  55. package/plugins/claude-codex-bridge/skills/result/SKILL.md +22 -0
  56. package/plugins/claude-codex-bridge/skills/review/SKILL.md +28 -0
  57. package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +27 -0
  58. package/plugins/claude-codex-bridge/skills/setup/SKILL.md +21 -0
  59. package/plugins/claude-codex-bridge/skills/status/SKILL.md +19 -0
  60. package/plugins/claude-skill/INSTALL.md +55 -0
  61. package/plugins/claude-skill/SKILL.md +46 -0
  62. package/plugins/codex-profile/.codex-plugin/plugin.json +43 -0
  63. package/plugins/codex-profile/.mcp.json +12 -0
  64. package/plugins/codex-profile/AGENTS.md +20 -0
  65. package/plugins/codex-profile/INSTALL.md +66 -0
  66. package/plugins/codex-profile/README.md +37 -0
  67. package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +23 -0
  68. package/plugins/cursor-marketplace/CHANGELOG.md +30 -0
  69. package/plugins/cursor-marketplace/LICENSE +21 -0
  70. package/plugins/cursor-marketplace/README.md +124 -0
  71. package/plugins/cursor-marketplace/agents/reliability-reviewer.md +31 -0
  72. package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
  73. package/plugins/cursor-marketplace/commands/capture-feedback.md +33 -0
  74. package/plugins/cursor-marketplace/commands/check-gates.md +25 -0
  75. package/plugins/cursor-marketplace/commands/show-lessons.md +27 -0
  76. package/plugins/cursor-marketplace/hooks/hooks.json +10 -0
  77. package/plugins/cursor-marketplace/mcp.json +12 -0
  78. package/plugins/cursor-marketplace/rules/feedback-capture.mdc +34 -0
  79. package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +30 -0
  80. package/plugins/cursor-marketplace/rules/session-continuity.mdc +28 -0
  81. package/plugins/cursor-marketplace/scripts/gate-check.sh +11 -0
  82. package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +47 -0
  83. package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +31 -0
  84. package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +30 -0
  85. package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +33 -0
  86. package/plugins/gemini-extension/INSTALL.md +92 -0
  87. package/plugins/gemini-extension/gemini_prompt.txt +14 -0
  88. package/plugins/gemini-extension/tool_contract.json +45 -0
  89. package/plugins/opencode-profile/INSTALL.md +57 -0
  90. package/public/assets/instagram-card.png +0 -0
  91. package/public/assets/tiktok-agent-memory.mp4 +0 -0
  92. package/public/blog.html +400 -0
  93. package/public/dashboard.html +1093 -0
  94. package/public/guide.html +317 -0
  95. package/public/index.html +1014 -0
  96. package/public/learn/agent-harness-pattern.html +180 -0
  97. package/public/learn/ai-agent-persistent-memory.html +202 -0
  98. package/public/learn/learn.css +45 -0
  99. package/public/learn/mcp-pre-action-gates-explained.html +172 -0
  100. package/public/learn/stop-ai-agent-force-push.html +134 -0
  101. package/public/learn/vibe-coding-safety-net.html +142 -0
  102. package/public/learn.html +213 -0
  103. package/public/lessons.html +650 -0
  104. package/public/vercel.json +8 -0
  105. package/scripts/__pycache__/train_from_feedback.cpython-312.pyc +0 -0
  106. package/scripts/a2ui-engine.js +73 -0
  107. package/scripts/access-anomaly-detector.js +12 -0
  108. package/scripts/adk-consolidator.js +266 -0
  109. package/scripts/agent-readiness.js +220 -0
  110. package/scripts/agent-security-hardening.js +227 -0
  111. package/scripts/agentic-data-pipeline.js +847 -0
  112. package/scripts/analytics-report.js +328 -0
  113. package/scripts/analytics-window.js +158 -0
  114. package/scripts/async-job-runner.js +1001 -0
  115. package/scripts/audit-trail.js +398 -0
  116. package/scripts/auto-promote-gates.js +299 -0
  117. package/scripts/auto-wire-hooks.js +312 -0
  118. package/scripts/autonomous-sales-agent.js +39 -0
  119. package/scripts/autoresearch-runner.js +216 -0
  120. package/scripts/background-agent-governance.js +237 -0
  121. package/scripts/behavioral-extraction.js +97 -0
  122. package/scripts/belief-update.js +84 -0
  123. package/scripts/billing.js +2438 -0
  124. package/scripts/bot-detector.js +50 -0
  125. package/scripts/budget-guard.js +173 -0
  126. package/scripts/build-claude-mcpb.js +189 -0
  127. package/scripts/build-metadata.js +97 -0
  128. package/scripts/check-congruence.js +322 -0
  129. package/scripts/cli-feedback.js +135 -0
  130. package/scripts/cli-telemetry.js +87 -0
  131. package/scripts/cloudflare-dynamic-sandbox.js +315 -0
  132. package/scripts/code-reasoning.js +350 -0
  133. package/scripts/codegraph-context.js +466 -0
  134. package/scripts/commercial-offer.js +56 -0
  135. package/scripts/computer-use-firewall.js +250 -0
  136. package/scripts/context-engine.js +694 -0
  137. package/scripts/contextfs.js +1287 -0
  138. package/scripts/conversation-context.js +119 -0
  139. package/scripts/creator-campaigns.js +239 -0
  140. package/scripts/daemon-manager.js +108 -0
  141. package/scripts/daily-digest.js +11 -0
  142. package/scripts/dashboard-render-spec.js +395 -0
  143. package/scripts/dashboard.js +1058 -0
  144. package/scripts/data-governance.js +173 -0
  145. package/scripts/delegation-runtime.js +900 -0
  146. package/scripts/deploy-gcp.sh +44 -0
  147. package/scripts/deploy-policy.js +263 -0
  148. package/scripts/disagreement-mining.js +315 -0
  149. package/scripts/dispatch-brief.js +159 -0
  150. package/scripts/distribution-surfaces.js +44 -0
  151. package/scripts/dpo-optimizer.js +209 -0
  152. package/scripts/ephemeral-agent-store.js +219 -0
  153. package/scripts/eval-harness.js +56 -0
  154. package/scripts/evolution-state.js +241 -0
  155. package/scripts/experiment-tracker.js +267 -0
  156. package/scripts/export-databricks-bundle.js +242 -0
  157. package/scripts/export-dpo-pairs.js +345 -0
  158. package/scripts/export-kto-pairs.js +310 -0
  159. package/scripts/export-training.js +448 -0
  160. package/scripts/failure-diagnostics.js +558 -0
  161. package/scripts/feedback-attribution.js +313 -0
  162. package/scripts/feedback-fallback.js +111 -0
  163. package/scripts/feedback-history-distiller.js +391 -0
  164. package/scripts/feedback-inbox-read.js +162 -0
  165. package/scripts/feedback-loop.js +1887 -0
  166. package/scripts/feedback-paths.js +145 -0
  167. package/scripts/feedback-quality.js +139 -0
  168. package/scripts/feedback-root-consolidator.js +238 -0
  169. package/scripts/feedback-schema.js +426 -0
  170. package/scripts/feedback-session.js +286 -0
  171. package/scripts/feedback-to-memory.js +185 -0
  172. package/scripts/feedback-to-rules.js +163 -0
  173. package/scripts/filesystem-search.js +404 -0
  174. package/scripts/funnel-analytics.js +35 -0
  175. package/scripts/gate-satisfy.js +42 -0
  176. package/scripts/gate-stats.js +116 -0
  177. package/scripts/gate-templates.js +70 -0
  178. package/scripts/gates-engine.js +816 -0
  179. package/scripts/generate-paperbanana-diagrams.sh +99 -0
  180. package/scripts/generate-pretool-hook.sh +40 -0
  181. package/scripts/github-about.js +350 -0
  182. package/scripts/github-outreach.js +65 -0
  183. package/scripts/gtm-revenue-loop.js +520 -0
  184. package/scripts/hallucination-detector.js +226 -0
  185. package/scripts/hf-papers.js +317 -0
  186. package/scripts/history-distiller.js +200 -0
  187. package/scripts/hook-auto-capture.sh +95 -0
  188. package/scripts/hook-stop-pr-thread-check.sh +68 -0
  189. package/scripts/hook-stop-self-score.sh +51 -0
  190. package/scripts/hook-stop-verify-deploy.sh +31 -0
  191. package/scripts/hook-thumbgate-cache-updater.js +48 -0
  192. package/scripts/hook-verify-before-done.sh +20 -0
  193. package/scripts/hosted-config.js +170 -0
  194. package/scripts/hybrid-feedback-context.js +676 -0
  195. package/scripts/install-mcp.js +159 -0
  196. package/scripts/intent-router.js +392 -0
  197. package/scripts/internal-agent-bootstrap.js +490 -0
  198. package/scripts/jsonl-watcher.js +155 -0
  199. package/scripts/lesson-db.js +613 -0
  200. package/scripts/lesson-inference.js +315 -0
  201. package/scripts/lesson-retrieval.js +95 -0
  202. package/scripts/lesson-rotation.js +137 -0
  203. package/scripts/lesson-search.js +644 -0
  204. package/scripts/lesson-synthesis.js +196 -0
  205. package/scripts/license.js +50 -0
  206. package/scripts/local-model-profile.js +383 -0
  207. package/scripts/markdown-escape.js +12 -0
  208. package/scripts/marketing-experiment.js +671 -0
  209. package/scripts/mcp-config.js +149 -0
  210. package/scripts/mcp-policy.js +99 -0
  211. package/scripts/memalign-recall.js +111 -0
  212. package/scripts/memory-firewall.js +222 -0
  213. package/scripts/memory-migration.js +296 -0
  214. package/scripts/meta-policy.js +194 -0
  215. package/scripts/metered-billing.js +16 -0
  216. package/scripts/model-tier-router.js +301 -0
  217. package/scripts/money-watcher.js +71 -0
  218. package/scripts/multi-hop-recall.js +240 -0
  219. package/scripts/natural-language-harness.js +330 -0
  220. package/scripts/obsidian-export.js +712 -0
  221. package/scripts/operational-dashboard.js +103 -0
  222. package/scripts/operational-summary.js +93 -0
  223. package/scripts/optimize-context.js +17 -0
  224. package/scripts/org-dashboard.js +201 -0
  225. package/scripts/partner-orchestration.js +146 -0
  226. package/scripts/per-step-scoring.js +165 -0
  227. package/scripts/perplexity-marketing.js +466 -0
  228. package/scripts/pii-scanner.js +153 -0
  229. package/scripts/plan-gate.js +154 -0
  230. package/scripts/post-everywhere.js +308 -0
  231. package/scripts/post-to-x-retry.sh +22 -0
  232. package/scripts/post-to-x.js +369 -0
  233. package/scripts/pr-manager.js +236 -0
  234. package/scripts/predictive-insights.js +356 -0
  235. package/scripts/principle-extractor.js +162 -0
  236. package/scripts/pro-features.js +40 -0
  237. package/scripts/pro-local-dashboard.js +174 -0
  238. package/scripts/problem-detail.js +53 -0
  239. package/scripts/product-feedback.js +134 -0
  240. package/scripts/profile-router.js +245 -0
  241. package/scripts/prompt-dlp.js +221 -0
  242. package/scripts/prompt-guard.js +83 -0
  243. package/scripts/prove-adapters.js +863 -0
  244. package/scripts/prove-attribution.js +365 -0
  245. package/scripts/prove-automation.js +653 -0
  246. package/scripts/prove-autoresearch.js +304 -0
  247. package/scripts/prove-claim-verification.js +277 -0
  248. package/scripts/prove-cloudflare-sandbox.js +163 -0
  249. package/scripts/prove-data-pipeline.js +410 -0
  250. package/scripts/prove-data-quality.js +227 -0
  251. package/scripts/prove-evolution.js +352 -0
  252. package/scripts/prove-harnesses.js +287 -0
  253. package/scripts/prove-intelligence.js +259 -0
  254. package/scripts/prove-lancedb.js +371 -0
  255. package/scripts/prove-local-intelligence.js +342 -0
  256. package/scripts/prove-loop-closure.js +263 -0
  257. package/scripts/prove-predictive-insights.js +357 -0
  258. package/scripts/prove-runtime.js +350 -0
  259. package/scripts/prove-seo-gsd.js +234 -0
  260. package/scripts/prove-settings.js +279 -0
  261. package/scripts/prove-subway-upgrades.js +277 -0
  262. package/scripts/prove-tessl.js +229 -0
  263. package/scripts/prove-training-export.js +327 -0
  264. package/scripts/prove-workflow-contract.js +116 -0
  265. package/scripts/prove-xmemory.js +332 -0
  266. package/scripts/publish-decision.js +133 -0
  267. package/scripts/pulse.js +80 -0
  268. package/scripts/rate-limiter.js +125 -0
  269. package/scripts/reddit-dm-outreach.js +182 -0
  270. package/scripts/reddit-monitor-cron.sh +26 -0
  271. package/scripts/reflector-agent.js +221 -0
  272. package/scripts/reminder-engine.js +132 -0
  273. package/scripts/revenue-status.js +472 -0
  274. package/scripts/risk-scorer.js +458 -0
  275. package/scripts/rlaif-self-audit.js +129 -0
  276. package/scripts/rubric-engine.js +230 -0
  277. package/scripts/schedule-manager.js +251 -0
  278. package/scripts/secret-scanner.js +414 -0
  279. package/scripts/self-heal.js +147 -0
  280. package/scripts/self-healing-check.js +188 -0
  281. package/scripts/semantic-layer.js +98 -0
  282. package/scripts/seo-gsd.js +1153 -0
  283. package/scripts/settings-hierarchy.js +214 -0
  284. package/scripts/shieldcortex-memory-firewall-runner.mjs +53 -0
  285. package/scripts/skill-exporter.js +262 -0
  286. package/scripts/skill-generator.js +446 -0
  287. package/scripts/skill-materializer.js +134 -0
  288. package/scripts/skill-packs.js +136 -0
  289. package/scripts/skill-proposer.js +99 -0
  290. package/scripts/skill-quality-tracker.js +284 -0
  291. package/scripts/slo-alert-engine.js +14 -0
  292. package/scripts/slow-loop.js +72 -0
  293. package/scripts/social-analytics/db/schema.sql +32 -0
  294. package/scripts/social-analytics/digest.js +256 -0
  295. package/scripts/social-analytics/generate-instagram-card.js +97 -0
  296. package/scripts/social-analytics/instagram-thumbgate-post.js +73 -0
  297. package/scripts/social-analytics/mcp-server.js +289 -0
  298. package/scripts/social-analytics/normalizer.js +580 -0
  299. package/scripts/social-analytics/notify.js +162 -0
  300. package/scripts/social-analytics/poll-all.js +107 -0
  301. package/scripts/social-analytics/pollers/github.js +195 -0
  302. package/scripts/social-analytics/pollers/instagram.js +253 -0
  303. package/scripts/social-analytics/pollers/linkedin.js +330 -0
  304. package/scripts/social-analytics/pollers/plausible.js +247 -0
  305. package/scripts/social-analytics/pollers/reddit.js +306 -0
  306. package/scripts/social-analytics/pollers/threads.js +233 -0
  307. package/scripts/social-analytics/pollers/tiktok.js +203 -0
  308. package/scripts/social-analytics/pollers/x.js +227 -0
  309. package/scripts/social-analytics/pollers/youtube.js +304 -0
  310. package/scripts/social-analytics/pollers/zernio.js +180 -0
  311. package/scripts/social-analytics/publish-instagram-thumbgate.js +85 -0
  312. package/scripts/social-analytics/publishers/devto.js +122 -0
  313. package/scripts/social-analytics/publishers/instagram.js +317 -0
  314. package/scripts/social-analytics/publishers/linkedin.js +294 -0
  315. package/scripts/social-analytics/publishers/reddit.js +390 -0
  316. package/scripts/social-analytics/publishers/threads.js +275 -0
  317. package/scripts/social-analytics/publishers/tiktok.js +217 -0
  318. package/scripts/social-analytics/publishers/x.js +259 -0
  319. package/scripts/social-analytics/publishers/youtube.js +223 -0
  320. package/scripts/social-analytics/publishers/zernio.js +209 -0
  321. package/scripts/social-analytics/run-digest.js +34 -0
  322. package/scripts/social-analytics/store.js +257 -0
  323. package/scripts/social-analytics/utm.js +143 -0
  324. package/scripts/social-pipeline.js +2628 -0
  325. package/scripts/social-quality-gate.js +18 -0
  326. package/scripts/social-reply-monitor.js +445 -0
  327. package/scripts/status-dashboard.js +155 -0
  328. package/scripts/statusline-lesson.js +16 -0
  329. package/scripts/statusline-tower.js +8 -0
  330. package/scripts/statusline.sh +116 -0
  331. package/scripts/stripe-live-status.js +115 -0
  332. package/scripts/subagent-profiles.js +79 -0
  333. package/scripts/sync-gh-secrets-from-env.sh +70 -0
  334. package/scripts/sync-github-about.js +52 -0
  335. package/scripts/sync-version.js +451 -0
  336. package/scripts/synthetic-dpo.js +234 -0
  337. package/scripts/telemetry-analytics.js +821 -0
  338. package/scripts/tessl-export.js +371 -0
  339. package/scripts/test-coverage.js +120 -0
  340. package/scripts/thompson-sampling.js +417 -0
  341. package/scripts/thumbgate-search.js +189 -0
  342. package/scripts/tool-kpi-tracker.js +12 -0
  343. package/scripts/tool-registry.js +811 -0
  344. package/scripts/train_from_feedback.py +910 -0
  345. package/scripts/user-profile.js +78 -0
  346. package/scripts/validate-feedback.js +580 -0
  347. package/scripts/validate-workflow-contract.js +287 -0
  348. package/scripts/vector-store.js +198 -0
  349. package/scripts/verification-loop.js +291 -0
  350. package/scripts/verify-obsidian-setup.sh +269 -0
  351. package/scripts/verify-run.js +269 -0
  352. package/scripts/webhook-delivery.js +62 -0
  353. package/scripts/weekly-auto-post.js +124 -0
  354. package/scripts/workflow-runs.js +154 -0
  355. package/scripts/workflow-sprint-intake.js +475 -0
  356. package/scripts/workspace-evolver.js +374 -0
  357. package/scripts/x-autonomous-marketing.js +139 -0
  358. package/scripts/xmemory-lite.js +405 -0
  359. package/skills/agent-memory/SKILL.md +97 -0
  360. package/skills/solve-architecture-autonomy/SKILL.md +17 -0
  361. package/skills/solve-architecture-autonomy/tool.js +33 -0
  362. package/skills/thumbgate/SKILL.md +114 -0
  363. package/skills/thumbgate-feedback/SKILL.md +49 -0
  364. package/src/api/server.js +4208 -0
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execFileSync } = require('child_process');
6
+
7
+ function isSourceCheckout(pkgRoot) {
8
+ return fs.existsSync(path.join(pkgRoot, '.git'));
9
+ }
10
+
11
+ function parseWorktreePaths(raw) {
12
+ return String(raw || '')
13
+ .split('\n')
14
+ .filter((line) => line.startsWith('worktree '))
15
+ .map((line) => line.slice('worktree '.length).trim())
16
+ .filter(Boolean);
17
+ }
18
+
19
+ function resolveStableSourceRoot(pkgRoot) {
20
+ if (!isSourceCheckout(pkgRoot)) {
21
+ return null;
22
+ }
23
+
24
+ try {
25
+ const output = execFileSync('git', ['-C', pkgRoot, 'worktree', 'list', '--porcelain'], {
26
+ encoding: 'utf8',
27
+ stdio: ['ignore', 'pipe', 'ignore'],
28
+ });
29
+ const worktreePaths = parseWorktreePaths(output);
30
+
31
+ for (const worktreePath of worktreePaths) {
32
+ const gitPath = path.join(worktreePath, '.git');
33
+ if (!fs.existsSync(gitPath)) {
34
+ continue;
35
+ }
36
+ if (fs.statSync(gitPath).isDirectory()) {
37
+ return worktreePath;
38
+ }
39
+ }
40
+ } catch (_) {
41
+ return pkgRoot;
42
+ }
43
+
44
+ return pkgRoot;
45
+ }
46
+
47
+ function resolveGitCommonDir(dirPath) {
48
+ try {
49
+ return execFileSync('git', ['-C', dirPath, 'rev-parse', '--path-format=absolute', '--git-common-dir'], {
50
+ encoding: 'utf8',
51
+ stdio: ['ignore', 'pipe', 'ignore'],
52
+ }).trim();
53
+ } catch (_) {
54
+ return null;
55
+ }
56
+ }
57
+
58
+ function isSameCheckoutFamily(pkgRoot, targetDir) {
59
+ const packageCommonDir = resolveGitCommonDir(pkgRoot);
60
+ const targetCommonDir = resolveGitCommonDir(targetDir);
61
+
62
+ if (packageCommonDir && targetCommonDir) {
63
+ return packageCommonDir === targetCommonDir;
64
+ }
65
+
66
+ const resolvedPkgRoot = path.resolve(pkgRoot);
67
+ const resolvedTargetDir = path.resolve(targetDir);
68
+ return resolvedTargetDir === resolvedPkgRoot || resolvedTargetDir.startsWith(`${resolvedPkgRoot}${path.sep}`);
69
+ }
70
+
71
+ function resolveLocalServerPath(pkgRoot, scope = 'project') {
72
+ const baseRoot = scope === 'home' ? resolveStableSourceRoot(pkgRoot) || pkgRoot : pkgRoot;
73
+ return path.join(baseRoot, 'adapters', 'mcp', 'server-stdio.js');
74
+ }
75
+
76
+ function portableMcpEntry(pkgVersion) {
77
+ return {
78
+ command: 'npx',
79
+ args: ['-y', `thumbgate@${pkgVersion}`, 'serve'],
80
+ };
81
+ }
82
+
83
+ function localMcpEntry(pkgRoot, scope = 'project') {
84
+ return {
85
+ command: 'node',
86
+ args: [resolveLocalServerPath(pkgRoot, scope)],
87
+ };
88
+ }
89
+
90
+ const publicationCache = new Map();
91
+
92
+ function publishedVersionOverride() {
93
+ const override = String(process.env.THUMBGATE_PUBLISH_STATE || '').trim().toLowerCase();
94
+ if (override === 'published') {
95
+ return true;
96
+ }
97
+ if (override === 'unpublished') {
98
+ return false;
99
+ }
100
+ return null;
101
+ }
102
+
103
+ function isVersionPublished(pkgVersion) {
104
+ const override = publishedVersionOverride();
105
+ if (override !== null) {
106
+ return override;
107
+ }
108
+ if (publicationCache.has(pkgVersion)) {
109
+ return publicationCache.get(pkgVersion);
110
+ }
111
+
112
+ let published = false;
113
+ try {
114
+ execFileSync('npm', ['view', `thumbgate@${pkgVersion}`, 'version'], {
115
+ encoding: 'utf8',
116
+ stdio: ['ignore', 'pipe', 'ignore'],
117
+ timeout: 5000,
118
+ });
119
+ published = true;
120
+ } catch (_) {
121
+ published = false;
122
+ }
123
+
124
+ publicationCache.set(pkgVersion, published);
125
+ return published;
126
+ }
127
+
128
+ function resolveMcpEntry({ pkgRoot, pkgVersion, scope = 'project', targetDir = pkgRoot }) {
129
+ if (!isSourceCheckout(pkgRoot)) {
130
+ return portableMcpEntry(pkgVersion);
131
+ }
132
+ if (scope === 'project' && !isSameCheckoutFamily(pkgRoot, targetDir) && isVersionPublished(pkgVersion)) {
133
+ return portableMcpEntry(pkgVersion);
134
+ }
135
+ return localMcpEntry(pkgRoot, scope);
136
+ }
137
+
138
+ module.exports = {
139
+ isVersionPublished,
140
+ isSourceCheckout,
141
+ isSameCheckoutFamily,
142
+ localMcpEntry,
143
+ parseWorktreePaths,
144
+ portableMcpEntry,
145
+ resolveGitCommonDir,
146
+ resolveLocalServerPath,
147
+ resolveMcpEntry,
148
+ resolveStableSourceRoot,
149
+ };
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const PROJECT_ROOT = path.join(__dirname, '..');
6
+ const DEFAULT_POLICY_PATH = path.join(PROJECT_ROOT, 'config', 'mcp-allowlists.json');
7
+ const DEFAULT_SUBAGENT_PROFILE_PATH = path.join(PROJECT_ROOT, 'config', 'subagent-profiles.json');
8
+
9
+ function getPolicyPath() {
10
+ return process.env.THUMBGATE_MCP_POLICY_PATH || DEFAULT_POLICY_PATH;
11
+ }
12
+
13
+ function loadMcpPolicy() {
14
+ const policyPath = getPolicyPath();
15
+ const raw = fs.readFileSync(policyPath, 'utf-8');
16
+ const parsed = JSON.parse(raw);
17
+ if (!parsed.profiles || typeof parsed.profiles !== 'object') {
18
+ throw new Error('Invalid MCP policy: missing profiles object');
19
+ }
20
+ return parsed;
21
+ }
22
+
23
+ function loadSubagentProfiles() {
24
+ const profilePath = process.env.THUMBGATE_SUBAGENT_PROFILE_PATH || DEFAULT_SUBAGENT_PROFILE_PATH;
25
+ const raw = fs.readFileSync(profilePath, 'utf-8');
26
+ const parsed = JSON.parse(raw);
27
+ if (!parsed.profiles || typeof parsed.profiles !== 'object') {
28
+ throw new Error('Invalid subagent profile config: missing profiles object');
29
+ }
30
+ return parsed;
31
+ }
32
+
33
+ function getActiveMcpProfile(toolName) {
34
+ const explicitProfile = process.env.THUMBGATE_MCP_PROFILE || null;
35
+ const runtimeSubagentProfile = process.env.THUMBGATE_SUBAGENT_PROFILE || null;
36
+ const autoRoute = process.env.THUMBGATE_AUTO_PROFILE_ROUTING !== 'false';
37
+
38
+ if (!runtimeSubagentProfile) {
39
+ // Auto-route when no explicit profile and auto-routing is enabled
40
+ if (!explicitProfile && autoRoute && toolName) {
41
+ const { routeProfile } = require('./profile-router');
42
+ const routing = routeProfile({ toolName });
43
+ return routing.profile;
44
+ }
45
+ return explicitProfile || 'default';
46
+ }
47
+
48
+ const config = loadSubagentProfiles();
49
+ const subagent = config.profiles[runtimeSubagentProfile];
50
+ if (!subagent || !subagent.mcpProfile) {
51
+ throw new Error(`Unknown subagent profile: ${runtimeSubagentProfile}`);
52
+ }
53
+
54
+ if (explicitProfile && explicitProfile !== subagent.mcpProfile) {
55
+ throw new Error(
56
+ `MCP profile conflict: THUMBGATE_MCP_PROFILE='${explicitProfile}' does not match subagent profile '${runtimeSubagentProfile}' (${subagent.mcpProfile})`,
57
+ );
58
+ }
59
+
60
+ return subagent.mcpProfile;
61
+ }
62
+
63
+ function getAllowedTools(profileName = getActiveMcpProfile()) {
64
+ const policy = loadMcpPolicy();
65
+ const tools = policy.profiles[profileName];
66
+ if (!tools) {
67
+ throw new Error(`Unknown MCP profile: ${profileName}`);
68
+ }
69
+ return tools;
70
+ }
71
+
72
+ function isToolAllowed(toolName, profileName = getActiveMcpProfile()) {
73
+ const allowed = getAllowedTools(profileName);
74
+ return allowed.includes(toolName);
75
+ }
76
+
77
+ function assertToolAllowed(toolName, profileName = getActiveMcpProfile()) {
78
+ if (!isToolAllowed(toolName, profileName)) {
79
+ throw new Error(`Tool '${toolName}' is not allowed in MCP profile '${profileName}'`);
80
+ }
81
+ }
82
+
83
+ module.exports = {
84
+ DEFAULT_POLICY_PATH,
85
+ getPolicyPath,
86
+ loadMcpPolicy,
87
+ loadSubagentProfiles,
88
+ getActiveMcpProfile,
89
+ getAllowedTools,
90
+ isToolAllowed,
91
+ assertToolAllowed,
92
+ DEFAULT_SUBAGENT_PROFILE_PATH,
93
+ };
94
+
95
+ if (require.main === module) {
96
+ const profile = getActiveMcpProfile();
97
+ const tools = getAllowedTools(profile);
98
+ console.log(JSON.stringify({ profile, tools }, null, 2));
99
+ }
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MemAlign dual-memory working memory construction.
5
+ *
6
+ * Combines:
7
+ * - Semantic memory (principles distilled from NL feedback)
8
+ * - Episodic memory (contextfs context packs)
9
+ *
10
+ * Budget split: 20% chars for principles, 80% for episodes.
11
+ */
12
+
13
+ const { constructContextPack } = require('./contextfs');
14
+ const { getPrinciples } = require('./principle-extractor');
15
+
16
+ /**
17
+ * Build a dual-memory working memory object.
18
+ *
19
+ * @param {object} opts
20
+ * @param {string} opts.query - Recall query.
21
+ * @param {number} [opts.maxItems=10] - Max episodic items.
22
+ * @param {number} [opts.maxChars=4000] - Total char budget.
23
+ * @param {string[]} [opts.namespaces] - Episodic search namespaces.
24
+ * @param {number} [opts.maxPrinciples=5] - Max principles to include.
25
+ * @returns {{ principles: object[], episodes: object, charBudget: { total: number, principles: number, episodes: number } }}
26
+ */
27
+ function constructWorkingMemory({ query, maxItems = 10, maxChars = 4000, namespaces, maxPrinciples = 5 } = {}) {
28
+ const principlesBudget = Math.floor(maxChars * 0.2);
29
+ const episodesBudget = maxChars - principlesBudget;
30
+
31
+ // Retrieve principles (semantic memory)
32
+ const allPrinciples = getPrinciples({ limit: maxPrinciples });
33
+
34
+ // Trim principles to char budget
35
+ const selectedPrinciples = [];
36
+ let usedPrincipleChars = 0;
37
+ for (const p of allPrinciples) {
38
+ const len = (p.text || '').length;
39
+ if (usedPrincipleChars + len > principlesBudget) break;
40
+ selectedPrinciples.push(p);
41
+ usedPrincipleChars += len;
42
+ }
43
+
44
+ // Retrieve episodic context pack
45
+ const episodes = constructContextPack({
46
+ query: query || '',
47
+ maxItems,
48
+ maxChars: episodesBudget,
49
+ namespaces,
50
+ });
51
+
52
+ return {
53
+ principles: selectedPrinciples,
54
+ episodes,
55
+ charBudget: {
56
+ total: maxChars,
57
+ principles: principlesBudget,
58
+ episodes: episodesBudget,
59
+ },
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Format a working memory object as markdown for context injection.
65
+ *
66
+ * @param {object} wm - Working memory from constructWorkingMemory.
67
+ * @returns {string} Markdown string.
68
+ */
69
+ function formatWorkingMemoryForContext(wm) {
70
+ const lines = [];
71
+
72
+ lines.push('## Principles (Semantic Memory)');
73
+ lines.push('');
74
+ if (wm.principles && wm.principles.length > 0) {
75
+ for (const p of wm.principles) {
76
+ lines.push(`- ${p.text}`);
77
+ }
78
+ } else {
79
+ lines.push('_No principles extracted yet._');
80
+ }
81
+
82
+ lines.push('');
83
+ lines.push('## Relevant Past Episodes (Episodic Memory)');
84
+ lines.push('');
85
+ if (wm.episodes && wm.episodes.retrieval && wm.episodes.retrieval.strategy === 'hierarchical') {
86
+ const themes = Array.isArray(wm.episodes.retrieval.selectedThemes)
87
+ ? wm.episodes.retrieval.selectedThemes.map((theme) => theme.replace(/-/g, ' '))
88
+ : [];
89
+ if (themes.length > 0) {
90
+ lines.push(`Themes: ${themes.join(', ')}`);
91
+ lines.push('');
92
+ }
93
+ }
94
+ if (wm.episodes && Array.isArray(wm.episodes.items) && wm.episodes.items.length > 0) {
95
+ for (const item of wm.episodes.items) {
96
+ lines.push(`- **${item.title || item.id}**`);
97
+ if (item.structuredContext && item.structuredContext.rawContent) {
98
+ lines.push(` ${item.structuredContext.rawContent.slice(0, 200)}`);
99
+ }
100
+ }
101
+ } else {
102
+ lines.push('_No relevant episodes found._');
103
+ }
104
+
105
+ return lines.join('\n');
106
+ }
107
+
108
+ module.exports = {
109
+ constructWorkingMemory,
110
+ formatWorkingMemoryForContext,
111
+ };
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+ const { scanText, buildSafeSummary, redactText } = require('./secret-scanner');
7
+
8
+ const SHIELDCORTEX_RUNNER_PATH = path.join(__dirname, 'shieldcortex-memory-firewall-runner.mjs');
9
+ const VALID_PROVIDERS = new Set(['auto', 'shieldcortex', 'local', 'off']);
10
+ const VALID_MODES = new Set(['strict', 'balanced', 'permissive']);
11
+
12
+ function resolveMemoryFirewallProvider(provider) {
13
+ const configured = String(
14
+ provider || process.env.THUMBGATE_MEMORY_FIREWALL_PROVIDER || 'auto'
15
+ ).trim().toLowerCase();
16
+ return VALID_PROVIDERS.has(configured) ? configured : 'auto';
17
+ }
18
+
19
+ function resolveMemoryFirewallMode(mode) {
20
+ const configured = String(
21
+ mode || process.env.THUMBGATE_MEMORY_FIREWALL_MODE || 'strict'
22
+ ).trim().toLowerCase();
23
+ return VALID_MODES.has(configured) ? configured : 'strict';
24
+ }
25
+
26
+ function canResolveShieldCortex() {
27
+ try {
28
+ require.resolve('shieldcortex/package.json');
29
+ return true;
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ function buildIngressRecord(feedbackEvent = {}, memoryRecord = null) {
36
+ const feedbackPayload = {
37
+ signal: feedbackEvent.signal || null,
38
+ context: feedbackEvent.context || '',
39
+ whatWentWrong: feedbackEvent.whatWentWrong || null,
40
+ whatToChange: feedbackEvent.whatToChange || null,
41
+ whatWorked: feedbackEvent.whatWorked || null,
42
+ reasoning: feedbackEvent.reasoning || null,
43
+ visualEvidence: feedbackEvent.visualEvidence || null,
44
+ tags: Array.isArray(feedbackEvent.tags) ? feedbackEvent.tags : [],
45
+ skill: feedbackEvent.skill || null,
46
+ actionType: feedbackEvent.actionType || null,
47
+ actionReason: feedbackEvent.actionReason || null,
48
+ };
49
+
50
+ const memoryPayload = memoryRecord
51
+ ? {
52
+ category: memoryRecord.category || null,
53
+ title: memoryRecord.title || null,
54
+ pattern: memoryRecord.pattern || null,
55
+ solution: memoryRecord.solution || null,
56
+ tags: Array.isArray(memoryRecord.tags) ? memoryRecord.tags : [],
57
+ }
58
+ : null;
59
+
60
+ const tags = new Set([
61
+ ...(Array.isArray(feedbackPayload.tags) ? feedbackPayload.tags : []),
62
+ ...(memoryPayload && Array.isArray(memoryPayload.tags) ? memoryPayload.tags : []),
63
+ ]);
64
+
65
+ return {
66
+ title: memoryPayload && memoryPayload.title
67
+ ? memoryPayload.title
68
+ : `feedback_ingress:${feedbackPayload.signal || 'unknown'}`,
69
+ content: JSON.stringify(
70
+ {
71
+ feedback: feedbackPayload,
72
+ promotedMemory: memoryPayload,
73
+ },
74
+ null,
75
+ 2
76
+ ),
77
+ tags: [...tags],
78
+ metadata: {
79
+ project: 'thumbgate',
80
+ feedbackSignal: feedbackPayload.signal || null,
81
+ memoryCategory: memoryPayload ? memoryPayload.category : null,
82
+ },
83
+ };
84
+ }
85
+
86
+ function buildLocalFirewallDecision(record, options = {}) {
87
+ const scanResult = scanText(record.content, {
88
+ provider: options.secretProvider,
89
+ source: 'memory_ingress',
90
+ });
91
+
92
+ if (!scanResult.detected) {
93
+ return {
94
+ allowed: true,
95
+ provider: 'local',
96
+ mode: options.mode,
97
+ reason: 'Local memory-ingress scan passed.',
98
+ threatIndicators: [],
99
+ findings: [],
100
+ redactedPreview: redactText(record.content).slice(0, 400),
101
+ };
102
+ }
103
+
104
+ return {
105
+ allowed: false,
106
+ provider: 'local',
107
+ mode: options.mode,
108
+ reason: buildSafeSummary(
109
+ scanResult.findings,
110
+ 'Memory ingestion blocked because it appears to contain secret material'
111
+ ),
112
+ threatIndicators: ['credential_leak'],
113
+ findings: scanResult.findings,
114
+ redactedPreview: redactText(record.content).slice(0, 400),
115
+ };
116
+ }
117
+
118
+ function runShieldCortexFirewall(record, options = {}) {
119
+ const child = spawnSync(
120
+ process.execPath,
121
+ [SHIELDCORTEX_RUNNER_PATH],
122
+ {
123
+ input: JSON.stringify({
124
+ record,
125
+ options: {
126
+ mode: options.mode,
127
+ sourceType: options.sourceType || 'hook',
128
+ sourceIdentifier: options.sourceIdentifier || 'feedback-loop',
129
+ },
130
+ }),
131
+ encoding: 'utf8',
132
+ maxBuffer: 4 * 1024 * 1024,
133
+ env: process.env,
134
+ }
135
+ );
136
+
137
+ if (child.error) {
138
+ return {
139
+ available: false,
140
+ error: child.error.message,
141
+ };
142
+ }
143
+
144
+ const output = String(child.stdout || '').trim();
145
+ if (!output) {
146
+ return {
147
+ available: false,
148
+ error: child.stderr || `ShieldCortex runner exited with code ${child.status}`,
149
+ };
150
+ }
151
+
152
+ try {
153
+ return JSON.parse(output);
154
+ } catch (error) {
155
+ return {
156
+ available: false,
157
+ error: `Invalid ShieldCortex runner output: ${error.message}`,
158
+ };
159
+ }
160
+ }
161
+
162
+ function evaluateMemoryIngress({
163
+ feedbackEvent,
164
+ memoryRecord = null,
165
+ provider,
166
+ mode,
167
+ sourceType = 'hook',
168
+ sourceIdentifier = 'feedback-loop',
169
+ secretProvider,
170
+ } = {}) {
171
+ const resolvedProvider = resolveMemoryFirewallProvider(provider);
172
+ const resolvedMode = resolveMemoryFirewallMode(mode);
173
+ const record = buildIngressRecord(feedbackEvent, memoryRecord);
174
+
175
+ if (resolvedProvider === 'off') {
176
+ return {
177
+ allowed: true,
178
+ provider: 'off',
179
+ mode: resolvedMode,
180
+ reason: 'Memory-ingress firewall disabled.',
181
+ threatIndicators: [],
182
+ findings: [],
183
+ redactedPreview: redactText(record.content).slice(0, 400),
184
+ };
185
+ }
186
+
187
+ const wantsShieldCortex = resolvedProvider === 'shieldcortex' || resolvedProvider === 'auto';
188
+ if (wantsShieldCortex && canResolveShieldCortex()) {
189
+ const decision = runShieldCortexFirewall(record, {
190
+ mode: resolvedMode,
191
+ sourceType,
192
+ sourceIdentifier,
193
+ });
194
+ if (decision && decision.available) {
195
+ return decision;
196
+ }
197
+ }
198
+
199
+ const localDecision = buildLocalFirewallDecision(record, {
200
+ mode: resolvedMode,
201
+ secretProvider,
202
+ });
203
+
204
+ if (resolvedProvider === 'shieldcortex') {
205
+ return {
206
+ ...localDecision,
207
+ degraded: true,
208
+ requestedProvider: 'shieldcortex',
209
+ reason: `ShieldCortex unavailable; ${localDecision.reason}`,
210
+ };
211
+ }
212
+
213
+ return localDecision;
214
+ }
215
+
216
+ module.exports = {
217
+ buildIngressRecord,
218
+ buildLocalFirewallDecision,
219
+ evaluateMemoryIngress,
220
+ resolveMemoryFirewallMode,
221
+ resolveMemoryFirewallProvider,
222
+ };