How I built an Automated Work Activity Tracker with Local Data and AI Summarization A developer built an automated daily work activity tracker that collects local data from git, browser history, and shell commands, condenses it to 40 lines, and sends it to an LLM for summarization, using Kiro CLI with Claude via Amazon Bedrock for structured output while maintaining data privacy. It’s 5:30 PM. I’ve been busy all day jumping between Terraform modules, multiple AWS accounts, code reviews, and meetings. When someone asks me “ What did you work on today?” My brain goes blank and I can barely remember anything. If you’re an engineer, you might have experienced a similar situation. Engineering requires constant context-switching requiring you to look into multiple repos, talk to different teams, debug issues, write code. At the end of the day, reconstructing what you actually did feels like archaeology. But the real problem wasn’t just reporting — it was awareness . I had no idea where my time was actually going. Was I spending more time in meetings than coding? Was I context-switching between 2–3 projects? Were my “ productive ” days actually productive? The thought was simple: my computer already knows what I did today . Git knows my commits. My browser history knows which services I visited. My shell history knows which commands I ran. Instead of remembering and manually reporting my daily work activity, why not let my computer do it? So I built a system that figures it out for me and more importantly, I built it as a pattern that any developer can replicate for their own workflow. I created a daily work activity tracker that collects everything locally, condenses aggressively, and sends only the minimum context to LLM for summarization. This architecture maintains control over what data leaves my machine, maintaining data privacy. All data extraction and classification happen on my machine, and the LLM only sees about 40 lines of condensed context. It only sees a curated summary, ticket memo, meeting names, and time breakdowns. However, it can’t be called fully private as those signals do hit a cloud API. So, before diving into the details, here’s how the whole system fits together: Work activity data is sensitive and must be confidential. My browser history contains internal URLs, project names, client information, and authentication flows. Shell history has infrastructure commands with account details. This flow allows me to parse and classify all the sensitive data myself using scripts, and only pass the relevant condensed output to an LLM for the final summarization step. I experimented with local models Llama, Mistral but they work for simple summarization, and struggle with the structured output. They’d drift from the template, add unwanted headers, or miss important signals. The quality gap was significant enough which required me to switch to a cloud model. For this project, I used the Kiro CLI tool. Kiro CLI is an AI-powered command-line tool that can use Claude via Amazon Bedrock under the hood. I chose it for Headless mode because it supports -no-interactive which is perfect for cron-triggered scripts that run without human input and Claude’s reliability at following strict output templates. It doesn’t drift from the template, respects the “no markdown” rules, and stays specific rather than generic. The LLM’s job is narrow: take about 40 lines of pre-classified activity signals and produce a structured daily summary. It’s the last 5% of the pipeline. The other 95% - the collection, deduplication, classification,time estimation, signal condensation — it is all deterministic Python and bash. The first version was embarrassingly simple with a bash one-liner that scanned all git repos and fed the commits to a local LLM model. Shellfind "$HOME/projects" -maxdepth 4 -name ".git" -type d | \ sed 's/\/.git$//' | \ xargs -P8 -I{} git -C "{}" log --all --since="yesterday" \ --author="$ git config user.email " --pretty=format:"- %s" The LLM would read my commit messages and generate a decent summary. But it only captured 30% of my day as engineering work isn’t just comments. It’s debugging in consoles, reading docs, sitting in meetings, running CLI commands, and reviewing other people’s code. So, I reworked it with more signals. I wrote a Python collector that reads activity from every local source I could think of. Here’s the interesting engineering decisions: Chrome and Brave store their history in a SQLite database. The catch: the browser locks the DB while it’s running. Solution — copy the file first, then read from the copy: Pythondef copy db db path : tmp = tempfile.NamedTemporaryFile delete=False, suffix=".sqlite" tmp.close shutil.copy2 db path, tmp.name return tmp.name Then there’s Chrome’s timestamp format. The timestamps includes microseconds since Jan 1, 1601, and here’s how you can convert it: Pythonchrome epoch = datetime 1601, 1, 1 since chrome = int since utc - chrome epoch .total seconds 1 000 000 ... later: actual time = chrome epoch + timedelta microseconds=ts + local offset This approach works for any Chromium-based browser, Chrome, Brave, Edge, Arc, with only the DB path changing. Raw browser history is hundreds of URLs per day. The useful signal is just 20%. To solve this issue, I built a rule-based classifier with 40+ conditions that maps every URL and command to a work category: AWS console URLs → "AWS Console" Jira/Atlassian domain → "Jira / Project Management" GitHub/GitLab → "Code Review" Meet/Teams/Zoom URLs → "Meeting" terraform/aws commands → "Infrastructure / IaC" docs. domains → "Documentation" Is it a machine learning model? No, and that’s by design. A rule-based classifier gives me full control over categorization, zero training overhead, and instant iteration. When a new tool or domain shows up in my workflow, I add one line, and it’s handled. No retraining, no data labeling, no model drift. Google Meet URLs follow a clean pattern /abc-defg-hij three-four-three letter codes which is relatively easy to detect. A simple expression is enough to reliably detect the meeting link. Pythondef is real meeting url url, domain : parsed = urlparse url path = parsed.path.rstrip "/" if domain == "meet.google.com": return bool re.match r"^/ a-z {3}- a-z {4}- a-z {3}$", path if domain == "teams.microsoft.com": if "/l/meetup-join" in url: return True if path in "", "/v2", "/v2/" or "launcher" in path: return False return True Microsoft Teams, however, is a different story. It includes login pages, launcher redirects, SSO callbacks, app integrations, which all look like meetings in the URL. This points out a challenge for anyone building browser-history tools. URL structure doesn’t reliably encode user intent. The practical solution is layered filtering: combine URL pattern matching with tab duration thresholds active 2 minutes and page title heuristics to separate actual meetings from transient navigation. The system groups all events browser, shell, git, file edits into a timeline, then splits them into “time blocks” using a 20-minute gap threshold. No activity for 20 minutes = new block. Each block gets a duration estimate and a primary category based on event frequency. This is the weakest part of the system so far. Deep focus work — reading, thinking, whiteboarding generates no events, so the system thinks you weren’t working. More on that in the “ what doesn’t work ” section. Here’s the key insight that makes this whole thing work: don’t feed the LLM everything. The Python collector generates 300–500 lines of raw data. If you send all of it to an LLM, you get generic corporate fluff back: “Worked on various infrastructure tasks and attended meetings throughout the day.” Useless. Instead, the bash orchestrator extracts ~40 lines of condensed signals: That’s what goes to the LLM, along with a prompt template: You are generating a factual engineering log. The input has already been collected, classified, and deduplicated. Treat every input item as evidence. Evidence priority trust higher over lower if they conflict : 1. Git commits highest confidence 2. File modifications 3. Shell commands 4. Browser activity 5. Meeting URLs 6. Time estimates lowest confidence THINK STEP BY STEP do this internally, do NOT include in output : Step 1: Identify the 3–5 major workstreams from the evidence. Step 2: For each workstream, gather ALL related evidence commits + browser + meetings that relate to the same topic . Step 3: Determine what actually got done vs what was just browsed/researched. Step 4: Check time blocks — does the time allocation make sense given the evidence weight? Step 5: Write the final report using only supported facts. EXAMPLE OF GOOD OUTPUT: Summary: Migrated database connection pooling and attended sprint planning Completed work: - Refactored connection pool config in user-service, tuned max connections and timeout settings - Investigated slow query alerts, identified a missing index on the orders table - Triaged dependency vulnerability in auth library - Updated API rate limiting doc with new per-tenant thresholds Meetings: - Sprint Planning — agreed on migration timeline for connection pooling - Platform Sync — discussed read replica promotion strategy Infrastructure / Cloud: - RDS, CloudWatch: Checked performance metrics for slow queries - ElasticCache reviewed connection metrics after pool changes Code changes: - backend: modified database config and connection middleware, added timeout env vars Key outcomes: - Connection pool PR approved and merged, deploying to staging tomorrow Time breakdown: - Total tracked: 7h 12m - Coding / Development: 3h 12m - Infrastructure / IaC: 1h 45m - Meetings: 1h 10m - Documentation: 52m Observations: Development-focused day split between coding and database performance investigation. END OF EXAMPLE. Rules: - Never invent work that is not supported by evidence. - Never infer meeting topics beyond the title shown in the data. - If something is ambiguous, say ‘worked on’ rather than assuming the purpose. - Mention repositories, tickets, AWS services and technologies when present. - Be concrete. - Avoid generic phrases like “worked on various tasks, multiple activities, collaborated with the team unless meeting ” - If evidence is weak, state only what is supported. - If uncertain, omit rather than guess. Prompt engineering lesson: This prompt combines three techniques. First, chain-of-thought reasoning — the 5-step internal thinking process forces the LLM to group related evidence before writing. Without it, a git commit about S3 policies, a browser visit to CloudFormation, and a ticket about bucket encryption would show up as three separate bullets. With it, they merge into one coherent activity: deployed S3 bucket policies for compliance. Second, one-shot example — giving the LLM a concrete target to match in tone, specificity, and structure. It sees what “good” looks like and mirrors it. Third, evidence priority hierarchy — this prevents hallucination. If git says you spent 2 hours on infra but browser history only shows a quick glance at CloudFormation, git wins. The explicit “never invent, if uncertain, omit” rules mean the summary only contains what actually happened. The CLI is simple to use. Run it with no arguments for today, or pass a date to generate or regenerate any past day: Shelldaily-log generate today's log \daily-log 2026-06-15 generate for a specific date weekly-log Friday summary of the week standup convert today's log into Slack standup format A cron job runs the pipeline at 5:30 PM on weekdays. By the time I close my laptop, the log is generated: None30 17 1-5 daily-log Weekday work logs 0 17 5 weekly-log Friday weekly rollup The weekly rollup feeds all five daily summaries into a single prompt asking for the week’s highlights. Layered summarization; each level compresses further. The standup command converts the daily log into Slack-ready bullets and copies it to clipboard. A React dashboard provides a calendar view of all logs, with click-to-view and copy-to-clipboard for pasting into timesheets. The Express backend exposes a “Generate Now” API endpoint for regenerating any specific date on demand. Works reliably: Unreliable 50/50 : Known gaps: After running this for a couple of months, the most valuable thing isn’t the work log itself — it’s the patterns. I noticed I had 5–6 meetings every Tuesday. Six. When I saw that number staring back at me from the weekly rollup, I blocked two of them, moved it around and got 1.5 hours back every week, just from seeing that data. Some days the system shows 2 meetings, 21 commits. Other days it would show 4 hours of deep AWS work with nothing visible to “show” for it. The point isn’t optimization — it’s awareness. Everyday is different and without this system I genuinely cannot reconstruct what I did by the end of day. Now I can see it — meetings-heavy day vs coding day vs research day, and that visibility alone is valuable When someone asks “what have you been working on this sprint?” I have an answer backed by data, not memory. The beauty of this architecture is that it’s entirely customizable: The full system is ~1000 lines of Python, ~100 lines of bash, and a Node.js dashboard. No complex dependencies, no infrastructure to maintain, no monthly costs beyond whatever LLM access you already have. You don’t need a fancy AI platform to build useful AI tooling. The pattern is straightforward: The hardest part wasn’t the LLM. It was figuring out which 8% of my daily data actually matters and once you see it laid out, you start noticing things about you work habits you never would have caught otherwise. How I built an Automated Work Activity Tracker with Local Data and AI Summarization https://pub.towardsai.net/how-i-built-an-automated-work-activity-tracker-with-local-data-and-ai-summarization-09f9bb5cb819 was originally published in Towards AI https://pub.towardsai.net on Medium, where people are continuing the conversation by highlighting and responding to this story.