Now in beta

Your AI Project Manager in Slack

Thor proactively handles all the project admin work that your team doesn't have time for.
Google meet ended
11:56

Sprint planning meeting transcript processed

Thor
APP
11:56

Here's a quick recap of the action items from today's Sprint Planning meeting.

  • Create issue for API timeout bug
  • Update architecture doc with new Redis setup
  • @Sarah Sarah committed to deploy by Thursday
  • Standup meeting is in 10 minutes. I’ll send you your notes shortly.

Want me to handle these?

Alex
11:57

Create the issue and update the doc

Thor
APP
11:58

✓ I’ve created Linear issue #234

✓ I’ve updated Architecture.doc

Calm fjord with a small boat, flanked by towering snow-capped mountains and a village with red houses on the left shore.

Thor handles the project management work you hate doing

Turning meetings into tasks. Keeping docs up to date. Creating issues from Slack. Thor takes care of the admin work so you can get back to building
Light blue pixelated pattern on a white background fading toward the center.
Standups That Write Themselves
Thor drafts your standup from actual activity—commits, tickets, threads—so you're not reconstructing yesterday from memory.
Integrated with
Subtle abstract background with a grid pattern and soft pink gradient on the left.
Turn Meeting Notes Into Tasks
After your meeting ends, Thor reads the transcript and figures out what needs to happen. You review it, give the thumbs up, and Thor handles it.
Integrated with
Abstract light blue and white pixelated gradient background with a subtle grid pattern.
Keep Specs Up-to-date
Thor suggests edits to your docs based on what your team talked about in meetings and Slack. Your product specs always reflect reality. 
Integrated with
Abstract grid pattern with soft gradient from pale beige to white, featuring scattered pixel-like squares.
Track Commitments You Can’t Drop
When you say "I'll get you this by the end of the day" Thor logs it, reminds you at the right moment, and follows up if it slips.
Integrated with
Abstract grid pattern with soft gradient from pale beige to white, featuring scattered pixel-like squares.
Create Issues from Threads
Just @mention Thor in any Slack conversation and it creates a ticket with all the right context pulled from the thread.
Integrated with
Ask Thor

Ask Thor anything.
Get answers right away

Thor has the complete picture of what your team is working on. Ask Thor anything you'd ask a teammate, and get an instant, accurate answer.
Y
You
Just now
This is some text inside of a div block.
T
Thor
Thinking...
T
Thor
Just now

Here's what George has been working on this week:

  • Completed PR #456 for authentication refactor (merged Tuesday)
  • Currently reviewing database migration plan in #eng-backend
  • Opened Linear issue #789 for Redis caching implementation
  • Had 3 meetings: Sprint planning, Architecture review, 1:1 with Tyler
T
Thor
Just now

Yes, 3 engineers are currently blocked:

  • Sarah is waiting on a review for PR #234 (API rate limiting refactor), open for 2 days with Marcus and Dev tagged as reviewers
  • James has PR #301 (payment webhook integration) pending review since yesterday, flagged as high priority but with no reviewers assigned yet
  • Priya is blocked on the new onboarding flow implementation until the product spec in Notion is approved, waiting on sign-off from the PM team

T
Thor
Just now

Here's the current status of the database migration:

  • Overall progress: 8 of 12 issues complete
    • Sarah Chen completed the schema conversion for the users table yesterday and has moved on to the orders table
    • James Okafor is currently running validation scripts on the migrated data from Phase 6, with results expected by EOD
  • Next milestone: Full staging environment cutover scheduled for Thursday
  • Risks: 2 tables have unresolved warnings that need review before production cutover
T
Thor
Just now

Here's a quick brief to help you prep for your 1:1 with George:

What George has been working on George completed PR #456 for the authentication refactor (merged Tuesday) and opened Linear issue #789 for the Redis caching implementation. He's currently reviewing the database migration plan in #eng-backend.

Potential talking points

  • PR #612 (error handling improvements) has been open for 4 days without a reviewer assigned, worth checking in on
  • George left a comment on the architecture review doc flagging concerns about the proposed caching strategy, may be worth discussing
  • His last 3 PRs have averaged 3 days to review, which is above the team average

Wins to acknowledge

  • The authentication refactor he led shipped cleanly with no post-merge issues
  • He proactively flagged a potential bottleneck in the migration pipeline last week

Open questions you might want to ask

  • Is he still on track for the Redis caching ticket given the current migration workload?
  • Does he need any support getting reviewers on his open PRs?
T
Thor
Just now

Your last meeting on the database migration was Tuesday, February 11th at 2:00 PM.

  • Attendees: Sarah, James, Priya Nair, Tyler (PM), and you
  • Duration: 47 minutes
  • Key decisions made: Agreed to prioritize the orders and users tables before moving to the analytics tables, and confirmed Thursday as the target date for the staging environment cutover
  • Action items from that meeting: James to run data validation scripts by EOD Friday, Sarah to document schema conversion process for handoff, Tyler to get sign-off on the analytics table spec from the PM team
  • Note: 2 action items from that meeting are still open, James's validation results are in and Sarah's documentation is still pending
T
Thor
Just now

Here's what I found and added to Linear Issue #123 (Redis Caching Implementation):

Description updated with the following context:

  • From the Architecture Review meeting (Feb 13th): George flagged concerns about cache invalidation strategy for user session data, specifically around edge cases where multiple services write to the same key. The team agreed this needs to be resolved before implementation begins.
  • From #eng-backend (Feb 14th): Marcus shared a link to an internal doc outlining the proposed TTL values for each cache layer. James replied suggesting a shorter TTL for the orders service given its write frequency.
  • From the Sprint Planning meeting (Feb 10th): This ticket was flagged as a dependency for the payment webhook integration (PR #301), meaning a delay here could push back James's work as well.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
/** * Thor Chat Interaction * Handles: question select → type → send → user bubble → thinking → answer */ (function () { "use strict"; /* ─── Config ─────────────────────────────── */ const TYPING_SPEED = 30; // ms per character — comfortable reading pace const THINKING_MIN = 2000; // ms min thinking time const THINKING_MAX = 2600; // ms max thinking time const SEND_SCALE_DUR = 380; // ms send button animation /* ─── DOM refs ────────────────────────────── */ const questionsWrapper = document.querySelector(".ask_questions-wrapper"); const typeInput = document.querySelector(".type_input"); const shareIconWrap = document.querySelector(".share_icon-wrap"); const chatArea = document.querySelector(".chat_area"); // Templates (already in DOM, cloned not moved) const userBubbleTemplate = chatArea ? chatArea.querySelector(".chat_content.user") : null; const dummyLoadingTemplate = chatArea ? chatArea.querySelector(".chat_content.dummy-loading") : null; if (!questionsWrapper || !typeInput || !shareIconWrap || !chatArea) { console.warn("[Thor] Missing required DOM elements. Aborting."); return; } /* ─── State ───────────────────────────────── */ let isAnimating = false; let typeInterval = null; let activeClone = null; // current dummy-loading clone in DOM /* ─── CSS injected once ───────────────────── */ const style = document.createElement("style"); style.textContent = ` /* Send button spring press */ .share_icon-wrap { transition: transform 160ms cubic-bezier(0.34,1.7,0.64,1); cursor: pointer; display: inline-flex; } .share_icon-wrap.pressed { transform: scale(0.72); } /* Question active state */ .ask_question.active { opacity: 1; } /* User bubble entrance - rises with tiny scale */ @keyframes thorSlideIn { from { opacity: 0; transform: translateY(14px) scale(0.97); } to { opacity: 1; transform: translateY(0) scale(1); } } .chat_content--entering { animation: thorSlideIn 460ms cubic-bezier(0.16,1,0.3,1) both; } /* Thinking - sequential wave */ @keyframes thorWave { 0%,60%,100% { opacity: 0.25; transform: scaleX(1); } 30% { opacity: 1; transform: scaleX(1.015); } } .chat_content.dummy-loading .dummy_chat { animation: thorWave 2s ease-in-out infinite; transform-origin: left center; } .chat_content.dummy-loading .dummy_chat:nth-child(2) { animation-delay: 0.18s; } .chat_content.dummy-loading .dummy_chat:nth-child(3) { animation-delay: 0.36s; } /* Answer entrance - slightly slower for weight */ @keyframes thorFadeUp { from { opacity: 0; transform: translateY(12px) scale(0.98); } to { opacity: 1; transform: translateY(0) scale(1); } } .chat_content--answer-in { animation: thorFadeUp 520ms cubic-bezier(0.16,1,0.3,1) both; } `; document.head.appendChild(style); /* ─── Helpers ─────────────────────────────── */ function scrollChatToBottom() { chatArea.scrollTo({ top: chatArea.scrollHeight, behavior: "smooth" }); } function clearActiveQuestion() { questionsWrapper .querySelectorAll(".ask_question.active") .forEach((el) => el.classList.remove("active")); } function clearTypeInterval() { if (typeInterval) { clearInterval(typeInterval); typeInterval = null; } } function typeText(text, onDone) { clearTypeInterval(); typeInput.value = ""; let i = 0; typeInterval = setInterval(() => { if (i < text.length) { typeInput.value += text[i]; i++; } else { clearTypeInterval(); if (onDone) onDone(); } }, TYPING_SPEED); } function animateSendButton(onDone) { shareIconWrap.classList.add("pressed"); setTimeout(() => { shareIconWrap.classList.remove("pressed"); setTimeout(onDone, SEND_SCALE_DUR / 2); }, SEND_SCALE_DUR / 2); } function hideAllAnswers() { chatArea .querySelectorAll(".chat_content[chat-answer]") .forEach((el) => (el.style.display = "none")); } function hideUserBubble() { if (userBubbleTemplate) userBubbleTemplate.style.display = "none"; } function hideDummyLoading() { if (dummyLoadingTemplate) dummyLoadingTemplate.style.display = "none"; // also remove any live clone if (activeClone && activeClone.parentNode) { activeClone.parentNode.removeChild(activeClone); activeClone = null; } } function resetChatArea() { hideUserBubble(); hideDummyLoading(); hideAllAnswers(); typeInput.value = ""; } /* ─── Main flow ───────────────────────────── */ function runChatFlow(questionNum, questionText) { if (isAnimating) return; isAnimating = true; // 1. Reset existing chat state resetChatArea(); // 2. Type text into input typeText(questionText, () => { // 3. Animate send button animateSendButton(() => { typeInput.value = ""; // Brief breath before bubble appears — feels intentional setTimeout(() => { // 4. Show user bubble showUserBubble(questionText, () => { // 5. Show thinking state showThinking(() => { // 6. Show answer showAnswer(questionNum, () => { isAnimating = false; }); }); }); }, 140); // end breath timeout }); }); } function showUserBubble(text, onDone) { if (!userBubbleTemplate) { onDone(); return; } // Update user text const userTextEl = userBubbleTemplate.querySelector("[user-text]"); if (userTextEl) userTextEl.textContent = text; userBubbleTemplate.style.display = ""; userBubbleTemplate.classList.add("chat_content--entering"); scrollChatToBottom(); userBubbleTemplate.addEventListener( "animationend", () => { userBubbleTemplate.classList.remove("chat_content--entering"); onDone(); }, { once: true } ); } function showThinking(onDone) { if (!dummyLoadingTemplate) { setTimeout(onDone, THINKING_MIN); return; } // Clone so original stays as template const clone = dummyLoadingTemplate.cloneNode(true); clone.style.display = ""; clone.classList.add("chat_content--entering"); // Insert after user bubble const insertAfter = userBubbleTemplate || null; if (insertAfter && insertAfter.nextSibling) { chatArea.insertBefore(clone, insertAfter.nextSibling); } else { chatArea.appendChild(clone); } activeClone = clone; scrollChatToBottom(); const thinkTime = THINKING_MIN + Math.random() * (THINKING_MAX - THINKING_MIN); setTimeout(() => { // Collapse height + fade — feels like it dissolves naturally const h = clone.offsetHeight; clone.style.overflow = "hidden"; clone.style.transition = "opacity 300ms ease, max-height 360ms cubic-bezier(0.4,0,0.2,1), margin 360ms ease, padding 360ms ease"; clone.style.maxHeight = h + "px"; // Force reflow clone.offsetHeight; // eslint-disable-line no-unused-expressions clone.style.opacity = "0"; clone.style.maxHeight = "0"; clone.style.marginTop = "0"; clone.style.marginBottom = "0"; clone.style.paddingTop = "0"; clone.style.paddingBottom = "0"; setTimeout(() => { if (clone.parentNode) clone.parentNode.removeChild(clone); if (activeClone === clone) activeClone = null; onDone(); }, 380); }, thinkTime); } function showAnswer(questionNum, onDone) { const answerEl = chatArea.querySelector( `.chat_content[chat-answer="${questionNum}"]` ); if (!answerEl) { console.warn(`[Thor] No answer found for chat-answer="${questionNum}"`); onDone(); return; } answerEl.style.display = ""; answerEl.classList.add("chat_content--answer-in"); scrollChatToBottom(); answerEl.addEventListener( "animationend", () => { answerEl.classList.remove("chat_content--answer-in"); onDone(); }, { once: true } ); } /* ─── Init ────────────────────────────────── */ // Hide all answers + user bubble + dummy on load resetChatArea(); // Bind question clicks const questions = questionsWrapper.querySelectorAll(".ask_question"); questions.forEach((questionEl) => { questionEl.addEventListener("click", () => { if (isAnimating) return; // Get question number from chat-question attribute const questionNum = questionEl.getAttribute("chat-question"); // Get label text (the visible text shown to user) const labelEl = questionEl.querySelector(".ask_question-label"); const questionText = labelEl ? labelEl.textContent.trim().replace(/^"|"$/g, "") // strip surrounding quotes : questionEl.textContent.trim(); // Active class on clicked question clearActiveQuestion(); questionEl.classList.add("active"); // Placeholder reflects active question typeInput.placeholder = questionText; // Run flow runChatFlow(questionNum, questionText); }); }); // Support: clicking send icon directly on typed text also triggers shareIconWrap.addEventListener("click", () => { const val = typeInput.value.trim(); if (!val || isAnimating) return; // Find matching question by text let matchedNum = null; questions.forEach((q) => { const lbl = q.querySelector(".ask_question-label"); const txt = lbl ? lbl.textContent.trim().replace(/^"|"$/g, "") : q.textContent.trim(); if (txt === val) { matchedNum = q.getAttribute("chat-question"); } }); if (matchedNum) runChatFlow(matchedNum, val); }); })();
Calm fjord surrounded by snow-capped mountains with a small dock and red houses on the left and a sailboat on the water.
Context Graph

Powered by a context graph of your projects

Thor doesn't just connect to your tools. Thor builds a graph of how people, teams, and projects connect across GitHub, Linear, Slack, meetings, and more.
Linear issue #243
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Linear issue #253
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Linear issue #248
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Linear issue #247
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Documents
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Architecture.docx
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Design System.md
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Feature PRD.md
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Feature PRD.md
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Positioning.docx
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Sprint Meeting
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Sprint Demo
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Daily Standup
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Architecture Sync
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Backend Team Sync
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Onboarding Call
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
What did the team ship this week?
Slack Thread
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Standup from the graph
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Project handoff context
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
Launch readiness check
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
PR #456
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
PR #454
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
PR #453
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
PR #450
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
PR #443
Initial draft of Architecture.doc defining the boundary between the Auth service and the new Gateway.
George T.
team A
Will D.
team A
Steve M.
team A
Laura S.
team A
Laura S.
team B
Steve M.
team B
SETUP

Get going in a few minutes

STEP 01
Connect your tools
Link Thor to GitHub, Linear, Slack, Google Meet, and Drive. Takes 2 minutes.
STEP 02
Invite Thor to Slack
Add Thor to the Slack channels and meetings where your team coordinates work.
STEP 03
Thor starts working
Thor begins building your context graph and handling coordination automatically.

FAQs

Is Thor SOC 2 compliant?
Yes, Thor is fully SOC 2 Type 2 compliant. Thor is a new product from the team behind Zenhub, who have a decade-long track record of building enterprise-grade developer tools with security at their core. We apply the same rigorous security and compliance standards to everything we build.
Do you train your AI models on my company's data?
Absolutely not. Your data is yours alone. We never use customer data, conversations, or meeting transcripts to train our models. Thor runs on a single-tenant architecture, meaning each customer has a dedicated and isolated instance to ensure your information remains completely private.
How does Thor handle permissions and data access?
Thor is designed to be policy-aware. It inherits the exact same permissions as the user who connects it. If you don't have access to a specific repository or document, neither does Thor. It can only see what you can see and only take action where you have the appropriate rights to do so.
Does Thor have access to read or write our source code?
No. Thor's integration with tools like GitHub is strictly limited to project management functions like creating, updating, and commenting on issues. It does not read, write, or interact with your source code in any way. Your codebase remains untouched.
Is there an audit trail for actions taken by Thor?
Yes. Thor maintains a complete and transparent audit trail of every action it takes on your behalf. You have a full, searchable history of every issue created, document updated, and message sent, providing complete visibility and accountability for your organization.
How accurate is Thor with technical conversations?
Thor is specifically tuned for software development and product team conversations. However, our key feature is the "human-in-the-loop" design. Thor always proposes a plan for your approval before taking any action, giving you the chance to add any nuance and ensuring you're always in control.
Will this create more notification spam for my team?
No, the opposite. By centralizing follow-ups and creating structured action plans, Thor reduces the random "just checking in" pings on Slack. You can configure how and when Thor communicates to fit your team's existing workflow and minimize unnecessary interruptions.
What if I use a tool that isn't on your integrations list?
We are constantly expanding our ecosystem of connected tools based on customer feedback. If you have a specific tool you'd like us to support, we'd love to hear about your use case. Please reach out to our team at thor@zenhub.com.
Who is Thor built for?
Thor is purpose-built for technical teams—engineering, product, and design—that want to eliminate administrative overhead. It's ideal for scaling leads, engineering managers, and founders who feel the pain of coordination tax most acutely and would rather be building product.
Do I have to invite another bot to all my meetings?
Nope. You have enough bots to deal with. Thor isn't an awkward guest you have to remember to add to every call. It works by hooking into the meeting transcripts your team already generates in Google Meet, Zoom, and Microsoft Teams to figure out what needs to get done.
Do I need to be a workspace admin to set up Thor?
Not at all. You can initiate the setup in minutes using standard, secure third-party authorization like GitHub OAuth. If an integration requires broader admin approval, Thor will provide a simple, shareable link for your workspace admin to grant the necessary permissions.
What is the pricing for Thor?
We are currently in an early access program, working closely with our design partners to finalize a fair and flexible pricing model. For now, early access is provided at no cost. Request access today to lock in preferential pricing when we launch.