<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Mo Taha]]></title><description><![CDATA[Mo Taha]]></description><link>https://motaha.io</link><image><url>https://motaha.io/img/substack.png</url><title>Mo Taha</title><link>https://motaha.io</link></image><generator>Substack</generator><lastBuildDate>Thu, 23 Apr 2026 12:40:03 GMT</lastBuildDate><atom:link href="https://motaha.io/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Mohamed Taha]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[mota@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[mota@substack.com]]></itunes:email><itunes:name><![CDATA[Mohamed]]></itunes:name></itunes:owner><itunes:author><![CDATA[Mohamed]]></itunes:author><googleplay:owner><![CDATA[mota@substack.com]]></googleplay:owner><googleplay:email><![CDATA[mota@substack.com]]></googleplay:email><googleplay:author><![CDATA[Mohamed]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Building Tiny AI Tools Part 2: Evolving JiraGPT with Real User Feedback and Cursor Assistance]]></title><description><![CDATA[Addressing Early Challenges, Implementing Multi-Project Support, and Leveraging Cursor for Development]]></description><link>https://motaha.io/p/building-tiny-ai-tools-part-2-evolving</link><guid isPermaLink="false">https://motaha.io/p/building-tiny-ai-tools-part-2-evolving</guid><dc:creator><![CDATA[Mohamed]]></dc:creator><pubDate>Sat, 09 Nov 2024 19:07:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!lEj2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lEj2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lEj2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 424w, https://substackcdn.com/image/fetch/$s_!lEj2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 848w, https://substackcdn.com/image/fetch/$s_!lEj2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 1272w, https://substackcdn.com/image/fetch/$s_!lEj2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lEj2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png" width="1456" height="1001" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1001,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:715263,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!lEj2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 424w, https://substackcdn.com/image/fetch/$s_!lEj2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 848w, https://substackcdn.com/image/fetch/$s_!lEj2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 1272w, https://substackcdn.com/image/fetch/$s_!lEj2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbac9b528-bcda-4429-9e68-9892475b5f27_2594x1784.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3><strong>Intro</strong></h3><p>It&#8217;s been a week since I published how I built JiraGPT as the first tool in the <a href="https://tinyai.tools">Tiny AI Tools</a> bundle. The reactions were great, especially from people curious about AI and automation. So, in the spirit of building in public, here is part 2. This time, we are still working on JiraGPT. Since last week, a few users started to use JiraGPT, or at least tried to, and that revealed some limitations, bugs, and a couple of missing features or things I didn&#8217;t account for. The big takeaway for me is you never know until real users start using it, no matter how much you test. So stay with me; I&#8217;ll walk you through how those limitations were discovered, how I tackled them, and how Cursor&#8212;as my new junior engineer&#8212;was a huge help!</p><p></p><h3><strong>User Doesn&#8217;t Have to Be a Jira Admin</strong></h3><p>When I was testing JiraGPT, I used my Jira admin account. So when the background job that syncs the project&#8217;s metadata kicked in, it never had any issues, and it shouldn&#8217;t have&#8212;except one of the Jira REST APIs we use is <a href="https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-project-roles/#api-rest-api-3-project-projectidorkey-roledetails-get">this one</a>, which we use to fetch display names of users in a project. That API requires the user to be an admin. As you can imagine, this broke when a non-admin user installed the app; things started to break. Luckily, I found a quick workaround using another API: <a href="https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-user-search/#api-rest-api-2-user-assignable-multiprojectsearch-get">this one</a>, and that was it. I noticed this issue because I had Sentry integrated, which is great for error monitoring. I can&#8217;t stress enough the importance of error monitoring even for an MVP/beta product, especially when it&#8217;s so easy to integrate Sentry.</p><p></p><h3><strong>What If a User Has Multiple Jira Projects? AKA Adding Multi-Project Support</strong></h3><p>Well, going back to my point about you never know until real users start using your app, in this case, they just connected Jira, but that was enough to stretch JiraGPT&#8217;s limits. The user works for a big and well-known company; they have lots of projects in their Jira instance, and in each project, they have hundreds of users. Up until this point, JiraGPT only supported a single Jira project, i.e., the user has one Jira project in their instance. Obviously, that is not ideal and had to change.</p><p>The challenge for me was how to do that in the easiest way possible without disrupting the user and with a clear user experience. After some whiteboarding and brainstorming with ChatGPT (o1-preview), I decided the flow should be as follows: when syncing the user&#8217;s project, check if they have a multi-project setup. If not, we shouldn&#8217;t disrupt those users; i.e., they don&#8217;t have to specify which project to search against in their queries. If they have multiple projects, the first time they ask a query without specifying a project, we prompt them to include which project they mean, and that&#8217;s only required for the first time. We then cache that so they don&#8217;t have to specify the project in subsequent queries. They still have the option to change that by specifying a different project in their query.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Qr8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Qr8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 424w, https://substackcdn.com/image/fetch/$s_!8Qr8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 848w, https://substackcdn.com/image/fetch/$s_!8Qr8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 1272w, https://substackcdn.com/image/fetch/$s_!8Qr8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Qr8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png" width="1456" height="1377" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1377,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:403737,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8Qr8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 424w, https://substackcdn.com/image/fetch/$s_!8Qr8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 848w, https://substackcdn.com/image/fetch/$s_!8Qr8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 1272w, https://substackcdn.com/image/fetch/$s_!8Qr8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1dc14ff9-1b4a-4465-a0be-373ad18ede29_1546x1462.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>After testing that on my test projects and a real project, it seemed to work pretty well. Now, as a user with multiple projects, you can use JiraGPT.</p><p></p><h3><strong>Cursor Was a Huge Help</strong></h3><p>I switched from GitHub Copilot to Cursor lately (more on that in a different article), and it&#8217;s been great. But still, you kinda have to act as the architect/driver and, most importantly, a code reviewer; otherwise, you end up with jargon code that doesn&#8217;t work. So even though I&#8217;ve been advocating for the importance of having a good, detailed prompt when interacting with LLMs (ChatGPT, Claude, etc.), I&#8217;ve kinda been neglecting that advice with Cursor, partly because it&#8217;s been great. But when it comes to complex tasks, it hallucinates and over-engineers solutions. So this time, when I wanted to add multi-project support, I went back to my advice and wrote down a more detailed prompt highlighting what I needed. I even already provided it with the system design I had in mind and asked it to ask questions and confirm it understood what we want to build before diving into building it. As expected, it worked mostly from the first time with minor tweaks about UX. So here is the prompt (don&#8217;t mind the typos); hope it can be helpful to you:</p><pre><code>Let's work on multi-project support. Currently, JiraGPT supports single project setup, i.e., the user has only one Jira project in their connection. We should modify the `JiraConnection` table to have that info and update it every time we sync the user's project. It should be a boolean flag. 

In the Slack integration, we should check if the Jira connection is multi-project. If not, keep doing what we have been doing so that users with a single Jira project are not disrupted. If, on the other hand, the connection is multi-project, we do the following:

a) If the user mentions a project in their query, use it to run the query as we don't support queries spanning multiple projects, and then cache the selected project (by cache, I mean save it on the `JiraConnection` table; we might cache it later in Redis), so that we can use it in subsequent queries. That way, the user doesn't have to specify it every time. It should only change when the user does so by specifying a different project in a query.

b) If the user doesn't mention it, we ask them to include it as part of their query just for the first time, and the rest of point a continues.

Notes:
1. Check `models.py`, `jira.py`, `slack.py`, `jql_builder.py`, `views.py` carefully and don't break existing features/code.
2. Make sure to implement it in a safe and secure way.
3. Solution should be simple.
4. Have a good user experience and clear copies.
5. If anything is not clear, ask me questions before diving into the implementation.</code></pre><p></p><h3><strong>What Is Next for JiraGPT and TinyAI.Tools?</strong></h3><p>The next thing for JiraGPT is to talk to some customers and see where things go from there, in addition to fixing bugs and improvements.</p><p>The next thing for Tiny AI Tools is to work on the next AI tools. It&#8217;s probably gonna be for businesses; I have a few ideas, so hopefully I&#8217;ll pick one and build a quick MVP. As usual, I&#8217;ll share more details on how I&#8217;m building it, lessons, and challenges, so make sure to subscribe to the newsletter. I&#8217;d love to hear from you if you liked the article and your thoughts on things we&#8217;ve discussed&#8212;any AI ideas you think I should work on, tips, etc. Until next time, have a great one!</p>]]></content:encoded></item><item><title><![CDATA[Building Tiny AI Tools: How I Integrated Slack, Jira, and AI to Create JiraGPT]]></title><description><![CDATA[An Introduction to Building Tiny AI Tools: My Experience Creating JiraGPT]]></description><link>https://motaha.io/p/building-tiny-ai-tools-how-i-integrated</link><guid isPermaLink="false">https://motaha.io/p/building-tiny-ai-tools-how-i-integrated</guid><dc:creator><![CDATA[Mohamed]]></dc:creator><pubDate>Sat, 02 Nov 2024 00:10:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PBiv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PBiv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PBiv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 424w, https://substackcdn.com/image/fetch/$s_!PBiv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 848w, https://substackcdn.com/image/fetch/$s_!PBiv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 1272w, https://substackcdn.com/image/fetch/$s_!PBiv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PBiv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png" width="1456" height="833" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:833,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1900048,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PBiv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 424w, https://substackcdn.com/image/fetch/$s_!PBiv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 848w, https://substackcdn.com/image/fetch/$s_!PBiv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 1272w, https://substackcdn.com/image/fetch/$s_!PBiv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0da453-a766-4657-84e7-85a5c8455afc_3616x2068.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A Screenshot of JiraGPT Slack Bot&#8230;</figcaption></figure></div><p>Ever since OpenAI launched ChatGPT, my productivity has never been the same. I&#8217;ve always loved coding, but what I enjoy most is building&#8212;coding is just the means to make that happen.</p><p>In 2022, when I discovered ChatGPT, everything changed. Suddenly, I could ship projects faster and build MVPs more efficiently with the help of AI. It became a core part of my workflow, making development not only more productive but also more enjoyable. I use it for almost everything, and adapting to this new AI-driven approach has been both fun and transformative.</p><p>With this newfound efficiency, I wanted to take things further. My plan included experimenting with different AI tools to enhance my workflow and building my own AI-powered solutions. This led me to create <strong><a href="https://tinyai.tools">TinyAI.Tools</a></strong>, a project where I develop and share AI tools that integrate seamlessly into everyday work. In this series, I&#8217;ll walk you through my journey, starting with how I built <strong>JiraGPT</strong>.</p><p></p><h3>Who Am I?</h3><p>I&#8217;ve been working as a software engineer for 10 years, primarily focused on backend development. Currently, I&#8217;m a hands-on engineering manager. As mentioned, I love building things and launching projects&#8212;it brings me joy to see something I built being useful for others.</p><p></p><h3>Why &#8220;Tiny&#8221; AI Tools?</h3><p>The word &#8220;tiny&#8221; isn&#8217;t meant to downplay the tool&#8217;s significance. It reflects that each tool is focused on solving a specific problem really well. There&#8217;s nothing I dislike more than exaggerated claims about what AI/LLMs can do. In my view, AI/LLMs work best when integrated into an already functional system to enhance it.</p><p>I&#8217;d like to emphasize that the tools I build are production-ready and secure. The goal isn&#8217;t just to experiment with AI/LLMs but also to solve real problems and build a sustainable business around them.</p><p></p><h3>What Kind of AI Tools?</h3><p>For now, I&#8217;m building AI wrappers&#8212;that is, leveraging off-the-shelf AI models (text, images, audio, video), whether open-source or proprietary. Some people criticize AI wrappers for not having a &#8220;moat&#8221;, and while that&#8217;s often true, I aim to provide more than just a simple interface to an LLM. After all, if you&#8217;re providing real value, who cares?</p><p></p><h3>First Tool: JiraGPT</h3><p>I initially built JiraGPT over a year ago, put it on hold for a few months, and recently rebuilt it with improvements. When I decided to work on the TinyAI.Tools project, I wanted to set up some groundwork and boilerplate for future tools, so I took the opportunity to rebuild JiraGPT with the features I envisioned. It may not have every feature yet, but the core functionality is there.</p><p>So, what is JiraGPT? It&#8217;s a Slack bot that allows you to search Jira issues using natural language directly from Slack. The idea came when I was a tech lead and often needed to search Jira without leaving Slack or memorizing complex JQL (Jira Query Language). Now, If you follow Atlassian (the company behind Jira), you might know they recently launched Atlassian AI, which lets you use natural language instead of JQL&#8212;but only within Jira, not Slack. That&#8217;s a key difference between their solution and JiraGPT. The grand vision for JiraGPT goes beyond searching; it aims to help you manage your Jira projects.</p><p></p><h3>How JiraGPT Works</h3><p>Once you connect your Jira instance (with read-only permissions) and install the Slack app, you can DM JiraGPT with queries like <code>&#8220;find all in-progress stories that haven't been updated in 2 weeks&#8221;</code>, and the bot will fetch the relevant Jira issue. Behind the scenes, the app converts your natural language query into JQL and calls the Jira REST API. Currently, JiraGPT does not store your Jira tickets, issues, or comments, which reduces data privacy concerns. For now, the app only requests permissions needed to fetch and store metadata, like project statuses, team member names, and custom fields, to help generate more accurate JQL.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0GSQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0GSQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 424w, https://substackcdn.com/image/fetch/$s_!0GSQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 848w, https://substackcdn.com/image/fetch/$s_!0GSQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 1272w, https://substackcdn.com/image/fetch/$s_!0GSQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0GSQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png" width="1456" height="895" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:895,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1946748,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0GSQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 424w, https://substackcdn.com/image/fetch/$s_!0GSQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 848w, https://substackcdn.com/image/fetch/$s_!0GSQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 1272w, https://substackcdn.com/image/fetch/$s_!0GSQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff3cd11a5-e51d-44d6-9be0-d9c2feb02217_3364x2068.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Screenshot of JiraGPT in Slack ...</figcaption></figure></div><p></p><h3>How JiraGPT Works Behind the Scenes</h3><p>This part gets a bit technical, but it should be understandable even if you work with a different tech stack. I use Python, Django, PostgreSQL, Celery, and Redis for background jobs, and TailwindCSS for styling and OpenAI GPT API. I won&#8217;t go into standard features like login or signup; instead, I&#8217;ll focus on how Slack, LLMs, and Jira are integrated.</p><p>When a user creates an account, they go through a two-step onboarding process. First, they connect their Jira instance. We use OAuth 2.0 for authentication and authorization, implementing the principle of least privilege by only requesting `read:jira-work` and `read:jira-user` permissions to call the search-by-JQL endpoint. The second step is to install the Slack bot. Once both steps are completed, JiraGPT initiates a DM with a greeting and usage examples.</p><p>Securing these connections is critical, and we follow best practices for authentication and authorization for both platforms. It&#8217;s also worth noting that we don&#8217;t store any of the messages exchanged between you and JiraGPT.</p><p></p><h3>Fetching Your Project Metadata</h3><p>For JiraGPT to generate accurate JQL and provide precise search results, it stores some metadata, like statuses, team member display names, and custom field names. Fetching and cleaning this data can be slow, so we use Celery to handle it in the background after the initial Jira connection. Metadata may change over time (e.g., new statuses or team members), so an hourly cron job updates this data regularly.</p><p></p><h3>A Typical User Flow</h3><p>Let&#8217;s walk through a typical user flow. You send a message to JiraGPT in Slack, such as <code>&#8220;Show me high-priority tickets created in the past week&#8221; </code>The system cleans and validates the query, then passes it to a service called JQLBuilder, which converts it into a valid JQL string. I use OpenAI&#8217;s structured output feature to ensure consistent results. Here&#8217;s what the service class looks like:</p><pre><code>class JQLBuilder:
    """Build JQL queries from natural language queries."""
    def __init__(self) -&gt; None:
        self.connection = connection
        self.client = openai.OpenAI(api_key=settings.OPENAI_API_KEY)
        self.project_metadata = &lt;project metadata&gt;

    def build_jql(self, user_query: str) -&gt; str:
        """Build a JQL query from a natural language query."""
        # Basic sanitization
        user_query = user_query.strip().lower()

        prompt = JQL_BUILDER_PROMPT.format(project_metadata=self.project_metadata, user_query=user_query)
        response = self.client.beta.chat.completions.parse(
            model="gpt-4o-mini-2024-07-18",
            messages=[{"role": "user", "content": prompt}],
            response_format=JQLBuilderResponse
        )
        return response.choices[0].message.parsed.jql_query
        
   
class JQLBuilderResponse(BaseModel):
    """Response from the JQL builder."""
    jql_query: str</code></pre><p>And here&#8217;s the prompt:</p><pre><code>JQL_BUILDER_PROMPT = """
You are an expert Jira Query Language (JQL) builder. Your task is to convert natural language queries into accurate JQL queries based on the provided project metadata.

Project Metadata:
{project_metadata}

User Query: {user_query}

Guidelines:
1. Use the exact field names, statuses, priorities, and team member names as provided in the metadata.
2. For text fields, use ~ for contains searches and = for exact matches.
3. Use appropriate operators (AND, OR, NOT) to combine multiple conditions.
4. If the user query mentions a date, use appropriate date functions (startOfDay(), endOfDay(), startOfWeek(), etc.).
5. If the query is about a specific team member, use the 'assignee' field.
6. For custom fields, use cf[ID] syntax, where ID is the custom field id provided in the metadata.
7. Always include 'project = project_key' in your query unless the user specifically asks for issues across multiple projects.
8. If the user query is vague, make reasonable assumptions based on common Jira use cases.
9. If a mentioned field or value doesn't exist in the metadata, use the closest match or omit it if there's no suitable alternative.
If you cannot generate a valid JQL query, return an empty string ("") for the JQL Query

Remember, your goal is to create a JQL query that best represents the user's intent while adhering to the specific metadata of the project.
"""</code></pre><p>Once the user&#8217;s query is parsed into JQL, we pass it to Jira&#8217;s search-by-JQL API, format the response, and send the results back to the user.</p><p></p><h3>Wrapping Up</h3><p>And that&#8217;s basically how you can build a tool that searches Jira using natural language. To create something like JiraGPT, you need project metadata, a reliable LLM with structured outputs, and a seamless user interface&#8212;such as a Slack bot.</p><p>While the current approach focuses on generating JQL dynamically, there is an alternative strategy: you could index all project tickets, comments, and metadata into a vector database and use Retrieval-Augmented Generation (RAG) for searches. However, this method can be overkill for the feature&#8217;s current scope.</p><p>Keep in mind that this implementation has some limitations and room for improvement. I have a few ideas for enhancements but wanted to release this as a solid MVP with the core feature intact. Future updates will include relevant and meaningful additions only.</p><p>This project was an exciting way to explore practical applications of AI, especially in enhancing productivity tools. It showed me how AI could integrate seamlessly into existing workflows to solve specific pain points without unnecessary complexity.</p><p>I&#8217;d love to hear your feedback. Did you find the concept of building AI-powered integrations like JiraGPT useful? Are there aspects you&#8217;d like to see expanded or discussed in future posts? Let me know what you think!</p><p></p><p>Also, If this sounds interesting, I invite you to <a href="https://tinyai.tools">try out JiraGPT</a> and see how it can streamline your workflow! And if you want to follow along as I build more AI tools and share insights from the journey, don&#8217;t forget to subscribe to my newsletter for more content like this!</p><p></p><p></p>]]></content:encoded></item></channel></rss>