<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hafid Idrissi</title>
    <description>The latest articles on DEV Community by Hafid Idrissi (@hafididrissi).</description>
    <link>https://dev.to/hafididrissi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4096275%2F294980ed-8006-4b98-a997-3ea91a8a9595.png</url>
      <title>DEV Community: Hafid Idrissi</title>
      <link>https://dev.to/hafididrissi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hafididrissi"/>
    <language>en</language>
    <item>
      <title>Crewlo: building a voxel workspace around coding agents</title>
      <dc:creator>Hafid Idrissi</dc:creator>
      <pubDate>Mon, 28 Sep 2026 12:13:12 +0000</pubDate>
      <link>https://dev.to/hafididrissi/crewlo-building-a-voxel-workspace-around-coding-agents-1d92</link>
      <guid>https://dev.to/hafididrissi/crewlo-building-a-voxel-workspace-around-coding-agents-1d92</guid>
      <description>&lt;p&gt;Running several coding agents creates an interface problem: where did a request go, what is the agent doing, and where is its reply?&lt;/p&gt;

&lt;p&gt;I'm developing &lt;strong&gt;Crewlo&lt;/strong&gt;, an open-source desktop workspace that puts those interactions in a voxel studio. You send a mission, follow activity, and open an agent's conversation or terminal to inspect the result.&lt;/p&gt;

&lt;p&gt;Crewlo is an independently maintained fork of &lt;a href="https://github.com/chaitanyagiri/munder-difflin" rel="noopener noreferrer"&gt;Munder Difflin&lt;/a&gt;, by Chaitanya Giri and contributors. It retains the upstream agent-engine foundation. My work focuses on the procedural voxel studio, the mission interface, activity labels and Telegram/WhatsApp transports.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8wwoknviwoxb1eg940it.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8wwoknviwoxb1eg940it.png" alt="Illustrated Crewlo studio with voxel characters and a reply panel; not a live agent recording" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Illustrated interface demo. The scene above is not evidence of a live AI task.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the scene connected to the work
&lt;/h2&gt;

&lt;p&gt;The inherited stack includes Electron, React, PixiJS and xterm.js. Crewlo adds its studio to that foundation rather than replacing the provider sessions and orchestration engine.&lt;/p&gt;

&lt;p&gt;The agent roster and execution events remain the source of operational state. Where execution hooks are available, activity labels reflect those events. Selecting a character opens the conversation or terminal so the user can inspect what actually happened.&lt;/p&gt;

&lt;p&gt;That distinction matters. A moving character cannot establish that code was changed correctly. A reply must still be read, and a task's result must still be checked. Crewlo's workshop, terrace and break room are visual zones, not independent autonomous teams.&lt;/p&gt;

&lt;p&gt;Cosmetic breaks are reserved for confirmed idle agents. They are rendered locally and do not require model prompts. Unknown or blocked states should not be disguised as an agent enjoying a break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the mission and its reply easy to find
&lt;/h2&gt;

&lt;p&gt;The mission composer is the starting point. Conversations and terminals sit alongside tasks, results and requests for approval or input.&lt;/p&gt;

&lt;p&gt;Message delivery has an explicit paused state and a Resume control. The interface needs to make this visible: an unanswered request may be waiting for user action rather than running in the background.&lt;/p&gt;

&lt;p&gt;Crewlo includes presets for Claude Code, Codex, Gemini CLI and other providers. A preset is configuration, not proof that every provider supports every coordination or messaging feature. Users supply their own authenticated CLI, with the provider's usual permissions, usage limits and costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Telegram and WhatsApp reuse existing sessions
&lt;/h2&gt;

&lt;p&gt;The phone integrations route messages to connected agents through the existing queues and delivery controls. They do not create a separate remote permission system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telegram&lt;/strong&gt; uses the Bot API. A basic real request and named agent reply have been verified in Telegram Web. This does not establish every agent, reconnection or physical-phone scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp is experimental.&lt;/strong&gt; It uses Meta Cloud API and signed webhooks, and requires the appropriate Meta setup plus an HTTPS callback forwarded to the local receiver. Full live end-to-end acceptance testing remains open.&lt;/p&gt;

&lt;p&gt;Both channels use pairing and desktop confirmation. Crewlo must remain open and the computer must stay awake. They do not remotely approve tools or resume paused delivery.&lt;/p&gt;

&lt;p&gt;Workspace state is stored locally, but the app is not inherently offline: connected cloud models and messaging services receive the data required for their work. Messaging credentials use an OS-encrypted vault; that does not mean the conversation history is encrypted by these features.&lt;/p&gt;

&lt;h2&gt;
  
  
  What has actually been checked
&lt;/h2&gt;

&lt;p&gt;One concrete Windows integration check used a real Codex session in the packaged app: Codex read a temporary file and returned its exact marker through Crewlo's IPC and terminal path. This verifies a narrow connection, not a complete autonomous development workflow.&lt;/p&gt;

&lt;p&gt;Windows and macOS preview packages are available. The Windows installer is unsigned, and the Mac builds are not notarized. Packaging checks and a successful local run do not replace testing on other people's machines. Linux runtime validation remains open.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/HafidIdrissi/Crewlo#current-status" rel="noopener noreferrer"&gt;README&lt;/a&gt; separates verified scope from remaining limits. The website's sample missions and gallery animations are illustrated demonstrations, so they should not be mistaken for live execution recordings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the preview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/HafidIdrissi/Crewlo" rel="noopener noreferrer"&gt;Source, Windows/macOS previews and setup instructions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hafididrissi.github.io/Crewlo/" rel="noopener noreferrer"&gt;Interactive illustrated studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/HafidIdrissi/Crewlo/issues" rel="noopener noreferrer"&gt;Report a reproducible issue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For installation reports, the OS, app version, provider CLI and reproduction steps are especially useful. Please remove credentials and private project data from logs before sharing them.&lt;/p&gt;

&lt;p&gt;The original MIT copyright notices remain preserved; bundled third-party assets retain their own licenses. Crewlo's attribution and engineering notes are linked from the repository.&lt;/p&gt;

&lt;p&gt;If you run several coding agents, which information do you need immediately: progress, a request for your input, or the evidence behind a completed result?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Writing disclosure: this article was drafted with AI assistance from Crewlo's repository documentation and validation notes.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>showdev</category>
      <category>agents</category>
    </item>
    <item>
      <title>Why I Built a Privacy-First Time Tracker with Python, Tkinter, and SQLite</title>
      <dc:creator>Hafid Idrissi</dc:creator>
      <pubDate>Thu, 27 Aug 2026 20:41:20 +0000</pubDate>
      <link>https://dev.to/hafididrissi/why-i-built-a-privacy-first-time-tracker-with-python-tkinter-and-sqlite-4bbm</link>
      <guid>https://dev.to/hafididrissi/why-i-built-a-privacy-first-time-tracker-with-python-tkinter-and-sqlite-4bbm</guid>
      <description>&lt;p&gt;Most time-tracking applications require an account, store activity in the cloud, or collect more data than users expect.&lt;/p&gt;

&lt;p&gt;I wanted a simpler alternative: a Windows application that records activity locally, works without an account, and sends no telemetry.&lt;/p&gt;

&lt;p&gt;That idea became &lt;a href="https://github.com/HafidIdrissi/Time-Tracker" rel="noopener noreferrer"&gt;Local Time Tracker&lt;/a&gt;, a free and open-source desktop application built with Python, Tkinter, and SQLite.&lt;/p&gt;

&lt;p&gt;In this article, I will explain what the application measures, the architecture behind it, the privacy trade-offs I had to make, and what I learned while preparing the project for external contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What problem does it solve?
&lt;/h2&gt;

&lt;p&gt;I wanted to answer a basic question: &lt;strong&gt;where does my time on Windows actually go?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Manual timers are useful, but they depend on remembering to start and stop them. Cloud-based tracking tools can automate this, but window titles and browser-tab names may reveal document names, searches, email subjects, or client information.&lt;/p&gt;

&lt;p&gt;Local Time Tracker takes a deliberately narrow approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it follows the foreground Windows application;&lt;/li&gt;
&lt;li&gt;it records the visible window title;&lt;/li&gt;
&lt;li&gt;it detects keyboard and mouse inactivity;&lt;/li&gt;
&lt;li&gt;it groups samples into continuous activity periods;&lt;/li&gt;
&lt;li&gt;it produces daily and seven-day summaries;&lt;/li&gt;
&lt;li&gt;it stores everything in a local SQLite database;&lt;/li&gt;
&lt;li&gt;it requires no account, server, advertising, or telemetry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tracker measures &lt;strong&gt;foreground focus&lt;/strong&gt;, not human productivity. If a window is focused, that does not prove that the user is reading or actively working in it. This distinction is important when interpreting the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;The application is split into a few focused layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Windows APIs
    |
    v
Activity snapshot
    |
    v
Tracking and state transitions
    |
    v
Local SQLite database
    |
    +--&amp;gt; Tkinter dashboard and analysis
    |
    +--&amp;gt; Standalone offline HTML reports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Collecting Windows activity
&lt;/h3&gt;

&lt;p&gt;The Windows provider uses &lt;code&gt;pywin32&lt;/code&gt; and &lt;code&gt;psutil&lt;/code&gt; to read the foreground process, its window title, and the time since the latest keyboard or mouse input.&lt;/p&gt;

&lt;p&gt;Each observation is only a point-in-time snapshot. It contains the application, title, and idle duration, but it does not yet represent a complete activity period.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Turning samples into periods
&lt;/h3&gt;

&lt;p&gt;The tracker compares each new snapshot with the current state. A new period starts when the application, window title, or idle state changes. Otherwise, the current period is extended.&lt;/p&gt;

&lt;p&gt;The core idea can be simplified as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IDLE&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idle_seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;idle_threshold&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;current_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;close_current_period&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;start_new_period&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;extend_current_period&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Idle detection has an additional subtlety. If the application checks every few seconds, it discovers inactivity only after the threshold has already been crossed. The tracker compensates for that excess idle time so the transition is recorded closer to when inactivity actually began.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Storing data locally with SQLite
&lt;/h3&gt;

&lt;p&gt;SQLite was a natural fit because it is embedded, reliable, and requires no separate database server. Activity periods can be queried by date and aggregated without sending anything outside the computer.&lt;/p&gt;

&lt;p&gt;The installed application stores its data under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%LOCALAPPDATA%\LocalTimeTracker\
|-- data\activity.db
`-- reports\
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLite also makes the application easy to install and back up, but local does not automatically mean encrypted. The database relies on the security of the Windows account and disk. This limitation is documented clearly because window titles may contain sensitive information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Python, Tkinter, and SQLite?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Python
&lt;/h3&gt;

&lt;p&gt;Python made it possible to keep the activity model, analytics, reporting, and Windows integration understandable in one codebase. Its standard library also covers much of the required data and HTML-processing work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tkinter
&lt;/h3&gt;

&lt;p&gt;Tkinter keeps the desktop interface lightweight and avoids introducing a browser runtime or a separate frontend build system. It is not the most visually flexible GUI toolkit, but it matches the project's goal: a focused Windows utility that remains approachable to contributors.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQLite
&lt;/h3&gt;

&lt;p&gt;SQLite provides transactional local storage with no account, network connection, or service configuration. That directly supports the privacy and offline requirements.&lt;/p&gt;

&lt;p&gt;These choices are not universally "best." They are appropriate for the constraints of this project: a small Windows desktop application, local data, straightforward installation, and a codebase that new contributors can understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normalizing browser titles
&lt;/h2&gt;

&lt;p&gt;Browser windows often expose titles such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gmail - Google Chrome
Documentation – Mozilla Firefox
Project — Microsoft Edge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If those suffixes are stored as-is, the analysis becomes noisy. The analytics layer recognizes supported browser executables and removes known browser suffixes, including hyphens, en dashes, and em dashes. Visits to the same tab can then be grouped under a cleaner title such as &lt;code&gt;Gmail&lt;/code&gt; or &lt;code&gt;Documentation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The application currently supports title formats from Chrome, Edge, Firefox, Brave, Opera, and Vivaldi. It does not inspect the page body or send browser activity to an external service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline analysis and reports
&lt;/h2&gt;

&lt;p&gt;The Tkinter interface provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;live foreground activity;&lt;/li&gt;
&lt;li&gt;active and idle totals;&lt;/li&gt;
&lt;li&gt;recent activity periods;&lt;/li&gt;
&lt;li&gt;daily and seven-day analysis;&lt;/li&gt;
&lt;li&gt;rankings by category, application, and browser tab;&lt;/li&gt;
&lt;li&gt;hourly and daily charts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reports are generated as standalone HTML files with no external scripts, fonts, trackers, or network dependencies. They can be opened later without an internet connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building privacy into the contribution process
&lt;/h2&gt;

&lt;p&gt;Opening an activity tracker to contributors creates an unusual documentation challenge: a normal bug report or screenshot can accidentally expose private window titles.&lt;/p&gt;

&lt;p&gt;The contribution and security guides therefore ask people to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use synthetic demonstration data;&lt;/li&gt;
&lt;li&gt;never upload a real &lt;code&gt;activity.db&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;avoid screenshots containing personal window titles;&lt;/li&gt;
&lt;li&gt;describe privacy and compatibility impacts in pull requests;&lt;/li&gt;
&lt;li&gt;use private vulnerability reporting for security issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This taught me that privacy is not only a runtime feature. It must also influence documentation, testing, issue templates, support, and review practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and releases
&lt;/h2&gt;

&lt;p&gt;The project uses Python's &lt;code&gt;unittest&lt;/code&gt; framework for tracking transitions, analytics, browser-title normalization, categories, database behavior, and report generation.&lt;/p&gt;

&lt;p&gt;GitHub Actions runs the test suite for pull requests and changes to the main branch. Tagged releases run the tests, build the Windows application, create an installer, and publish a SHA-256 checksum beside it.&lt;/p&gt;

&lt;p&gt;The current installer may still trigger a Windows SmartScreen warning because it is not yet signed with a trusted Authenticode certificate. I prefer to document that limitation instead of presenting the release as more mature than it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;The most useful lessons from this project have been:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Privacy promises must become architectural constraints.&lt;/strong&gt; A "no telemetry" claim should be visible in the code, reports, packaging, and documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic tracking needs honest language.&lt;/strong&gt; Foreground focus is useful evidence, but it is not a perfect measure of attention or productivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge cases live in ordinary strings.&lt;/strong&gt; Browser names, capitalization, empty titles, Unicode dashes, and unusual Windows titles all deserve tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributor experience is part of the product.&lt;/strong&gt; Focused issues, acceptance criteria, test commands, and privacy-safe examples make participation easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limitations build trust.&lt;/strong&gt; Windows-only support, sampling gaps, unsigned installers, and unencrypted local data should be stated explicitly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it or contribute
&lt;/h2&gt;

&lt;p&gt;Local Time Tracker is available under the MIT License:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/HafidIdrissi/Time-Tracker" rel="noopener noreferrer"&gt;Source code and documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/HafidIdrissi/Time-Tracker/releases/latest" rel="noopener noreferrer"&gt;Latest Windows release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/HafidIdrissi/Time-Tracker/contribute" rel="noopener noreferrer"&gt;Beginner-friendly contribution opportunities&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, I would especially value feedback about the first-run experience, tracking accuracy, and privacy controls that should be prioritized next.&lt;/p&gt;

&lt;p&gt;If you are interested in contributing, please use synthetic data and start with a focused issue. Questions about the architecture and trade-offs are welcome in the comments.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>privacy</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
