The Practice of Integrating Pi Agent into HagiCode





The Practice of Integrating Pi Agent into HagiCode
This article shares our practical experience in integrating Pi agent as a first-class workflow entry point in the HagiCode project, including architecture design, core component implementation, and frontend-backend adaptation details.
Background
Actually, everything started from that question: in the HagiCode Mono project, although repos/Hagicode.Libs has already implemented a reusable PiProvider, repos/hagicode-core and repos/web haven’t yet elevated Pi to a project-level first-class Agent CLI. This is like having a good pair of shoes but not having tied the shoelaces - you can walk, but it feels like something is missing.
The existing PiProvider is located at HagiCode.Libs/src/HagiCode.Libs.Providers/Pi/PiProvider.cs. It implements a CLI communication protocol based on line-delimited JSON (--mode json --print), supporting session management, tool calling, and streaming output. This code is well-written, it’s just lying there, not yet awakened.
This situation created a gap: Pi’s underlying capabilities are complete, but the project-level integration chain (from user configuration to execution monitoring) is missing. This is like painting a good picture but not hanging it on the wall for people to appreciate. So we need to integrate Pi as a new active provider PiCli into the entire system, making it a first-class workflow entry point like other Agent CLIs.
After all, we want a complete experience, not fragmented pieces.
About HagiCode
The solution shared in this article comes from our practical experience in the HagiCode project. HagiCode is an intelligent code assistant project. During development, we need to integrate multiple AI capability providers. The Pi agent integration solution described in this article is a reusable integration pattern we summarized from practice, which has reference value for similar multi-provider integration scenarios. The project’s GitHub address is github.com/HagiCode-org/site.
Architecture Design
Pi agent integration adopts the thin adapter pattern, instead of re-implementing the Pi process protocol in the core layer. This design decision isn’t actually complicated - just thought, why reinvent the wheel?
After all:
- Avoid duplicate implementation: The parameter building, process startup, JSON parsing, and error handling logic in
PiProviderare already complete. Re-implementing would create two sources of behavior and two test matrices. That’s one thing, but maintenance would be troublesome. - Maintain consistency: Keep consistent with the integration methods of existing CLIs like Kimi, Gemini, Reasonix, and DeepAgents, all delegating to the libs layer implementation through thin adapter. Everyone does this, just follow along.
- Separation of concerns:
hagicode-corefocuses on runtime contracts and business logic, while process details are left toHagiCode.Libsto handle. Each does its job, isn’t that nice?
This design allows HagiCode to quickly integrate new AI capability providers while keeping the core layer concise. After all, simplicity is beauty, efficiency is money.
Core Component Implementation
Provider Enum and Factory
Added PiCli = 13 enum value in hagicode-core/src/PCode.Models/AIProviderType.cs:
public enum AIProviderType{ ClaudeCodeCli = 0, // ... other providers PiCli = 13,}This enum value is the root of provider identity, affecting the OpenAPI-generated PCode_Models_AIProviderType.ts frontend enum, the creation branch of AIProviderFactory, and Provider parsing and active provider determination logic. Register the new creation branch in AIProviderFactory:
case AIProviderType.PiCli: provider = new PiCliProvider( logger, configuration, providerFactory.GetRequiredService<ICliProvider<PiOptions>>(), providerFactory.GetService<IAgentCliRuntimeEnvironmentResolver>()); break;Actually, these code snippets aren’t anything special, just an enum and a switch case. But they’re important, like notes on sheet music, added one by one to play a complete piece.
Thin Adapter Implementation
PiCliProvider.cs is the core thin adapter, implementing the IAIProvider, IVersionedAIProvider, and IAsyncDisposable interfaces. It receives ICliProvider<PiOptions> (from HagiCode.Libs) through the constructor, maps AIRequest / ProviderConfiguration to PiOptions, and then delegates execution, ping, version query, and other operations to the underlying provider.
The key is to handle Pi-specific JSON event streams, including assistant.thought, assistant, terminal.completed, and other events. These events need to be correctly parsed and converted to the system’s standard format during streaming output. This is a bit like translation - translating one language to another, the meaning must be conveyed accurately.
Primary Profession Preset
Added profession-pi primary profession entry in main-professions.yaml:
- Id: "profession-pi" Name: "Pi" Family: "pi" Summary: "hero.professionCopy.primary.pi.summary" Icon: "executor-avatar:Pi" SourceLabel: "hero.professionCopy.sources.aiProvidersPiCli" ProviderType: "PiCli" SortOrder: 59 DefaultEnabled: true DefaultParameters: binary: "pi" provider: "omniroute" thinking: "balanced"This ensures that the backend snapshot and frontend fallback directory have consistent Pi identity, Pi configuration can be managed through the existing Hero editor, and Pi’s addition won’t break the consumability of existing primary profession entries. After all, we don’t want to mess up existing things just because we added a new feature, that would be penny wise and pound foolish.
Monitoring Registry
AgentCliMonitoringRegistry needs to add Pi’s monitoring descriptor, enabling the system to parse executable paths, display brand names, perform health probes, and show Pi status in the status bar and health details:
new AgentCliMonitoringDescriptor( CliId: "pi", DisplayName: "Pi", ProviderType: AIProviderType.PiCli, DisplayOrder: 13, Strategy: AgentCliMonitoringStrategy.Grain, NotConfiguredMessage: "Pi CLI is not configured or executable not found.", EnabledPaths: [], ExecutablePathConfigPaths: ["Hero:PrimaryProfessions:Pi:ExecutablePath"], DefaultExecutablePath: "pi")The monitoring system is like a dashboard, telling you how the car is running. Pi’s status is clear at a glance, so users can know if everything is normal.
Frontend Adaptation
Executor Type Adaptation
The frontend needs to update executorTypeAdapter.ts, adding Pi’s type recognition logic:
const PI_IDS = new Set<string>([ PCode_Models_AIProviderType.PI_CLI, 'Pi', 'PiCli', 'pi', 'picli', 'pi-cli',]);
export function isPi(value: string): boolean { const normalized = normalize(value); const normalizedLower = normalized.toLowerCase(); return PI_IDS.has(normalized) || normalizedLower.includes('pi-cli') || normalizedLower.includes('picli') || normalizedLower === 'pi';}This is like giving Pi several names. No matter how you call it, it knows you’re calling it. After all, what name you use isn’t important, what’s important is knowing who you are.
Fallback Hero Directory
Add Pi’s fallback entry in hero.ts, ensuring that even if backend data isn’t loaded, the frontend can still display Pi configuration normally:
{ id: "profession-pi", name: "Pi", family: "pi", summary: "hero.professionCopy.primary.pi.summary", icon: "executor-avatar:Pi", sourceLabel: "hero.professionCopy.sources.aiProvidersPiCli", providerType: AIProviderType.PI_CLI, sortOrder: 59, isReadOnly: true, managedParameterKeys: { // Parameter keys supported in the first version }, defaultParameters: { binary: "pi", provider: "omniroute", thinking: "balanced", },}Fallback is like a backup plan. If the backend goes down or data doesn’t load, the frontend can still work normally. After all, no one wants to be flustered when that happens.
Localization Copy and Forms
Add Pi-related translations in locales/*/common/{hero,settings}.yml, and add a configuration field section for Pi in HeroCliEquipmentForm.tsx, supporting binary, provider, thinking, sessionDirectory, and tool/session switch fields.
The first version of Pi only exposes minimally necessary fields. Complex features like tool allowlist/denylist and environment variable editors are explicitly postponed to subsequent changes. After all, you can’t get fat in one bite, it’s faster to take it slow.
Configuration Examples
Backend Configuration
Configure Pi parameters in appsettings.yml:
Hero: PrimaryProfessions: Pi: ExecutablePath: /usr/local/bin/pi Provider: omniroute Thinking: balanced SessionDirectory: ~/.pi/sessions NoSession: false DisableAllTools: false DisableBuiltinTools: falseFrontend Configuration
Configure Pi profession in the Hero editor:
{ id: "my-pi-profession", name: "My Pi Profession", family: "pi", providerType: AIProviderType.PI_CLI, primaryModel: { provider: "PiCli", model: "glm-4.7", providerSettings: { provider: "omniroute", thinking: "balanced", sessionDirectory: "/Users/username/.pi/sessions", noSession: false, disableAllTools: false, disableBuiltinTools: false, }, },}Configuration files are like recipes. Follow them and you can make good dishes. But sometimes, even following the recipe, you might burn the dish… However, this configuration is quite clear, so there shouldn’t be any problems.
Validation and Testing
Backend Validation
Validate primary profession presets in tests:
var snapshot = await presetProvider.GetSnapshotAsync();var piProfession = snapshot.FindById("profession-pi");piProfession.ShouldNotBeNull();piProfession.ProviderType.ShouldBe(AIProviderType.PiCli);piProfession.Family.ShouldBe("pi");Also need to validate Pi availability and health checks:
# Check Pi executablewhich pipi --version
# Verify backend provider registrationcurl http://localhost:35168/api/health/agent-cli/piTests are like exams. Only after passing can you say you really know it. But sometimes, passing an exam doesn’t necessarily mean you really understand it, but at least it shows you can answer questions correctly.
Frontend Validation
// Validate type resolution and display namesexpect(resolveExecutorVisualType('pi-cli')).toBe('Pi');expect(resolveExecutorVisualType(PCode_Models_AIProviderType.PI_CLI)).toBe('Pi');expect(resolveExecutorDisplayName('PiCli')).toBe('Pi');
// Validate fallback directoryconst piFallback = findFallbackProfessionById('profession-pi');expect(piFallback?.providerType).toBe(AIProviderType.PI_CLI);These test cases cover the main logic paths, ensuring Pi can be correctly recognized and displayed. After all, things shown to users can’t go wrong.
Common Issue Troubleshooting
Pi Executable Not Found
If the health check returns “Pi executable was not found.”, you need to check if pi is in PATH, or confirm the configured path is correct. The solution is to ensure pi is installed and in PATH, or configure the correct ExecutablePath in appsettings.yml.
This is like not being able to find your house key - you have to wonder if you put it in the wrong place. Actually, the solution is quite simple - either put the key back in its original place, or change the lock.
Configuration Fields Not Recognized
If an error “PiCli runtime settings […] are not supported” is thrown at startup, check if you’re only using configuration fields supported in the first version. Fields supported in the first version include: provider, thinking, sessionDirectory, noSession, disableAllTools, disableBuiltinTools.
Sometimes it’s just greed - wanting too many features, but the system doesn’t support them. Actually, the features in the first version are already sufficient. Greedy people bite off more than they can chew.
Unable to Select Pi in Frontend
If there’s no Pi option in the Hero editor, check if you’ve run npm run generate:api to regenerate frontend enums, if there’s a profession-pi entry in hero.ts, and if localization copy has been correctly added.
Troubleshooting is like looking for lost items - you have to go step by step. After all, blind searching won’t find it, you need to search logically.
Best Practices
-
Use thin adapter pattern: Don’t re-implement process protocols in the core layer, delegate to the libs layer provider. This avoids duplicate implementation and maintains code consistency. After all, reinventing the wheel is not only tiring but also prone to problems.
-
Maintain naming consistency: Use unified naming conventions across frontend and backend to avoid confusion. Provider enum uses
PiCli, CLI ID uses"pi", display name uses"Pi". Good naming means lower communication costs. -
Prioritize using presets: The first version should be based on the
profession-pipreset, rather than requiring manual user configuration. This allows users to get started quickly and reduces configuration complexity. Users like simple things; let them come to me for complex ones. -
Focus on error messages: Ensure error messages are clear and actionable, helping users quickly locate problems. Clear error messages mean users won’t panic over an error.
-
Version compatibility: Considering the serialization stability of
AIProviderTypeenum values, changes need to be handled cautiously. The enum value ofAIProviderType.PiCli = 13cannot be easily modified. After all, changing this value might break backward compatibility, which would be troublesome.
Summary
Through the thin adapter pattern, we successfully integrated Pi agent into the HagiCode system, making it a first-class workflow entry point like other Agent CLIs. The core advantages of this solution are:
- Avoided duplicate implementation, reused the existing
PiProviderfrom the libs layer - Maintained consistent integration methods with existing providers, reducing maintenance costs
- Implemented the complete chain from user configuration to execution monitoring
HagiCode’s practice proves that the thin adapter pattern is an effective solution for integrating AI capability providers. It allows us to quickly support new agents while maintaining system stability and maintainability.
Actually, doing technology is like this - find a good pattern, then reuse it. This way, you can move forward quickly without getting lost. It’s like walking - when you find a good path, just keep walking forward, just occasionally stop to look at the scenery…
If you’re also doing multi-provider AI capability integration, I hope this solution gives you some inspiration. If you’re interested in the HagiCode project, welcome to communicate on GitHub. After all, technology needs communication to make progress.
References
- HagiCode GitHub Repository
- HagiCode Official Website
- HagiCode Installation Guide
- Pi Agent Documentation
Summary
Regarding “The Practice of Integrating Pi Agent into HagiCode”, a more prudent approach is to first get the key configuration, dependency boundaries, and implementation path working step by step, then fill in optimization details.
When goals, steps, and acceptance criteria are clear, such solutions can usually enter actual delivery more smoothly.
开始使用 HagiCode
一次安装,几分钟上手
HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。
Ecosystem Sites
Quick Links
Community
© 2026 HagiCode | Powered By hagilight-starlight@0.6.1