Skip to content

Select language

Current language: English

The Practice of Integrating Pi Agent into HagiCode

Edit page
HagiCode for Windows Microsoft Store artwork
HagiCode for Windows is now on Microsoft Store
HagiCode for Windows is officially live on Microsoft Store. Windows users can install it directly from the storefront and stay on the store-managed update path. Open the listing and take a look.
Open Microsoft Store
Awesome Site banner: community-curated open-source collections
Awesome Site: Curated Open-Source Collections
Find your next tool, idea, or inspiration in community-curated open-source collections. Pick a topic, read in your language, and follow the link back to the original source.
Explore Awesome Site
Awesome Design banner: a gallery of DESIGN.md design systems
Awesome Design: A DESIGN.md Gallery
Browse curated design systems through live previews and README references, then copy a DESIGN.md into your project and let your AI agent build a page that matches.
Browse the Design Gallery
OpenSpec banner: agree on the spec, then build with AI
OpenSpec: Agree on the Plan Before Your AI Writes Code
OpenSpec is a lightweight spec layer for AI coding. Describe a change, review the proposal, specs, design, and tasks your AI drafts, then build it and archive it into living specs. No rigid phases, and it fits existing codebases and the AI assistants you already use.
Explore OpenSpec
OmniRoute Docs banner: one endpoint for many AI providers
OmniRoute Docs: One Endpoint for Many AI Providers
Learn how OmniRoute routes many AI providers and models through a single endpoint, with free-tier support and automatic fallback so your coding tools keep working.
Read the OmniRoute Docs

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:

  1. Avoid duplicate implementation: The parameter building, process startup, JSON parsing, and error handling logic in PiProvider are already complete. Re-implementing would create two sources of behavior and two test matrices. That’s one thing, but maintenance would be troublesome.
  2. 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.
  3. Separation of concerns: hagicode-core focuses on runtime contracts and business logic, while process details are left to HagiCode.Libs to 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: false

Frontend 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 executable
which pi
pi --version
# Verify backend provider registration
curl http://localhost:35168/api/health/agent-cli/pi

Tests 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 names
expect(resolveExecutorVisualType('pi-cli')).toBe('Pi');
expect(resolveExecutorVisualType(PCode_Models_AIProviderType.PI_CLI)).toBe('Pi');
expect(resolveExecutorDisplayName('PiCli')).toBe('Pi');
// Validate fallback directory
const 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

  1. 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.

  2. 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.

  3. Prioritize using presets: The first version should be based on the profession-pi preset, 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.

  4. 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.

  5. Version compatibility: Considering the serialization stability of AIProviderType enum values, changes need to be handled cautiously. The enum value of AIProviderType.PiCli = 13 cannot 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 PiProvider from 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

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 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。