<?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: kamin deng</title>
    <description>The latest articles on DEV Community by kamin deng (@kamindeng).</description>
    <link>https://dev.to/kamindeng</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3868905%2F1c7724b3-6dd4-452e-b0f8-c47b4e719dfb.png</url>
      <title>DEV Community: kamin deng</title>
      <link>https://dev.to/kamindeng</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kamindeng"/>
    <language>en</language>
    <item>
      <title>One C++ Interface Across Linux and Multiple RTOSes: The Design Value of dp_sdk_core</title>
      <dc:creator>kamin deng</dc:creator>
      <pubDate>Sun, 19 Apr 2026 08:55:17 +0000</pubDate>
      <link>https://dev.to/kamindeng/write-product-logic-once-swap-rtos-and-boards-later-why-dpsdkcore-exists-2047</link>
      <guid>https://dev.to/kamindeng/write-product-logic-once-swap-rtos-and-boards-later-why-dpsdkcore-exists-2047</guid>
      <description>&lt;h1&gt;
  
  
  One C++ Interface Across Linux and Multiple RTOSes: The Design Value of &lt;code&gt;dp_sdk_core&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/KaminDeng/dp_sdk_core" rel="noopener noreferrer"&gt;https://github.com/KaminDeng/dp_sdk_core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many embedded projects move quickly in the beginning.&lt;br&gt;&lt;br&gt;
A workflow is validated on Linux first, then moved to an MCU. One board is supported first, then more hardware variants follow. One RTOS is adopted first, and portability is considered later.&lt;/p&gt;

&lt;p&gt;The real difficulty is usually not whether the first version can run, but whether the codebase remains stable when platforms start changing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;can business logic remain stable when the OS changes?&lt;/li&gt;
&lt;li&gt;can Linux and multiple RTOSes share a reasonably aligned programming model?&lt;/li&gt;
&lt;li&gt;can board, chip, or system changes stay confined to a small number of boundaries?&lt;/li&gt;
&lt;li&gt;can C++ abstractions preserve expressiveness on MCUs without letting resource cost get out of control?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;dp_sdk_core&lt;/code&gt; is aimed at exactly those problems.&lt;br&gt;&lt;br&gt;
It is not “one more wrapper layer.” It is an attempt to provide a core layer better suited for long-term embedded evolution.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Its real value is not just “cross-platform support”
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;dp_sdk_core&lt;/code&gt; consists of three core modules:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dp_osal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OS abstraction&lt;/td&gt;
&lt;td&gt;Unifies threads, mutexes, queues, timers, semaphores, and related primitives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dp_hal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hardware abstraction&lt;/td&gt;
&lt;td&gt;Unifies UART, GPIO, SPI, I2C, ADC, PWM, and related peripheral interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dp_device&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Device management&lt;/td&gt;
&lt;td&gt;Unifies registration, lookup, open/close control, and lifecycle handling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The dependency structure is intentionally restrained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;device  -&amp;gt;  hal   (public)
device  -&amp;gt;  osal  (private)
hal     -&amp;gt;  independent
osal    -&amp;gt;  POSIX / CMSIS-OS backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What matters is not only that the project is split into three modules.&lt;br&gt;&lt;br&gt;
What matters is that this split gives change a clear home:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OS differences are contained in &lt;code&gt;dp_osal&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;hardware differences are contained in &lt;code&gt;dp_hal&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;device lifecycle concerns are contained in &lt;code&gt;dp_device&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For embedded systems that must evolve over time, those boundaries matter more than how fast a single driver can be wrapped.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. CMSIS-OS is used to adapt multiple RTOSes and align Linux / RTOS programming interfaces
&lt;/h2&gt;

&lt;p&gt;This is one of the most important characteristics of &lt;code&gt;dp_sdk_core&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Many embedded projects do not fail because they support only one system. They fail because, even when multiple systems are supported, the programming model becomes fragmented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux uses one threading and synchronization style&lt;/li&gt;
&lt;li&gt;FreeRTOS uses another&lt;/li&gt;
&lt;li&gt;RT-Thread or Zephyr requires another adaptation layer&lt;/li&gt;
&lt;li&gt;the business logic stays conceptually similar, but the upper-layer programming style does not&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;dp_sdk_core&lt;/code&gt; does not expose each RTOS’s native API directly to the application layer.&lt;br&gt;&lt;br&gt;
Instead, it stabilizes the upper-layer system programming model through &lt;code&gt;dp_osal&lt;/code&gt;, and converges different RTOSes through the &lt;strong&gt;CMSIS-OS interface&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;upper-layer code is primarily written against one OSAL interface,&lt;/li&gt;
&lt;li&gt;RTOS-specific variation is concentrated in the adaptation layer,&lt;/li&gt;
&lt;li&gt;any RTOS that can be adapted to CMSIS-OS can, in principle, fit into this framework.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The project has already been adapted and validated on &lt;strong&gt;FreeRTOS, RT-Thread, and Zephyr&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
This is why its value is not merely “multi-RTOS support,” but a &lt;strong&gt;unified interface model across Linux and multiple RTOS environments&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Structurally, it solves the problem of where change should live
&lt;/h2&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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fdp_sdk_core%40master%2Fdocs%2Fblog%2Fimages%2Fdp-sdk-core-arch.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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fdp_sdk_core%40master%2Fdocs%2Fblog%2Fimages%2Fdp-sdk-core-arch.png" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main value of this diagram is not just its layered layout.&lt;br&gt;&lt;br&gt;
It shows several engineering realities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Application logic should remain as stable as possible&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Upper-layer business code depends on core abstractions rather than direct platform APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OS differences are concentrated in &lt;code&gt;dp_osal&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Linux / POSIX and RTOS environments both expose capabilities upward through one OSAL model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware differences are concentrated in &lt;code&gt;dp_hal&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
UART, GPIO, SPI, I2C, and similar peripheral implementations do not directly leak into business code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Device lifecycle is concentrated in &lt;code&gt;dp_device&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Registration, lookup, open/close behavior, and centralized management are not scattered across modules.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cross-platform projects rarely fail because differences exist.&lt;br&gt;&lt;br&gt;
They fail because those differences have no disciplined boundary.&lt;br&gt;&lt;br&gt;
That is one of the clearest strengths of &lt;code&gt;dp_sdk_core&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. From an evolution perspective, it makes migration and product expansion more controllable
&lt;/h2&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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fdp_sdk_core%40master%2Fdocs%2Fblog%2Fimages%2Fdp-sdk-core-migration.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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fdp_sdk_core%40master%2Fdocs%2Fblog%2Fimages%2Fdp-sdk-core-migration.png" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compared with the architecture diagram, this migration view is closer to the project’s practical engineering value.&lt;/p&gt;

&lt;p&gt;The left side reflects a common embedded state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;business logic, RTOS APIs, vendor HAL calls, and device initialization order are all mixed together&lt;/li&gt;
&lt;li&gt;the firmware runs, but any system, hardware, or product-profile change quickly expands the modification surface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The right side reflects the state &lt;code&gt;dp_sdk_core&lt;/code&gt; is designed to establish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;upper-layer business code depends on stable contracts&lt;/li&gt;
&lt;li&gt;OS differences are absorbed through POSIX / CMSIS-OS paths&lt;/li&gt;
&lt;li&gt;hardware differences are absorbed through HAL ports&lt;/li&gt;
&lt;li&gt;device lifecycle is gathered into the Device layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is also why this design is especially suitable when a team moves from a demo mindset to a product-line mindset.&lt;br&gt;&lt;br&gt;
A demo optimizes for “getting it running.”&lt;br&gt;&lt;br&gt;
A product line optimizes for “keeping change under control.”&lt;/p&gt;




&lt;h2&gt;
  
  
  5. It is not only about abstraction, but also about the engineering value of C++ interfaces
&lt;/h2&gt;

&lt;p&gt;Many embedded abstraction layers eventually become little more than renamed C APIs. They may use C++ syntax, but they do not significantly improve expressiveness or cost control.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dp_sdk_core&lt;/code&gt; is more interesting because it does not give up the engineering strengths of C++.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Production paths prefer CRTP instead of default all-virtual interfaces
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;dp_osal&lt;/code&gt; and &lt;code&gt;dp_hal&lt;/code&gt;, many core interfaces are built around CRTP.&lt;/p&gt;

&lt;p&gt;The point is not to appear “more template-oriented.”&lt;br&gt;&lt;br&gt;
The point is compile-time binding of implementation types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hot paths are easier to inline&lt;/li&gt;
&lt;li&gt;a vtable is not introduced by default&lt;/li&gt;
&lt;li&gt;abstraction remains available without automatically becoming a runtime burden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That matters on MCUs because abstraction becomes risky when runtime cost is spread across every path by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Virtual wrappers are used only when test injection is needed
&lt;/h3&gt;

&lt;p&gt;The project also provides &lt;code&gt;dp_osal_virtual.h&lt;/code&gt; and &lt;code&gt;dp_hal_virtual.h&lt;/code&gt; for host-side dependency injection and GMock scenarios.&lt;/p&gt;

&lt;p&gt;That reflects a clear design strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;shipping / production paths&lt;/strong&gt; prefer CRTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;host-test paths&lt;/strong&gt; can switch to virtual wrappers only when mocking is required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So testability is preserved, but test-related runtime cost is not forced onto all firmware paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 This is disciplined use of modern C++
&lt;/h3&gt;

&lt;p&gt;From an engineering perspective, the project uses several C++ features that are genuinely valuable for long-term maintenance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;typed interfaces instead of raw pointer-heavy plumbing&lt;/li&gt;
&lt;li&gt;RAII-style helpers such as lock guards&lt;/li&gt;
&lt;li&gt;templates combined with compile-time switches for trimming and portability&lt;/li&gt;
&lt;li&gt;clearer separation of interfaces and implementations for collaborative development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the notable characteristic is not merely that &lt;code&gt;dp_sdk_core&lt;/code&gt; uses C++17.&lt;br&gt;&lt;br&gt;
It is that it &lt;strong&gt;uses C++ interface design to improve engineering structure while keeping the added cost under control&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. &lt;code&gt;dp_device&lt;/code&gt; addresses one of the most failure-prone late-stage problems: device lifecycle
&lt;/h2&gt;

&lt;p&gt;Early in an embedded project, teams tend to focus on whether drivers work. Very few teams treat device lifecycle management as a first-class design concern from the beginning.&lt;/p&gt;

&lt;p&gt;Once the system grows, the following problems usually appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;device initialization order becomes unclear&lt;/li&gt;
&lt;li&gt;some modules reopen devices while others close them too early&lt;/li&gt;
&lt;li&gt;naming and type expectations remain informal conventions&lt;/li&gt;
&lt;li&gt;lookup, registration, and cleanup logic spreads across modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;dp_sdk_core&lt;/code&gt; turns that into a dedicated layer through &lt;code&gt;dp_device&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Device&lt;/code&gt; provides a unified base for naming, typing, and open/close reference counting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DeviceManager&lt;/code&gt; centralizes registration, lookup, iteration, and removal&lt;/li&gt;
&lt;li&gt;the registry uses a fixed-size array with an explicit upper bound&lt;/li&gt;
&lt;li&gt;type handling relies on &lt;code&gt;DeviceType&lt;/code&gt; instead of RTTI&lt;/li&gt;
&lt;li&gt;synchronization can be protected by OSAL primitives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This may be less visually obvious than a driver optimization, but it is critical for long-term maintainability.&lt;br&gt;&lt;br&gt;
It turns device usage rules from “things the team is expected to remember” into “things the framework enforces.”&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Its treatment of resource cost is “controlled,” not “pretending there is no cost”
&lt;/h2&gt;

&lt;p&gt;This is a very important point.&lt;/p&gt;

&lt;p&gt;Many embedded abstraction layers talk about elegance, consistency, and modernization, but avoid the harder question: &lt;strong&gt;what exactly does the abstraction cost, and where is it appropriate?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dp_sdk_core&lt;/code&gt; takes a more grounded approach. It does not market all paths as “zero cost.” Instead, it tries to keep costs analyzable, trimmable, and verifiable.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 What cost is intentionally reduced
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dp_hal&lt;/code&gt; is primarily header-only + CRTP, with the goal of eliminating most abstraction overhead at compile time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dp_osal&lt;/code&gt; core interfaces are not all-virtual by default&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dp_device&lt;/code&gt; uses fixed-size arrays instead of default heap allocation&lt;/li&gt;
&lt;li&gt;type handling avoids RTTI where practical&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7.2 What cost is explicitly acknowledged
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;threads, thread pools, and timers are not cost-free primitives&lt;/li&gt;
&lt;li&gt;once &lt;code&gt;std::function&lt;/code&gt;, synchronization objects, and extra state are introduced, RAM / Flash impact must be evaluated&lt;/li&gt;
&lt;li&gt;switching to virtual wrappers for host-side testing naturally adds runtime dispatch cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is much more credible than pretending abstraction is free.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.3 The MCU feasibility table is already a good way to explain this
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Target MCU&lt;/th&gt;
&lt;th&gt;Feasibility&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cortex-M4 / M7, RAM ≥ 192 KB (CCM recommended)&lt;/td&gt;
&lt;td&gt;✅ current full-test profile is feasible&lt;/td&gt;
&lt;td&gt;already has a practical build-and-validate basis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cortex-M3 / small-memory M4, RAM 64–128 KB&lt;/td&gt;
&lt;td&gt;⚠️ feasible only with aggressive trimming&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;test_all&lt;/code&gt; must be disabled, features reduced, and the result re-measured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cortex-M0 / M0+, RAM ≤ 64 KB&lt;/td&gt;
&lt;td&gt;❌ not something the current profile should claim directly&lt;/td&gt;
&lt;td&gt;requires a dedicated tiny profile and measured proof&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The importance of this table is not only the conclusion itself.&lt;br&gt;&lt;br&gt;
More importantly, it shows an engineering attitude:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not every MCU should be treated as equally suitable for the same profile&lt;/li&gt;
&lt;li&gt;architectural value does not remove the need to respect hardware limits&lt;/li&gt;
&lt;li&gt;whether the abstraction is viable depends on target profile, trimming strategy, and measurement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the project’s stance on cost is not “there is no cost.”&lt;br&gt;&lt;br&gt;
It is that &lt;strong&gt;cost can be controlled and reasoned about through CRTP, fixed limits, feature trimming, and platform validation&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. The best-fit workflow is to validate on Linux first, then move smoothly to RTOS and hardware targets
&lt;/h2&gt;

&lt;p&gt;Looking at its structure and interface model, &lt;code&gt;dp_sdk_core&lt;/code&gt; fits a workflow like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;validate core business logic in Linux / POSIX first&lt;/li&gt;
&lt;li&gt;keep upper-layer programming stable through unified OSAL / HAL interfaces&lt;/li&gt;
&lt;li&gt;bring different RTOSes into the same model through CMSIS-OS adaptation&lt;/li&gt;
&lt;li&gt;finally switch to target-specific hardware implementations and complete MCU integration and measurement&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The benefits are direct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;business logic can be verified earlier on the host&lt;/li&gt;
&lt;li&gt;Linux and RTOS-side programming styles stay closer&lt;/li&gt;
&lt;li&gt;board and system changes stay more localized&lt;/li&gt;
&lt;li&gt;teams can treat business validation, RTOS adaptation, and hardware porting as one coherent engineering path&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  9. What kind of teams benefit most from this kind of core layer
&lt;/h2&gt;

&lt;p&gt;This type of architecture tends to pay off most when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;business logic must run in both Linux simulation and MCU firmware&lt;/li&gt;
&lt;li&gt;the product must span multiple RTOSes or multiple hardware platforms&lt;/li&gt;
&lt;li&gt;the team is already paying visible maintenance cost for platform glue code&lt;/li&gt;
&lt;li&gt;a reusable C++17 foundation layer is desired&lt;/li&gt;
&lt;li&gt;a more consistent Linux / multi-RTOS interface experience matters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By contrast, if the firmware is short-lived, one-off, and tied to a single fixed platform, a custom lightweight platform layer may still be enough.&lt;/p&gt;

&lt;p&gt;The value of &lt;code&gt;dp_sdk_core&lt;/code&gt; appears most clearly in &lt;strong&gt;long-term evolution&lt;/strong&gt;, not in &lt;strong&gt;one-time bring-up&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Conclusion
&lt;/h2&gt;

&lt;p&gt;What makes &lt;code&gt;dp_sdk_core&lt;/code&gt; worth attention is not merely that it wraps threads, UART, or GPIO.&lt;br&gt;&lt;br&gt;
Its real value is that it places several of the most failure-prone parts of embedded development into one coherent structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to keep Linux and multiple RTOSes closer to one programming model&lt;/li&gt;
&lt;li&gt;how to converge different RTOSes through CMSIS-OS&lt;/li&gt;
&lt;li&gt;how to isolate hardware differences through HAL ports&lt;/li&gt;
&lt;li&gt;how to centralize device lifecycle handling&lt;/li&gt;
&lt;li&gt;how to retain C++ expressiveness while controlling resource cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From that perspective, it is not just “another abstraction layer.”&lt;br&gt;&lt;br&gt;
It is a &lt;strong&gt;more stable, more unified, and more evolvable foundation for embedded development across Linux, RTOS, and hardware platforms&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>iot</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Open Source: Control Claude Code / Codex CLI Entirely from Your Phone with Feishu (Lark) — Approve, Choose, and Send Commands on the Go</title>
      <dc:creator>kamin deng</dc:creator>
      <pubDate>Thu, 09 Apr 2026 04:10:40 +0000</pubDate>
      <link>https://dev.to/kamindeng/open-source-control-claude-code-codex-cli-entirely-from-your-phone-with-feishu-lark-approve-3opn</link>
      <guid>https://dev.to/kamindeng/open-source-control-claude-code-codex-cli-entirely-from-your-phone-with-feishu-lark-approve-3opn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Ever had this happen? Claude Code has been running for a while and finally pops up a permission prompt — but you've already walked away to grab coffee.&lt;/p&gt;

&lt;p&gt;Or Codex finishes an entire task, and you only realize it when you come back — ten minutes too late.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today I'm open-sourcing a little tool I use daily — &lt;strong&gt;Agent Notifier&lt;/strong&gt;. It routes all interactions from Claude Code and Codex CLI to Feishu, so you can handle everything from your phone without being chained to the terminal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub: &lt;a href="https://github.com/KaminDeng/agent_notifier" rel="noopener noreferrer"&gt;https://github.com/KaminDeng/agent_notifier&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Problem It Solves
&lt;/h2&gt;

&lt;p&gt;When you use Claude Code or Codex CLI for coding, the biggest pain point isn't that the AI isn't smart enough — it's the &lt;strong&gt;interaction gap&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The AI needs your approval to run a command → you're away from your desk → task stalls&lt;/li&gt;
&lt;li&gt;The AI offers three approaches and asks you to pick one → you don't see it → task stalls&lt;/li&gt;
&lt;li&gt;The task finishes → you have no idea → you've been waiting for nothing for 30 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At its core, these CLI tools &lt;strong&gt;have no mobile interaction layer&lt;/strong&gt;. You have to sit in front of the terminal, watching the output in real time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent Notifier's approach is simple: turn Feishu into your remote terminal controller.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pain Point&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stuck waiting for permission prompts at the terminal&lt;/td&gt;
&lt;td&gt;Feishu pushes interactive cards in real time — one tap on your phone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Want mobile access, but there's no official app&lt;/td&gt;
&lt;td&gt;Feishu is a full multi-platform app — iOS / Android / Mac / Windows / Web&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosted push notifications need a server, domain, and SSL&lt;/td&gt;
&lt;td&gt;Feishu's long-connection (WebSocket) mode requires no public IP — direct local connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise app approval is a bureaucratic nightmare&lt;/td&gt;
&lt;td&gt;Feishu's custom enterprise apps get instant approval; personal accounts work too — &lt;strong&gt;completely free&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running tasks across multiple terminals, notifications get tangled&lt;/td&gt;
&lt;td&gt;Multi-terminal parallel routing — each terminal gets its own delivery channel, no cross-talk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2. What It Looks Like
&lt;/h2&gt;

&lt;p&gt;Here are actual screenshots from the Feishu mobile app:&lt;/p&gt;

&lt;h3&gt;
  
  
  Permission Confirmation &amp;amp; Option Selection
&lt;/h3&gt;

&lt;p&gt;When Claude Code needs you to approve a command, or an AskUserQuestion prompt pops up with choices, Feishu sends you an interactive card:&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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Fpermission-confirm.jpg" 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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Fpermission-confirm.jpg" width="800" height="1722"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Fpermission-options.jpg" 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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Fpermission-options.jpg" width="800" height="1722"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Fask-user-question.jpg" 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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Fask-user-question.jpg" width="800" height="1722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Left: Permission confirmation — Allow / Deny / Allow for Session / Allow Always + text input&lt;br&gt;Center: Permission options — Tool option buttons (e.g. ExitPlanMode) + text input&lt;br&gt;Right: Option selection — AskUserQuestion with dynamic choices + Other + free-form input&lt;br&gt;Footer: project name · terminal ID (fifo:/tmp/claude-inject-ptsN) · session duration · timestamp&lt;/p&gt;

&lt;p&gt;Tap a button or type a response, and the action &lt;strong&gt;flows directly back to your local terminal&lt;/strong&gt; — as if you were typing on your keyboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Live Execution Summary
&lt;/h3&gt;

&lt;p&gt;Every time Claude invokes a tool, Feishu pushes a real-time execution summary card. Cards for the same task update in-place instead of flooding your chat:&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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Flive-execution.jpg" 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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Flive-execution.jpg" width="800" height="1722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Live execution summary — tool call table · in-place patch updates for the same task&lt;br&gt;Footer: project name · timestamp&lt;/p&gt;

&lt;h3&gt;
  
  
  Task Completion Notification
&lt;/h3&gt;

&lt;p&gt;When a task finishes, you get a completion card with a change summary, token usage, and duration stats. There's a text input at the bottom so you can continue the conversation right away:&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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Ftask-complete.jpg" 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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Ftask-complete.jpg" width="800" height="1722"&gt;&lt;/a&gt;&lt;br&gt;
   &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Ftask-complete-stats.jpg" 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%2Fcdn.jsdelivr.net%2Fgh%2FKaminDeng%2Fagent_notifier%40master%2Fdocs%2Fimages%2Ftask-complete-stats.jpg" width="800" height="1722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Left: Change summary + text input to continue chatting&lt;br&gt;Right: Test results table + text input to continue chatting&lt;br&gt;Footer: project name · session duration · timestamp · token usage breakdown (input / output / cache read / cache write)&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Supported Card Types
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Card Color&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Permission confirmation&lt;/td&gt;
&lt;td&gt;Orange&lt;/td&gt;
&lt;td&gt;Allow / Allow for Session / Deny + text input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AskUserQuestion (single choice)&lt;/td&gt;
&lt;td&gt;Orange&lt;/td&gt;
&lt;td&gt;Dynamic option buttons + Other + text input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AskUserQuestion (multi-part)&lt;/td&gt;
&lt;td&gt;Orange&lt;/td&gt;
&lt;td&gt;Q1 → Q2 → Q3 sent sequentially&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task complete&lt;/td&gt;
&lt;td&gt;Green&lt;/td&gt;
&lt;td&gt;Summary, duration, tokens + text input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abnormal exit&lt;/td&gt;
&lt;td&gt;Red&lt;/td&gt;
&lt;td&gt;Error details + text input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live execution summary&lt;/td&gt;
&lt;td&gt;Blue&lt;/td&gt;
&lt;td&gt;In-place patch updates for the same task&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Why Feishu Instead of WeChat / Telegram / Slack?
&lt;/h2&gt;

&lt;p&gt;This is the question I get asked the most, so let me address it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Feishu custom apps are free&lt;/strong&gt; — you can create one with a personal account, and enterprise approval is instant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feishu supports long connections (WebSocket)&lt;/strong&gt; — no public IP or domain required. Whether you're at home, at the office, or on a VPN, it just works&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feishu's interactive card system is incredibly powerful&lt;/strong&gt; — buttons, text inputs, tables, multi-column layouts — far beyond what a basic message notification can do&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feishu syncs natively across all devices&lt;/strong&gt; — phone, desktop, tablet, and web all receive and can interact with the same card&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No need to build a separate app&lt;/strong&gt; — the Feishu client itself becomes your remote control&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That said, the architecture uses a channel abstraction layer (&lt;code&gt;src/channels/&lt;/code&gt;), so adding support for other platforms is straightforward.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Get Started in 5 Minutes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js &amp;gt;= 18&lt;/li&gt;
&lt;li&gt;Python 3 (for the PTY terminal relay)&lt;/li&gt;
&lt;li&gt;A Feishu account&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone the repo&lt;/span&gt;
git clone https://github.com/KaminDeng/agent_notifier.git
&lt;span class="nb"&gt;cd &lt;/span&gt;agent_notifier

&lt;span class="c"&gt;# 2. Edit .env with your Feishu app credentials (auto-created on first run)&lt;/span&gt;
&lt;span class="c"&gt;#    FEISHU_APP_ID=your_app_id&lt;/span&gt;
&lt;span class="c"&gt;#    FEISHU_APP_SECRET=your_app_secret&lt;/span&gt;

&lt;span class="c"&gt;# 3. One-command install (handles all configuration automatically)&lt;/span&gt;
bash install.sh

&lt;span class="c"&gt;# 4. Reload your shell&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc  &lt;span class="c"&gt;# or source ~/.bashrc&lt;/span&gt;

&lt;span class="c"&gt;# 5. Use Claude Code as usual&lt;/span&gt;
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;install.sh&lt;/code&gt; automatically takes care of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing npm dependencies&lt;/li&gt;
&lt;li&gt;Writing Claude Code hooks to &lt;code&gt;~/.claude/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Injecting &lt;code&gt;claude&lt;/code&gt; / &lt;code&gt;codex&lt;/code&gt; shell wrapper functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting the Feishu listener and registering it for auto-start&lt;/strong&gt; (launchd on macOS, systemd on Linux)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Running &lt;code&gt;install.sh&lt;/code&gt; again is safe — it always cleans up before reinstalling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Feishu App Setup (3 Minutes)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to the &lt;a href="https://open.feishu.cn" rel="noopener noreferrer"&gt;Feishu Open Platform&lt;/a&gt; and create a custom enterprise app&lt;/li&gt;
&lt;li&gt;Copy the App ID / App Secret into your &lt;code&gt;.env&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Enable bot capabilities&lt;/li&gt;
&lt;li&gt;Set the event subscription to &lt;strong&gt;Long Connection&lt;/strong&gt; (no public IP needed)&lt;/li&gt;
&lt;li&gt;Add the event: &lt;code&gt;card.action.trigger&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Request permissions: &lt;code&gt;im:message&lt;/code&gt;, &lt;code&gt;im:message:send_as_bot&lt;/code&gt;, &lt;code&gt;im:chat:readonly&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Publish the app and add the bot to your target group&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No domain, no SSL certificates, no dedicated server.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Cross-Platform Support
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Service Management&lt;/th&gt;
&lt;th&gt;Auto-Start&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;launchd&lt;/td&gt;
&lt;td&gt;RunAtLoad + KeepAlive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux (systemd)&lt;/td&gt;
&lt;td&gt;systemd user service&lt;/td&gt;
&lt;td&gt;systemctl --user enable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux (SSH / no systemd)&lt;/td&gt;
&lt;td&gt;nohup + crontab &lt;a class="mentioned-user" href="https://dev.to/reboot"&gt;@reboot&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;crontab fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Uninstalling is a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash uninstall.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This cleans up all configuration, stops services, and removes hooks and shell injections.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. How It Works (Brief Overview)
&lt;/h2&gt;

&lt;p&gt;There are two main pipelines:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Code Pipeline:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude Hooks fire an event → hook-handler.js parses it → generates Feishu interactive card
                                                              ↓
User taps/types in Feishu → feishu-listener.js receives callback → injects input into local terminal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Codex CLI Pipeline:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pty-relay.py creates a PTY terminal proxy → captures Codex output → generates Feishu card
                                                              ↓
User taps/types in Feishu → feishu-listener.js receives callback → injects input via FIFO into terminal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terminal injection supports multiple methods: tmux, PTY FIFO, pty master direct write, and TIOCSTI — the best method is auto-detected.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Project Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agent_notifier/
├── install.sh / uninstall.sh          # Install / uninstall
├── hook-handler.js / live-handler.js  # Claude Hooks entry points (thin shims)
├── bin/
│   └── pty-relay.py                   # PTY terminal relay
├── src/
│   ├── apps/                          # App entry points (claude-hook, claude-live, feishu-listener, etc.)
│   ├── adapters/                      # Claude / Codex adapters
│   ├── channels/                      # Feishu channel (card rendering, client, interaction handling)
│   ├── core/                          # Low-level primitives (session store, terminal injector)
│   └── lib/                           # App-level services (env config, session state, terminal inject)
├── tests/                             # 81 test cases
└── scripts/                           # Debug / test scripts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Who Is This For?
&lt;/h2&gt;

&lt;p&gt;If any of the following apply to you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You use Claude Code or Codex CLI for daily coding&lt;/li&gt;
&lt;li&gt;You frequently step away from your desk and don't want tasks stalling on permission prompts&lt;/li&gt;
&lt;li&gt;You want to interact with your AI coding assistant from your phone&lt;/li&gt;
&lt;li&gt;You run multiple terminals simultaneously and need a unified notification hub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then this tool was built for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The project is fully open source under the MIT license. Stars, forks, and issues are all welcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub: &lt;a href="https://github.com/KaminDeng/agent_notifier" rel="noopener noreferrer"&gt;https://github.com/KaminDeng/agent_notifier&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you find this tool useful, a star on the repo is the best way to show your support. Feel free to open an issue if you have questions or feedback.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
