[AI-886] Add Pi (badlogic/pi-mono) CLI vendor support#162
Conversation
Pi has no shell hooks, so live capture ships as a Pi TypeScript extension (kcap.ts) that invokes 'kcap hook --pi' on session_start/shutdown; batch import via 'kcap import --pi'. New: PiPaths + PiExtensionInstaller (embeds kcap.ts) in Core, PiImportSource + PiHookCommand in the CLI. Wired --pi into VendorSelection, the import source list, the hook dispatch, plugin install/remove, and 'kcap setup' auto-detection (--skip-pi-hooks). Unit tests: PiImportSourceTests, PiExtensionInstallerTests. README updated. Builds + AOT-publishes clean (0 IL warnings); 20 Pi unit tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
…allow pi in parent-exit fallback PR #162 review fixes. (1) PiHookCommand derived the session id from the header or the whole filename stem, but Pi hands the session file over before the header line is flushed and names files <timestamp>_<uuid>.jsonl — so a not-yet-flushed session_start derived a non-uuid id and dropped the session + watcher. ExtractSessionId now prefers the header uuid and falls back to the filename's uuid suffix. (2) WatchCommand.PostSessionEndOnParentExitAsync gated on {claude,codex,copilot}; added 'pi' so a crashed/closed Pi watcher posts /hooks/session-end/pi on parent exit instead of leaving the session active. Tests: PiHookCommandTests (ExtractSessionId) + a Pi case in WatcherParentExitPostTests. Builds + AOT-clean (0 IL); 24 unit + 5 parent-exit tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
New re-review note:
|
…tled PR #162 re-review: WatchCommand's title helpers read the Claude JSONL shape (top-level type:user/assistant), so live Pi sessions — which emit type:"message" with message.role — fell through and never got the initial or LLM title (historical import already opts into titles). Added Pi branches to TryExtractUserText / TryExtractAssistantText / IsEvent mirroring the server PiTranscriptNormalizer user/assistant mapping (string or text-block content). Adds PiTitleHelperTests covering user/assistant extraction + the IsEvent message-role threshold. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Addressed in 4186407 — added Pi-aware branches to |
… role PR #162 re-review: the Pi IsEvent branch counted any type:"message" with role user/assistant toward the title-event threshold, but empty envelopes produce no canonical event (server NormalizeUser returns null for empty content, NormalizeAssistant for zero parts; IsImportRelevantLine requires HasContent). A live Pi session could thus hit the 5-event threshold on contentless lines. Gated on content via PiUserHasContent / PiAssistantHasContent (mirroring the normalizer: user = non-empty string or a text block; assistant = a text/thinking/toolCall part). Added empty-user/assistant false-cases + a tool-only-assistant true-case to PiTitleHelperTests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…her IsEvent PR #162 re-review: PiImportSource.IsImportRelevantLine's user/assistant check (HasContent = any non-empty content array) was broader than the server PiTranscriptNormalizer — which only emits for thinking/text/toolCall blocks (assistant) or text (user). An assistant array of only unsupported blocks (e.g. images) was treated as import-relevant, so a complete session re-classified as Partial forever. Extracted the predicate to Capacitor.Cli.Core.Pi.PiContent (HasUserContent / HasAssistantContent) mirroring the normalizer, and used it from BOTH IsImportRelevantLine and WatchCommand.IsEvent (replacing the duplicated WatchCommand helpers) so the two can't drift from the normalizer or each other. Added assistant-unsupported-block / empty-user import-relevance cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…espace extension lookup) PR #162 review: PiContent (Capacitor.Cli.Core.Pi) calls the Str/Arr JsonElement extension members declared in the parent namespace Capacitor.Cli.Core. It already builds via enclosing-namespace extension-method resolution (the 45 Pi unit tests + AOT publish exercise it through PiImportSource), but added an explicit `using Capacitor.Cli.Core;` so it no longer relies on that and stays correct if the file ever moves. Clean -t:Rebuild of the CLI is green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…guards) PR #162 review: VendorSelection added --pi to KnownVendorFlags but the unknown-vendor-prefixed guards only matched --cursor-/--claude-/--codex-/--copilot-, so a --pi-<x> typo or future option wasn't rejected — and if it was >2 edit distance from --pi it fell through silently, leaving Vendors empty (= import everything). Added --pi- to both guard expressions (the rejection loop and the typo-detection skip). Tests: --pi selects only pi; --pi-session is rejected as an unknown source option. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
New full-pass finding:
|
…trings
Address review on kcap-cli#162:
- PiImportSource discovery now reuses PiHookCommand.ExtractSessionId to
validate the session id the same way the live hook path does. A
corrupt/non-GUID `{"type":"session"}` header no longer mints an
arbitrary non-GUID session id; it also gains the live path's filename
fallback ("<timestamp>_<uuid>.jsonl") for an unflushed header. Two new
discovery tests cover both (skip non-GUID header, recover from filename).
- `kcap watch` usage: --vendor list now includes `pi` (matches
WatchCommand.KnownVendors).
- `kcap plugin` PrintUsage: now lists all current targets
(--codex|--cursor|--copilot|--pi|--skills); previously omitted both
--copilot and --pi.
- CLI README: plugin quick-reference and import-filter sentence now
mention --pi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
New broader-pass finding: the embedded CLI help resources were not updated with the new Pi surface, even though some direct usage strings and the root README were. Concrete stale spots at
These back |
|
New routed-import finding: Pi classifications set Net effect: historical |
|
New setup UX finding: the end-of-setup restart reminder is still Claude-specific after adding Pi to the setup flow.
|
|
Additional docs/help drift from the exhaustive sweep, separate from the command-specific help resources already noted: The root README still has several old “four agents” / Claude-Codex-only sections after the targeted edits: the npm install-script warning and manual refresh command omit Please do one docs sweep across the root README, npm package README, and |
|
|
|
Coverage gap worth closing before this lands: the new Pi support has helper-level tests ( |
…t, generic setup tip, update help Address the broader-pass review on kcap-cli#162: - Lifecycle callers: `kcap uninstall` now removes the Pi extension (`plugin remove --pi`) and refresh.js refreshes it (`plugin install --pi --if-installed`), so uninstall no longer leaves ~/.pi/agent/extensions/kcap.ts auto-loading and npm postinstall / `kcap update` refresh an opted-in Pi extension. InstallPi already honours --if-installed (no-op unless previously installed), so the refresh never force-installs Pi onto non-Pi users. - PiImportSource.SupportsTitleGeneration => false. Pi is a routed source (FilePath=""), so it never reaches the chain title worker, and the import title path is Claude-shaped anyway. Imported Pi sessions get the server-side fallback title (PiHookHandlers), matching Copilot/Cursor; live Pi still gets an LLM title via the watcher. Advertising true was a no-op contract lie. - Setup restart reminder is now built from the installed agents instead of always naming Claude: Pi-only -> "restart pi so the kcap extension loads"; Claude keeps --continue; others get a generic restart. - Embedded help resources updated for the Pi surface: help-import, help-plugin, help-setup (--skip-pi-hooks), help-hook (arg-driven Pi dispatch), help-usage. Tests: PluginCommandPiTests (install --if-installed no-op / refresh / remove), uninstall removes Pi extension, Pi-only restart tip, import does not support title generation. 1573/1573 unit pass; AOT-clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
All four broader-pass findings addressed in Lifecycle callers (uninstall + refresh). Routed-import title contract. Set Setup restart reminder. Embedded help resources. Updated No JS test harness exists in the repo (no 1573/1573 unit tests pass; AOT publish clean (0 IL warnings). |
…ration tests
Address the status + coverage-gap review on kcap-cli#162:
- `kcap status` now reports per-agent integration state for all five
agents (Claude, Codex, Cursor, Copilot, Pi) instead of only
Claude+Codex, so a Pi extension install is verifiable from status. The
Hooks-line formatting is extracted into the pure, testable
StatusCommand.BuildHooksStatusLine; help-status.txt updated.
- Close the user-facing Pi coverage gap (the helpers had tests, the
command/routed-import branches did not):
* CodingAgentsStepTests: Pi detected/accepted, declined, not-detected,
skipped-by-flag, --no-prompt auto-install, kcap-not-on-PATH abort,
installer-failure warning (mirrors the Copilot/Cursor coverage).
* PiImportSourceImportTests (integration, WireMock): full
discover -> classify -> import asserting the wire contract —
POST /hooks/session-start/pi, the transcript batch tagged vendor=pi,
POST /hooks/session-end/pi (reason pi-import), plus the
Partial/resume-from-watermark path.
(The `plugin install/remove --pi --if-installed` branches were covered in
the prior commit's PluginCommandPiTests.)
1582/1582 unit + 2 new integration tests pass; AOT-clean (0 IL).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Both addressed in
Command + routed-import coverage gap closed. Added the user-facing branch tests that were missing:
1582/1582 unit + 2 new integration tests pass; AOT publish clean. |
The server PiTranscriptNormalizer now maps `compaction` → ContextCompacted (AI-892), so a compaction line advances the canonical $lineNumber watermark. Keep PiImportSource.IsImportRelevantLine in sync: `compaction` now returns true (was in the skip set). Flips the matching is_import_relevant_line_matches_normalizer case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Remaining docs/help drift on the current head after the Pi follow-ups:
This is user-visible packaging/help text, so it is worth one final docs sweep before merging; otherwise the shipped CLI/npm docs understate the new Pi support and some already-supported Cursor/Copilot paths. |
…oding-vendor # Conflicts: # src/Capacitor.Cli/Commands/PluginCommand.cs
Address the docs-drift review on kcap-cli#162:
- README.md: postinstall refresh list, manual `--if-installed` example,
setup step 4 detection, both import backfill source lists ("all four" →
"all five" + ~/.pi root), uninstall coverage (+ Copilot, + Pi extension),
and the `plugin remove` flag list now all include Pi (and the
already-supported Cursor/Copilot where they were missing).
- npm/kcap/README.md: no longer Claude-only — describes multi-agent
recording (Claude/Codex/Cursor/Copilot/Pi) and setup's per-agent
integration step.
(help-usage.txt's Plugin section was already broadened to
--cursor|--copilot|--pi|--skills in the origin/main merge, 38c6046.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Done in
Verified no residual |
…oding-vendor # Conflicts: # README.md # src/Capacitor.Cli.Core/Resources/help-hook.txt # src/Capacitor.Cli.Core/Resources/help-import.txt # src/Capacitor.Cli.Core/Resources/help-plugin.txt # src/Capacitor.Cli.Core/Resources/help-setup.txt # src/Capacitor.Cli.Core/Resources/help-usage.txt # src/Capacitor.Cli/Commands/CodingAgentsStep.cs # src/Capacitor.Cli/Commands/PluginCommand.cs # src/Capacitor.Cli/Commands/PluginEnvironment.cs # src/Capacitor.Cli/Commands/SetupCommand.cs # src/Capacitor.Cli/Commands/UninstallCommand.cs # src/Capacitor.Cli/Commands/VendorSelection.cs # src/Capacitor.Cli/Commands/WatchCommand.cs # src/Capacitor.Cli/Program.cs # test/Capacitor.Cli.Tests.Unit/CodingAgentsStepTests.cs
Re-merge after #168 (AI-897 Copilot session.shutdown tail) landed on main. Only conflict was help-usage.txt's plugin flag list — kept the superset (--gemini AND --pi). Program.cs auto-merged (Pi + Gemini hook routing intact).
|
Fresh re-review findings on current head (
|
|
New functional finding from the latest re-review:
Net effect: live Gemini sessions still stream transcript lines, but they never capture |
Linear: AI-886
Adds Pi (badlogic/pi-mono, the
piCLI) as a CLI ingest vendor. Pi has no shell hooks, so live capture ships as a Pi TypeScript extension rather than a hooks file.Changes
PiPaths(the~/.pi/agentlayout) andPiExtensionInstaller, which embedskcap.tsand writes it to~/.pi/agent/extensions/(version-markered, like the Copilot installer).kcap hook --pi(PiHookCommand) reads the session JSONL header for the id, POSTs/hooks/session-{start,end}/pi(repo/PR-enriched), and spawns the existing detached watcher withvendor=pi. The shippedkcap.tsextension calls it frompi.on("session_start"/"session_shutdown").kcap import --pi(PiImportSource) walks~/.pi/agent/sessions/**/*.jsonl, sharing the import classify/watermark machinery.--piintoVendorSelection, the import source list, the hook dispatcher,kcap plugin install/remove --pi, andkcap setupauto-detection (--skip-pi-hooks).PiImportSourceTests+PiExtensionInstallerTests(20 pass). README updated.Builds + AOT-publishes clean (0 IL warnings).
Server-side ingest is in the kurrent-io/kcap-server PR on the same branch.
🤖 Generated with Claude Code