Skip to content

feat: thin Pi adapter mounting ECC's canonical skills and commands - #2759

Merged
haelyra merged 6 commits into
affaan-m:mainfrom
Renan-Olovics:feat/pi-thin-adapter
Aug 12, 2026
Merged

feat: thin Pi adapter mounting ECC's canonical skills and commands#2759
haelyra merged 6 commits into
affaan-m:mainfrom
Renan-Olovics:feat/pi-thin-adapter

Conversation

@Renan-Olovics

@Renan-Olovics Renan-Olovics commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What Changed

Adds first-class Pi (@earendil-works/pi-coding-agent) support to ECC as a thin adapter, following the maintainer review on #2352.

8 files, +1,289 lines, 0 deletions. For comparison, #2352 is 440 files, +99,628 / −2,785, which exceeded both Greptile's and CodeRabbit's review limits.

Review point from #2352 How this PR addresses it
Expose ECC's canonical skills directly instead of committing generated copies The pi manifest points Pi at ./skills and ./commands. Nothing is copied or generated under .pi/. Two regression tests fail the build if .pi/skills, .pi/agents, .pi/prompts, .pi/chains reappear, or if more than 10 files are tracked under .pi/
Use Pi's documented extension lifecycle export default function (pi: ExtensionAPI) with pi.on("session_start" | "session_shutdown" | "before_agent_start"). No app.events
Resolve hooks from the installed package, execute without a shell path.resolve(__dirname, "..", "..") for resolution; execFile(process.execPath, [...]) for execution. process.cwd() appears nowhere in the file
Separate Pi-native resources from optional subagent resources Manifest declares only extensions, skills, prompts. No agents / chains keys — those belong to pi-subagents and would be silently ignored by Pi's core manifest. A test asserts they stay absent
Start with a small, verifiable feature set Package loading, canonical skill/command discovery, /ecc-doctor, and two lifecycle mappings. Profiles, approval UI, todos, agent conversion, and chains are explicitly out of scope
Add compatibility tests and exact tested versions 20 new tests across two files; tested against the exact versions listed below

No transformation needed

ECC's assets are already Pi-compatible, which is what makes the copy-free approach work:

  • skills/*/SKILL.md already follows the Agent Skills standard that Pi implements
  • commands/*.md frontmatter (description, argument-hint) is already Pi's prompt-template format

Rules are the exception — Pi has no rules concept — so the adapter reads them from the canonical rules/common/ directory at runtime and appends them to the system prompt inside an <ecc-engineering-rules> block, reusing the injection path already built for session context. Still no copies: nothing is generated under .pi/.

The adapter

.pi/extensions/index.ts is the only logic added. Beyond the review points above it:

  • invokes hooks through ECC's own scripts/hooks/run-with-flags.js, so ECC_HOOK_PROFILE and ECC_DISABLED_HOOKS keep gating hooks under Pi rather than being bypassed
  • runs hooks in the user's project directory while resolving the scripts package-relative, so ECC's project detection reports the user's project, not the ECC install
  • honors ECC's stdin/stdout hook contract: sends the event payload as JSON on stdin and parses hookSpecificOutput.additionalContext from stdout, injecting it into the system prompt on the next before_agent_start inside an <ecc-session-context> block. Non-JSON output (a profile-disabled hook passing stdin through) is tolerated, not treated as an error
  • isolates hook failures behind a 30s timeout and a 1 MB output cap; a missing, broken, or slow hook degrades to a warning and never terminates the session

Also registers .pi in the platform-configs install module and adds a Pi row to the harness adapter compliance matrix.

Testing Done

Tested against Pi 0.84.1, Node 24.18.1, macOS 15 (Darwin 25.5.0). No companion packages installed — the integration was exercised without pi-subagents, @juicesharp/rpiv-ask-user-question, or @juicesharp/rpiv-todo present.

Global install, isolated config directory

Installed into a clean PI_CODING_AGENT_DIR so the results could not be contaminated by pre-existing user settings, then inspected the loaded resources through pi.getCommands(), filtering to entries whose sourceInfo.path is inside the ECC package:

PKG_SKILLS=285
PKG_PROMPTS=94
PKG_EXTENSION_CMDS=1
PKG_SKILL_EG=skill:accessibility  <- /path/to/ECC/skills/accessibility/SKILL.md
PKG_PROMPT_EG=aside               <- /path/to/ECC/commands/aside.md

Repo ground truth: ls skills | wc -l = 285, ls commands/*.md | wc -l = 94. Exact match, and the resolved paths are the canonical directories — not .pi/. /ecc-doctor resolved to .pi/extensions/index.ts from the package.

Lifecycle and context injection, from a path containing a space

Ran Pi from a working directory whose path contains a space, with a probe extension asserting on the assembled system prompt:

PROBE_INJECTED=YES
PROBE_SNIPPET="<ecc-session-context>\nProject type: {\"languages\":[\"javascript\"],...,
               \"projectDir\":\".../proj with space\"}\n</ecc-session-context>"

projectDir is the user's project, confirming hooks execute there while resolving package-relative. An earlier iteration passed cwd as the ECC root and reported ECC's own directory as the project; that was caught by this test and fixed.

Automated

node tests/pi/pi-package-manifest.test.js     Passed: 9,  Failed: 0
node tests/pi/pi-extension-adapter.test.js    Passed: 24, Failed: 0
npm run harness:adapters -- --check           PASS (12 adapters)
node tests/docs/harness-adapter-compliance.test.js   Passed: 10, Failed: 0
node tests/scripts/npm-publish-surface.test.js       Passed: 2,  Failed: 0
npx markdownlint-cli .pi/README.md            clean

All 8 CI validators pass, including validate-install-manifests.js for the install-modules.json change.

Test coverage includes: package manifest shape, the .pi/ copy regression guards, canonical-asset Pi compatibility, __dirname vs process.cwd() resolution, absence of shell invocation, documented-lifecycle usage, a global-install simulation from a package root whose path contains a space, hook resolution when cwd points elsewhere, profile/disable gating, and additionalContext parsing including malformed and non-JSON input.

Full suite: node tests/run-all.js is green on this branch — 3,757 passed, 0 failed. npm run lint (eslint + markdownlint) passes clean.

One transient failure appeared mid-work and is fixed in this PR rather than worked around: adding .pi/ to package.json files broke tests/scripts/npm-publish-surface.test.js, which validates the published surface against the install module graph. The fix was to register .pi in the platform-configs module in manifests/install-modules.json, which is where a shipped harness directory belongs.

Type of Change

  • feat: New feature
  • docs: Documentation
  • test: Tests
  • chore: Maintenance/tooling

Security & Quality Checklist

  • No secrets or API keys committed
  • JSON files validate cleanly
  • No sensitive data exposed in logs or output
  • Follows conventional commits format

Security notes specific to this adapter:

  • Hooks execute via execFile with an argv array — no shell, so paths with spaces or shell metacharacters cannot be reinterpreted as commands.
  • Hook scripts resolve from the installed package, so a hostile working directory cannot substitute its own scripts/hooks/*.js.
  • Hook failures are isolated and cannot silently authorize a blocked operation.
  • /ecc-doctor prints paths, counts, and profile names only — no credentials or environment secrets.
  • The adapter never auto-commits, pushes, merges, or deploys.

Known Limitations

  • Full subagent orchestration, chains, structured approval gates, and persistent todos are out of scope and require companion packages. ECC is usable in Pi without any of them.
  • Pi has no MCP surface by design, so ECC's MCP reference configs do not apply.
  • session_shutdown maps to ECC's session:end:marker hook rather than session-end.js: the latter expects a Claude Code transcript, and Pi's session JSONL is a different format. Feeding it one would fabricate a compatibility that does not exist.

What Reaches Pi

ECC asset Ships to Pi How
skills (285) all canonical skills/, via the pi manifest
commands (94) all canonical commands/, via the pi manifest
rules (rules/common/) portable subset, 7 files read at runtime, injected into the system prompt
MCP configs (35 servers) all unchanged, through the community pi-mcp-adapter — see below
hooks 2 of 22 session:start and session:end:marker, through ECC's own run-with-flags.js
agents (67) none needs the pi-subagents companion package

The last two rows are deliberate. Converting 67 agents would mean generating 67 files under .pi/, which is the pattern this PR exists to avoid, and the review asked for agent conversion and chains to follow independently. The remaining 20 hooks are Claude PreToolUse/PostToolUse/Stop events; Pi has equivalents (tool_call, tool_result, agent_settled), so they are mappable, but each carries its own semantics and belongs in a separate change.

agents.md, hooks.md, and performance.md are excluded from rule injection on purpose: they describe Claude Code primitives Pi does not have, so injecting them would point the model at tools that are not there. Tests assert both that they stay excluded and that none of those primitives appear in the injected text.

MCP needs nothing from ECC

pi-mcp-adapter reads the standard mcpServers format from .mcp.json — exactly what ECC already uses. Verified against version 2.21.2 in an isolated PI_CODING_AGENT_DIR: copying mcp-configs/mcp-servers.json to a project's .mcp.json registers Pi's mcp tool and /mcp command with all 35 ECC servers discovered, coexisting with /ecc-doctor. No translation layer, no ECC change. ECC neither installs nor depends on that package; /ecc-doctor reports which optional companions are present and prints the exact pi install command for the ones that are not.

Review Response

Automated review flagged two real runtime defects, both fixed in 2597544f:

  • EPIPE could kill the session. stdin.end() writes asynchronously, so a hook that exits, short-circuits, or is killed by the timeout before reading its payload raises EPIPE as an error event that the surrounding try/catch cannot observe. Unhandled, it would take the Pi session down — exactly the guarantee the adapter claims to provide. Now handled, with both a source contract test and a behavioral test that writes 2 MB to a child which exits without reading.
  • Stale context could replay across sessions. pendingContext lives between session_start and before_agent_start. Pi can begin a new session (/new, /resume, /fork) before that value is consumed; if the newer hook then failed, the next agent start received context describing a different session's project state. Greptile reproduced this against a real failing hook. pendingContext is now cleared at the top of the handler, before the hook runs.

A third issue was found while verifying the review: companion-package detection used require.resolve, which cannot see packages Pi installs under its own config directory (~/.pi/agent/npm, overridable via PI_CODING_AGENT_DIR) because that path is not on Node's module resolution path from the extension. /ecc-doctor would have reported every companion as missing regardless of what was installed. It now reads Pi's own packages list, correctly handling scoped names with versions.

Also addressed: the compliance matrix renderer joins list entries with "; ", so internal semicolons and trailing periods in the Pi record split one entry into several in the rendered cell; the profile-gating test ran against the real checkout and could leave marker artifacts behind, and now uses the isolated skeleton; the .pi/ file-count guard counted tracked files only, so untracked copies could bypass it, and now walks disk; the README heuristic rejected valid negated phrasing; and the local parser mirrors in the tests are now pinned by source assertions so they cannot silently diverge from the adapter.

Credit

This builds on the groundwork and problem framing in #2352 by @juicesharp. The Pi integration surface, the companion-package landscape, and the installation model were mapped there first; this PR reimplements that scope as an adapter along the lines the maintainer review requested.

#2270 by @SiaoZeng is the prior groundwork for the structured tool-result envelope discussed around ECC's harness integrations. This adapter registers no tools — it exposes one command, /ecc-doctor, and no registerTool call — so there is no tool-result surface here for that envelope to apply to. If a Pi tool port happens later it will be a separate, focused PR, and it will credit #2270 for the envelope design.

Adds first-class Pi (@earendil-works/pi-coding-agent) support as a thin
adapter layer, following the maintainer review on affaan-m#2352. ECC's canonical
assets stay the single source of truth: nothing is copied or generated
under .pi/.

The `pi` manifest in package.json points Pi directly at `skills/` and
`commands/`. No transformation is needed — ECC's SKILL.md files already
follow the Agent Skills standard Pi implements, and ECC's command
frontmatter is already Pi's prompt-template format.

.pi/extensions/index.ts is the only adapter logic. It:

- uses Pi's documented `pi.on(...)` lifecycle, not an undocumented event bus
- resolves hook scripts from the installed package via `__dirname`, never
  `process.cwd()`, so global installs work from any project directory
- runs hooks with `execFile(process.execPath, [...])` and no shell, so paths
  containing spaces or shell metacharacters are safe
- invokes hooks through ECC's own `run-with-flags.js`, so `ECC_HOOK_PROFILE`
  and `ECC_DISABLED_HOOKS` keep gating hooks under Pi
- runs hooks in the user's project directory so project detection stays
  correct, while resolving the scripts themselves package-relative
- injects the SessionStart hook's `additionalContext` into the system prompt
  on the next `before_agent_start`
- isolates hook failures behind a timeout and an output limit
- registers `/ecc-doctor` for install diagnostics

Registers `.pi` in the platform-configs install module and adds a Pi row to
the harness adapter compliance matrix.

Verified against Pi 0.84.1: a global `pi install` exposes 285 skills and 94
commands resolved from `skills/` and `commands/`, plus `/ecc-doctor`, with
no generated copies.

Scope deliberately excludes subagents, chains, approval gates, todos,
profiles, and MCP; ECC works in Pi without any companion package.
@ecc-tools

ecc-tools Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added Pi harness integration with session lifecycle hooks, context injection, diagnostics through /ecc-doctor, and isolated hook failures.
    • Added Pi package configuration, extensions, skills, prompts, and installation support.
  • Documentation
    • Added setup, troubleshooting, security, limitations, and verification guidance for Pi.
    • Documented Pi in the harness adapter compliance matrix.
  • Tests
    • Added compatibility and package-manifest regression coverage for Pi integration.

Walkthrough

Adds a Pi extension adapter for ECC resources, session hooks, context injection, diagnostics, packaging, compliance tracking, and compatibility tests.

Changes

Pi adapter integration

Layer / File(s) Summary
Package and resource integration
.pi/README.md, package.json, manifests/install-modules.json, scripts/lib/harness-adapter-compliance.js, docs/architecture/harness-adapter-compliance.md
Configures Pi extensions, skills, prompts, published files, managed paths, installation options, supported assets, limitations, and verification commands.
Hook execution and context mapping
.pi/extensions/index.ts
Runs ECC session hooks with execFile, mapped session metadata, environment variables, timeouts, output limits, failure isolation, rule loading, and context injection.
Adapter behavior and publication validation
tests/pi/pi-extension-adapter.test.js, tests/pi/pi-package-manifest.test.js
Validates lifecycle hooks, context handling, package discovery, portable rules, diagnostics, manifest entries, publication rules, canonical resources, compatible formats, and documentation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Pi
  participant PiExtension
  participant ECCHookRunner
  participant AgentSystemPrompt
  Pi->>PiExtension: start session
  PiExtension->>ECCHookRunner: run SessionStart hook
  ECCHookRunner-->>PiExtension: return hook output or warning
  PiExtension->>AgentSystemPrompt: inject additionalContext and portable rules
  Pi->>PiExtension: end session
  PiExtension->>ECCHookRunner: run SessionEnd hook
Loading

Possibly related PRs

  • affaan-m/ECC#2649: Adds shared lifecycle-hook infrastructure and configuration behavior reused by the Pi adapter.

Suggested reviewers: affaan-m

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a thin Pi adapter that mounts ECC's canonical skills and commands.
Description check ✅ Passed The description directly explains the Pi adapter, its supported features, limitations, implementation details, and test coverage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.pi/extensions/index.ts:
- Around line 190-199: Attach an error handler to child.stdin in the hook
execution flow, alongside the existing child error handler, and resolve the
operation with the same failure result when asynchronous stdin writes emit EPIPE
or another error. Keep the synchronous try/catch for immediate failures and
ensure stdin errors are handled without allowing an unhandled stream event to
terminate the session.
- Around line 289-303: Update isCompanionInstalled to resolve packageName from
Pi’s package roots, including the global ~/.pi/agent/npm and project .pi/npm
module roots, rather than relying only on the ECC extension context’s
require.resolve. Reuse Pi’s resolved package-path/module-resolution mechanism
and preserve the existing package.json-first fallback to the package entry
point, returning false only when neither Pi root resolves the companion.

In `@scripts/lib/harness-adapter-compliance.js`:
- Around line 136-149: Update the affected entries in the compliance data:
remove the trailing period from the first risk_notes string and replace the
internal semicolon in the second unsupported_surfaces string with punctuation
that does not conflict with the matrix renderer’s "; " join. Preserve the entry
meanings and all other records unchanged.

In `@tests/pi/pi-extension-adapter.test.js`:
- Around line 89-106: Update the tests around extractAdditionalContext and its
assertions to explicitly acknowledge the local implementation is a copy, then
add source-level assertions against the shipped adapter’s actual guard behavior,
including the startsWith("{") check and non-empty trimmed string validation.
Ensure the tests fail when .pi/extensions/index.ts diverges, rather than
validating only the copied function or merely checking field-name presence.
- Around line 309-340: Update the profile-gating test around disabledResult and
minimalResult to create and use an isolated buildEccSkeleton root for both
runHookRunner calls instead of repoRoot. Wrap the test setup and assertions in
try/finally, and remove the temporary skeletonRoot recursively in the finally
block.

In `@tests/pi/pi-package-manifest.test.js`:
- Around line 110-126: Harden both `.pi/` regression guards: update the
file-count test around “REGRESSION GUARD: git tracks fewer than 10 files under
.pi/” to recursively walk `.pi/` on disk using a Node 18-compatible manual
traversal, counting files regardless of Git tracking status. Also revise the
README heuristic in the nearby regression tests to recognize copy instructions
while allowing valid negated phrasing such as “no generated copies.”
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4638f045-10da-401c-bbf1-abbc31ea4610

📥 Commits

Reviewing files that changed from the base of the PR and between bed96af and 04b4aba.

📒 Files selected for processing (8)
  • .pi/README.md
  • .pi/extensions/index.ts
  • docs/architecture/harness-adapter-compliance.md
  • manifests/install-modules.json
  • package.json
  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp,properties,yml,yaml,json,env,config}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

NEVER hardcode secrets in source code - ALWAYS use environment variables or a secret manager

Files:

  • manifests/install-modules.json
  • package.json
  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{js,ts,jsx,tsx,json,env*}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not hardcode secrets, API keys, passwords, or tokens

Files:

  • manifests/install-modules.json
  • package.json
  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Delegate complex features to a planner agent, architectural decisions to an architect agent, modified code to a code-reviewer agent, and security-sensitive work to a security-reviewer agent; use parallel agents for independent operations.
Never compromise security; validate all inputs and prevent hardcoded secrets, injection, XSS, CSRF, authentication or authorization failures, sensitive error leakage, and missing rate limits.
Never hardcode secrets; use environment variables or a secret manager, validate required secrets at startup, and rotate exposed secrets immediately.
Always create new objects and never mutate existing ones.
Plan complex features before implementation, identifying dependencies, risks, and phases.
Prefer many small, focused files; keep functions under 50 lines, files under 800 lines where practical, avoid nesting deeper than four levels, and use readable, well-named identifiers.
Handle errors at every level, provide user-friendly UI messages, log detailed server-side context, and never silently swallow errors.
Validate all user input at system boundaries using schema-based validation; fail fast with clear messages and never trust external data.
Required tests include unit tests, integration tests for APIs and database operations, and end-to-end tests for critical user flows.
Follow the mandatory TDD cycle: write a failing test, implement the minimum passing solution, then refactor and verify coverage.
Use a consistent API response envelope containing a success indicator, data payload, error message, and pagination metadata.
Encapsulate data access behind a repository interface with operations such as findAll, findById, create, update, and delete; business logic must depend on the abstraction rather than storage details.

Files:

  • manifests/install-modules.json
  • package.json
  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • docs/architecture/harness-adapter-compliance.md
  • tests/pi/pi-package-manifest.test.js
{package.json,*.config.js,scripts/**/*.js}

📄 CodeRabbit inference engine (CLAUDE.md)

Package manager detection should support npm, pnpm, yarn, and bun, with configuration via CLAUDE_PACKAGE_MANAGER environment variable or project config.

Files:

  • package.json
  • scripts/lib/harness-adapter-compliance.js
**/*.{js,ts,jsx,tsx,py,java,cs,go,rb,php,scala,kt}

📄 CodeRabbit inference engine (.cursor/rules/common-coding-style.md)

**/*.{js,ts,jsx,tsx,py,java,cs,go,rb,php,scala,kt}: Always create new objects, never mutate existing ones. Use immutable patterns to prevent hidden side effects and enable safe concurrency
Organize code into many small files (200-400 lines typical, 800 lines max) organized by feature/domain rather than by type
Always handle errors explicitly at every level and never silently swallow errors
Always validate all user input before processing at system boundaries
Use schema-based validation where available
Fail fast with clear error messages when validation fails
Never trust external data (API responses, user input, file content)
Ensure code is readable and well-named
Keep functions small (less than 50 lines)
Keep files focused (less than 800 lines)
Avoid deep nesting (more than 4 levels)
Do not use hardcoded values; use constants or configuration instead

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

No hardcoded secrets (API keys, passwords, tokens) - validate before any commit

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php}: All user inputs must be validated
Enable CSRF protection on all state-changing endpoints
Verify authentication and authorization for all protected endpoints
Implement rate limiting on all endpoints to prevent abuse
Ensure error messages do not leak sensitive data in responses

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,sql}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

Use parameterized queries to prevent SQL injection

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{js,ts,jsx,tsx,html,php,java,cs,rb,go}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

Implement XSS prevention by sanitizing HTML output

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/typescript-coding-style.md)

**/*.{ts,tsx,js,jsx}: Use spread operator for immutable updates in TypeScript/JavaScript instead of direct mutation
Use async/await with try-catch for error handling in TypeScript/JavaScript
Use Zod for schema-based input validation in TypeScript/JavaScript
No console.log statements in production code; use proper logging libraries instead

**/*.{ts,tsx,js,jsx}: Auto-format JavaScript/TypeScript files using Prettier after edit
Warn about console.log statements in edited files
Check all modified files for console.log statements before session ends

**/*.{ts,tsx,js,jsx}: Use the ApiResponse interface pattern with generic type parameter: interface ApiResponse<T> { success: boolean; data?: T; error?: string; meta?: { total: number; page: number; limit: number; } }
Implement custom React hooks following the pattern: export a named function with use prefix, generic type parameters, and proper useEffect cleanup for side effects

**/*.{ts,tsx,js,jsx}: Never hardcode secrets; always use environment variables for sensitive credentials like API keys
Throw an error when required environment variables are not configured to fail fast and ensure security prerequisites are met

Use Playwright as the E2E testing framework for critical user flows in TypeScript/JavaScript

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
scripts/**/*.js

📄 CodeRabbit inference engine (CLAUDE.md)

Ensure cross-platform support for Windows, macOS, and Linux via Node.js scripts in the scripts/ directory.

Files:

  • scripts/lib/harness-adapter-compliance.js
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,jsx,tsx}: Always create new objects and never mutate in place; return new copies instead
Keep files between 200–400 lines typical, with a maximum of 800 lines
Extract helpers when a file exceeds 200 lines
Handle errors explicitly at every level; never swallow errors silently
Validate all user input before processing; use schema-based validation where available
Never trust external data (API responses, file content, query params); always validate
All user inputs must be validated and sanitized
Error messages must be scrubbed of sensitive internals
Use readable, well-named identifiers in all code
Keep functions under 50 lines
Keep files under 800 lines
Avoid nesting deeper than 4 levels
Implement comprehensive error handling in all code
Do not hardcode values; use constants or environment configuration instead
Do not use in-place mutation; always return new objects or state

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts}: Use parameterized queries for all database writes (no string interpolation)
Auth/authz must be checked server-side for every sensitive path
Rate limiting must be applied to all public endpoints

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

HTML output must be sanitized where applicable

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
**/*.{js,ts,env*}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Required environment variables must be validated at startup

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
{scripts,bin}/**

⚙️ CodeRabbit configuration file

{scripts,bin}/**: Focus on command injection, unsafe subprocess usage, path traversal, SSRF, secret exposure, and missing tests for new CLI behavior.

Files:

  • scripts/lib/harness-adapter-compliance.js
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write tests before implementation (test-driven development); target 80%+ coverage
Achieve minimum 80% test coverage across all three layers: Unit, Integration, and E2E
Use AAA structure (Arrange / Act / Assert) in tests with descriptive test names that explain behavior under test

Files:

  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
🪛 ast-grep (0.45.0)
tests/pi/pi-extension-adapter.test.js

[warning] 28-28: Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: require("child_process")
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process)


[warning] 115-115: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(extensionPath, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

tests/pi/pi-package-manifest.test.js

[warning] 14-14: Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: require("child_process")
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process)


[warning] 41-41: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)


[warning] 153-153: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(planCommandPath, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)


[warning] 179-179: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(sampleSkillPath, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)


[warning] 201-201: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(readmePath, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

.pi/extensions/index.ts

[warning] 322-322: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(path.join(ECC_ROOT, "package.json"), "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 23-23: Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (9)
docs/architecture/harness-adapter-compliance.md (1)

42-42: The rendered .; and duplicated-semicolon artifacts in this row come from the source record. I raised it on scripts/lib/harness-adapter-compliance.js lines 136-149. Regenerate this row with npm run harness:adapters after that fix.

.pi/README.md (2)

22-23: LGTM!

Also applies to: 87-88, 102-107, 143-148


53-61: 📐 Maintainability & Code Quality

Keep the Pi settings example unchanged.

Pi supports the skills and prompts keys and expands ~ in paths.

			> Likely an incorrect or invalid review comment.
package.json (1)

53-53: LGTM!

Also applies to: 475-485

manifests/install-modules.json (1)

116-116: LGTM!

scripts/lib/harness-adapter-compliance.js (1)

125-135: LGTM!

Also applies to: 150-157

.pi/extensions/index.ts (2)

103-142: LGTM!

Also applies to: 207-287, 332-368, 435-447


377-413: 🎯 Functional Correctness

No change needed. Pi awaits session_start during runtime setup before it accepts the first prompt, so pendingContext is populated before before_agent_start.

			> Likely an incorrect or invalid review comment.
tests/pi/pi-extension-adapter.test.js (1)

43-52: LGTM!

Also applies to: 59-87, 118-236, 240-307

Comment thread .pi/extensions/index.ts
Comment thread .pi/extensions/index.ts Outdated
Comment thread scripts/lib/harness-adapter-compliance.js
Comment thread tests/pi/pi-extension-adapter.test.js
Comment thread tests/pi/pi-extension-adapter.test.js Outdated
Comment thread tests/pi/pi-package-manifest.test.js Outdated
@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The Pi integration installs successfully and loads the ECC extension, canonical skills, and canonical commands. The previously reported stale-session-context behavior is resolved: when a newer session start fails, the prior context is cleared and is not added to the next agent prompt.

Confidence Score: 5/5

No blocking failure remains.

The exercised Pi installation, resource loading, and session lifecycle behavior completed without a reproduced product defect.

T-Rex T-Rex Logs

What T-Rex did

  • Ran the prrc-pending-context-lifecycle-harness.js to exercise the pending-context lifecycle sequence, including a successful session start, a failed newer start, and before_agent_start, and verified that old context is cleared before the failed start returns.
  • Installed Pi 0.84.1 in isolation, registered the repo, and started Pi via installed-package discovery, confirming the provider-auth boundary is reached without an ECC load failure and that the focused manifest suite passes all 9 checks.
  • Uploaded harness artifacts through the Greptile mechanism, capturing both the exact harness source and the captured command output as artifacts.
  • Under an after-install run, recorded the package registry, generated Pi settings, and startup outcome, and validated that the Pi CLI accepts the real .pi/extensions/index.ts along with skills and commands, without modifying repository sources.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (3): Last reviewed commit: "fix: /ecc-doctor misreported filtered pa..." | Re-trigger Greptile

Comment thread .pi/extensions/index.ts

@daltino daltino left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! The changes look neat and well-structured.

@Renan-Olovics
Renan-Olovics marked this pull request as draft August 11, 2026 00:09
Bot review on affaan-m#2759 surfaced two real runtime defects and several
hardening gaps.

Runtime fixes:

- Attach an `error` listener to the hook child's stdin. `stdin.end()`
  writes asynchronously, so a hook that exits, short-circuits, or is
  killed by the timeout before reading the payload raises EPIPE as an
  `error` event that the surrounding try/catch cannot see. Unhandled,
  that event would terminate the Pi session and break the isolation
  guarantee the adapter documents.
- Clear `pendingContext` at the top of the `session_start` handler. Pi
  can start a new session (/new, /resume, /fork) before
  `before_agent_start` consumes the previous value; if the newer hook
  then failed, the next agent start received context describing a
  different session's project state.
- Replace `require.resolve` companion detection with a read of Pi's own
  `packages` list, honoring `PI_CODING_AGENT_DIR`. Pi installs packages
  under its config directory, which is not on Node's module resolution
  path from the extension, so the previous check reported every
  companion as missing no matter what was installed.

Compliance matrix: remove internal semicolons and a trailing period from
the Pi record's list entries. The renderer joins entries with "; ", so
those characters split one entry into several in the rendered cell.

Tests: run profile gating against the temp skeleton instead of the real
checkout so it cannot leave marker artifacts behind; count files under
.pi/ by walking disk rather than git, so untracked copies cannot bypass
the regression guard; allow negated phrasing in the README heuristic;
pin the adapter's real parser guards with source assertions so the local
mirrors cannot silently diverge; add coverage for EPIPE isolation, stale
context clearing, and companion detection.
@ecc-tools

ecc-tools Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@Renan-Olovics

Copy link
Copy Markdown
Contributor Author

All review findings addressed in 2597544f. Summary of what was accepted and what changed:

Real defects (both confirmed, both fixed)

Finding Verdict
CodeRabbit — child.stdin needs an error handler; try/catch does not catch EPIPE Correct. The write is asynchronous, so a hook that exits or is killed by the timeout before reading raises EPIPE as an event the catch never sees. Unhandled it terminates the session — the exact guarantee the adapter documents. Fixed, plus a behavioral test that writes 2 MB to a child which exits without reading, asserting no uncaught exception.
Greptile P1 — failed session start replays prior session context Correct, and the reproduction was accurate. pendingContext now clears at the top of the session_start handler, before the hook runs, so a new session cannot inherit a previous one's context whether or not the newer hook succeeds.

Found while verifying the review

CodeRabbit's question about companion-package resolution led to a third real bug: require.resolve cannot see packages Pi installs under its own config directory (~/.pi/agent/npm, overridable via PI_CODING_AGENT_DIR), since that path is not on Node's module resolution path from the extension file. /ecc-doctor would have reported every companion as "not installed" regardless of reality. It now reads Pi's own packages list, with correct handling of scoped names carrying versions (npm:@scope/name@1.2.3) — the case a naive split("@") gets wrong.

Also fixed

  • Compliance matrix: the renderer joins entries with "; ", so internal semicolons and a trailing period in the Pi record split one entry into several in the rendered cell. Verified against the regenerated table.
  • Profile-gating test ran against the real checkout and could leave session-end-marker artifacts in a developer tree or CI; it now uses the same isolated skeleton as the neighbouring tests, with cleanup in finally.
  • The .pi/ file-count regression guard counted git-tracked files only, so untracked generated copies bypassed it entirely. It now walks disk (Node 18 compatible manual traversal), with the git check kept as a secondary signal.
  • The README heuristic rejected valid negated phrasing such as "Nothing is copied or generated under .pi/". It now detects imperative copy instructions and exempts negations, verified both against the real README and against a synthetic violating sentence.
  • The local parser mirrors in the tests are now pinned by source-level assertions against .pi/extensions/index.ts, so a change to the adapter's real guards fails the test instead of passing against a stale copy.

Adapter tests went from 11 to 17. Full suite: 3,750 passed, 0 failed. npm run lint clean.

Marked as draft while the remaining scope questions in the PR description are settled.

…ng them

Every capability listed as out of scope is already provided by a maintained
community Pi package: pi-subagents, @juicesharp/rpiv-ask-user-question,
@juicesharp/rpiv-todo, and pi-mcp-adapter for MCP.

Pi supports pulling other pi packages in via dependencies plus
bundledDependencies, but this adapter deliberately does not. Bundling would
ship third-party code that executes with full user permissions in every ECC
install, turn optional capabilities into mandatory ones, and add four
fast-moving pins to maintain.

Instead /ecc-doctor now prints the exact `pi install npm:<name>` command for
each companion it does not find, so adopting one stays a deliberate user
choice.

Also corrects the MCP claim: Pi core has no MCP surface by design, but the
community pi-mcp-adapter package adds one. This adapter neither installs nor
verifies it, and ECC's MCP reference configs are not known to be compatible.
@ecc-tools

ecc-tools Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

Tested rather than assumed. The community pi-mcp-adapter package reads the
standard mcpServers format from .mcp.json and ~/.config/mcp/mcp.json, which
is exactly the format ECC already uses in .mcp.json and
mcp-configs/mcp-servers.json.

Verified against pi-mcp-adapter 2.21.2 in an isolated PI_CODING_AGENT_DIR:
copying mcp-configs/mcp-servers.json to a project's .mcp.json registers Pi's
`mcp` tool and `/mcp` command with all 35 ECC servers discovered, coexisting
with this adapter's /ecc-doctor. No translation layer and no ECC change are
needed, so this stops being a limitation and becomes documentation.

Recorded caveats: the adapter's first run against a new config performs
initialization that blocks in non-interactive mode, and only discovery was
verified, not live tool invocation.

ECC still neither installs nor depends on the package.
@ecc-tools

ecc-tools Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@haelyra haelyra mentioned this pull request Aug 11, 2026
19 tasks
@haelyra

haelyra commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Maintainer routing note: #2270 is prior groundwork for the structured tool-result envelope proposed around ECC’s Pi integration. I closed that older adapter to keep one active integration path here because its runtime and policy layer had drifted substantially. As this thin adapter stabilizes, please preserve credit to #2270 for that envelope idea and keep any generic tool port as a separate, focused follow-up.

ECC's rules were the one durable asset the adapter did not deliver: skills
and commands reached Pi in full, but the 122 rule files that carry ECC's
coding style, testing, security, git workflow, and code-review standards
did not, so ECC in Pi was a library of skills rather than a set of
enforced standards.

Rules are read at runtime from the canonical rules/common/ directory of
the installed package and appended to the system prompt inside an
<ecc-engineering-rules> block. Nothing is copied or generated under .pi/,
which keeps the single-source-of-truth constraint this PR exists to
satisfy. Injection reuses the before_agent_start path already built for
session context, so no new lifecycle mapping is introduced.

Rules are re-applied every turn because they are standing policy, while
the session context stays one-shot and is consumed on first use.

agents.md, hooks.md, and performance.md are excluded: they describe Claude
Code primitives Pi does not have (Task/TodoWrite delegation, Claude hook
event types, thinking-budget toggles), so injecting them would point the
model at tools that are not there. A test asserts they stay excluded, and
a leakage test asserts none of those primitives appear in the injected
text. Language-specific rules under rules/<language>/ are out of scope for
this first adapter.

Injection is bounded by MAX_RULES_BYTES and can be disabled with
ECC_PI_RULES, following ECC's existing off-switch convention. /ecc-doctor
reports the state and injected size.

Measured on this repo: 7 files, 12,361 characters, roughly 3k tokens.

Also replaces a Function() call in the test helper with direct arithmetic,
and repins a stale assertion that pinned one spelling of the context
handoff rather than the guarantee (read before clear, clear before return).
@ecc-tools

ecc-tools Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@Renan-Olovics

Copy link
Copy Markdown
Contributor Author

Understood on all three points, and thank you for the routing note.

No tool port here. Confirming concretely, since that is the part most at risk of scope creep: this adapter registers zero tools (no pi.registerTool call) and exactly one command, /ecc-doctor. The full diff is 8 files:

.pi/extensions/index.ts                          adapter logic
.pi/README.md                                    adapter docs
package.json                                     pi manifest + files entry
manifests/install-modules.json                   register .pi in platform-configs
scripts/lib/harness-adapter-compliance.js        adapter record
docs/architecture/harness-adapter-compliance.md  regenerated matrix
tests/pi/pi-extension-adapter.test.js            24 tests
tests/pi/pi-package-manifest.test.js             9 tests

Because nothing here produces a tool result, there is no surface in this PR for the structured envelope to attach to — which is why keeping it out is natural rather than a concession.

Credit preserved. The description now records #2270 by @SiaoZeng as prior groundwork for the structured tool-result envelope, and states that if a Pi tool port lands later it will be a separate PR crediting #2270 for the envelope design.

Scope held. Following the same rule already applied elsewhere in this PR — agent conversion and chains stay out because they would mean generating 67 files under .pi/, and 20 of ECC's 22 hooks stay out because Pi's tool_call / tool_result / agent_settled equivalents each carry their own semantics. A generic tool port joins that list as its own follow-up.

For visibility, the one capability added since your July review beyond the minimal set is rule injection: ECC's portable rules/common/ files are read at runtime and appended to Pi's system prompt inside an <ecc-engineering-rules> block. It reuses the before_agent_start path already built for session context, so it introduces no new lifecycle mapping and copies nothing into .pi/. Without it, ECC in Pi had skills and commands but none of its engineering standards. Happy to split it out if you would rather the first release be strictly the minimal set.

@Renan-Olovics
Renan-Olovics marked this pull request as ready for review August 11, 2026 06:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/pi/pi-package-manifest.test.js (1)

215-218: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate all published skill manifests.

The packed artifact omits skills/skill-comply/SKILL.md. Assert that every checkout skills/**/SKILL.md is present in npm pack --dry-run --json.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/pi/pi-package-manifest.test.js` around lines 215 - 218, Extend the
package manifest assertion around the existing files.some check to enumerate
every checkout skills/**/SKILL.md file and assert each appears in the npm pack
dry-run file list, including skills/skill-comply/SKILL.md. Preserve the existing
requirement that the package contains at least one skills/ entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.pi/extensions/index.ts:
- Around line 471-482: Update the rule-loading status flow around
loadPortableRules and describeRulesStatus to track the number of rule files
successfully loaded, excluding read failures, empty files, and files skipped
after MAX_RULES_BYTES is reached. Report that tracked count instead of
PORTABLE_RULE_FILES.length, and reset cachedRuleFileCount to zero when
ECC_PI_RULES disables the rules.
- Around line 378-425: Update normalizePiPackageName and its use in
listInstalledPiPackages to support object-form package entries, extracting and
normalizing the package name from the entry’s source field (including
npm-prefixed sources). Preserve existing handling for string entries and ensure
these packages are included in the installed-name set.

In @.pi/README.md:
- Around line 121-142: Update the Notes bullet that says MCP servers are out of
scope so it references the new MCP section and its pi-mcp-adapter guidance
instead. Remove the conflicting direction to use ECC MCP configs only with other
harnesses, while preserving the remaining Notes content.

---

Outside diff comments:
In `@tests/pi/pi-package-manifest.test.js`:
- Around line 215-218: Extend the package manifest assertion around the existing
files.some check to enumerate every checkout skills/**/SKILL.md file and assert
each appears in the npm pack dry-run file list, including
skills/skill-comply/SKILL.md. Preserve the existing requirement that the package
contains at least one skills/ entry.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d0a0da7f-4492-412f-a0da-60061df15c99

📥 Commits

Reviewing files that changed from the base of the PR and between 04b4aba and 8587a0a.

📒 Files selected for processing (6)
  • .pi/README.md
  • .pi/extensions/index.ts
  • docs/architecture/harness-adapter-compliance.md
  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-extension-adapter.test.js
  • tests/pi/pi-package-manifest.test.js
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (17)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Delegate complex, domain-specific, architectural, security-sensitive, review, build, and testing tasks to the appropriate specialized agent; use parallel execution for independent operations.
For new features and bug fixes, follow TDD: write a failing test first, implement the minimum solution, then refactor and verify coverage.
Maintain at least 80% test coverage and provide unit, integration, and end-to-end tests, including critical user flows.
Never compromise security: validate all inputs, prevent SQL injection with parameterized queries, sanitize HTML against XSS, enable CSRF protection, verify authentication and authorization, rate-limit endpoints, and avoid leaking sensitive data in errors.
Never hardcode API keys, passwords, tokens, or other secrets; use environment variables or a secret manager, validate required secrets at startup, and rotate exposed secrets immediately.
If a security issue is found, stop, use the security-reviewer agent, fix critical issues, rotate exposed secrets, and review the codebase for similar issues.
Always create new objects and return new copies with changes applied; never mutate existing objects.
Organize code into many small, focused files by feature or domain rather than by type; target 200–400 lines and keep files below 800 lines where practical.
Handle errors at every level, show user-friendly messages in UI code, log detailed context server-side, and never silently swallow errors.
Validate all external and user input at system boundaries using schema-based validation; fail fast with clear messages and never trust external data.
Keep functions under 50 lines, files focused and under 800 lines, avoid nesting deeper than four levels, avoid hardcoded values, and use readable, well-named identifiers.
Plan complex features before implementation, identifying dependencies and risks and breaking work into phases.
After modifying code, run code review immediately and address critical and high-severity issues.
Store personal ...

Files:

  • docs/architecture/harness-adapter-compliance.md
  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,go,rb,php,scala,kt}

📄 CodeRabbit inference engine (.cursor/rules/common-coding-style.md)

**/*.{js,ts,jsx,tsx,py,java,cs,go,rb,php,scala,kt}: Always create new objects, never mutate existing ones. Use immutable patterns to prevent hidden side effects and enable safe concurrency
Organize code into many small files (200-400 lines typical, 800 lines max) organized by feature/domain rather than by type
Always handle errors explicitly at every level and never silently swallow errors
Always validate all user input before processing at system boundaries
Use schema-based validation where available
Fail fast with clear error messages when validation fails
Never trust external data (API responses, user input, file content)
Ensure code is readable and well-named
Keep functions small (less than 50 lines)
Keep files focused (less than 800 lines)
Avoid deep nesting (more than 4 levels)
Do not use hardcoded values; use constants or configuration instead

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

No hardcoded secrets (API keys, passwords, tokens) - validate before any commit

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php}: All user inputs must be validated
Enable CSRF protection on all state-changing endpoints
Verify authentication and authorization for all protected endpoints
Implement rate limiting on all endpoints to prevent abuse
Ensure error messages do not leak sensitive data in responses

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,sql}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

Use parameterized queries to prevent SQL injection

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,html,php,java,cs,rb,go}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

Implement XSS prevention by sanitizing HTML output

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp,properties,yml,yaml,json,env,config}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

NEVER hardcode secrets in source code - ALWAYS use environment variables or a secret manager

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/typescript-coding-style.md)

**/*.{ts,tsx,js,jsx}: Use spread operator for immutable updates in TypeScript/JavaScript instead of direct mutation
Use async/await with try-catch for error handling in TypeScript/JavaScript
Use Zod for schema-based input validation in TypeScript/JavaScript
No console.log statements in production code; use proper logging libraries instead

**/*.{ts,tsx,js,jsx}: Auto-format JavaScript/TypeScript files using Prettier after edit
Warn about console.log statements in edited files
Check all modified files for console.log statements before session ends

**/*.{ts,tsx,js,jsx}: Use the ApiResponse interface pattern with generic type parameter: interface ApiResponse<T> { success: boolean; data?: T; error?: string; meta?: { total: number; page: number; limit: number; } }
Implement custom React hooks following the pattern: export a named function with use prefix, generic type parameters, and proper useEffect cleanup for side effects

**/*.{ts,tsx,js,jsx}: Never hardcode secrets; always use environment variables for sensitive credentials like API keys
Throw an error when required environment variables are not configured to fail fast and ensure security prerequisites are met

Use Playwright as the E2E testing framework for critical user flows in TypeScript/JavaScript

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
{package.json,*.config.js,scripts/**/*.js}

📄 CodeRabbit inference engine (CLAUDE.md)

Package manager detection should support npm, pnpm, yarn, and bun, with configuration via CLAUDE_PACKAGE_MANAGER environment variable or project config.

Files:

  • scripts/lib/harness-adapter-compliance.js
scripts/**/*.js

📄 CodeRabbit inference engine (CLAUDE.md)

Ensure cross-platform support for Windows, macOS, and Linux via Node.js scripts in the scripts/ directory.

Files:

  • scripts/lib/harness-adapter-compliance.js
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,jsx,tsx}: Always create new objects and never mutate in place; return new copies instead
Keep files between 200–400 lines typical, with a maximum of 800 lines
Extract helpers when a file exceeds 200 lines
Handle errors explicitly at every level; never swallow errors silently
Validate all user input before processing; use schema-based validation where available
Never trust external data (API responses, file content, query params); always validate
All user inputs must be validated and sanitized
Error messages must be scrubbed of sensitive internals
Use readable, well-named identifiers in all code
Keep functions under 50 lines
Keep files under 800 lines
Avoid nesting deeper than 4 levels
Implement comprehensive error handling in all code
Do not hardcode values; use constants or environment configuration instead
Do not use in-place mutation; always return new objects or state

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,json,env*}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not hardcode secrets, API keys, passwords, or tokens

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts}: Use parameterized queries for all database writes (no string interpolation)
Auth/authz must be checked server-side for every sensitive path
Rate limiting must be applied to all public endpoints

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

HTML output must be sanitized where applicable

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,env*}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Required environment variables must be validated at startup

Files:

  • scripts/lib/harness-adapter-compliance.js
  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
{scripts,bin}/**

⚙️ CodeRabbit configuration file

{scripts,bin}/**: Focus on command injection, unsafe subprocess usage, path traversal, SSRF, secret exposure, and missing tests for new CLI behavior.

Files:

  • scripts/lib/harness-adapter-compliance.js
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write tests before implementation (test-driven development); target 80%+ coverage
Achieve minimum 80% test coverage across all three layers: Unit, Integration, and E2E
Use AAA structure (Arrange / Act / Assert) in tests with descriptive test names that explain behavior under test

Files:

  • tests/pi/pi-package-manifest.test.js
  • tests/pi/pi-extension-adapter.test.js
🪛 ast-grep (0.45.1)
tests/pi/pi-package-manifest.test.js

[error] 42-42: An archive entry path (e.g. entry.path / entry.fileName / header.name) is joined to an output directory without validating that the resolved path stays inside that directory. A malicious archive can use "../" sequences to escape the extraction directory and overwrite arbitrary files (Zip Slip). Resolve the path and verify it starts with the normalized output directory, or strip traversal with path.basename, before writing the entry.
Context: path.join(dir, entry.name)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(zip-slip-archive-extraction-javascript)

.pi/extensions/index.ts

[warning] 356-356: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(path.join(ECC_ROOT, "rules", "common", file), "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)


[warning] 408-408: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(file, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename-typescript)

tests/pi/pi-extension-adapter.test.js

[warning] 46-46: Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: require("child_process")
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process)


[warning] 169-169: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(settingsFile, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)


[warning] 255-255: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(path.join(rootDir, "rules", "common", file), "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)


[warning] 937-947: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(
settingsFile,
JSON.stringify({
packages: [
"npm:pi-subagents@2.0.0",
"npm:@juicesharp/rpiv-todo@1.4.2",
"/Users/example/local-pi-plugin",
"git:https://github.com/example/pi-plugin.git",
],
})
)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)


[warning] 981-981: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(malformedFile, "{ this is not valid json")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

🪛 LanguageTool
.pi/README.md

[style] ~84-~84: Try using a descriptive adverb here.
Context: ....md, and performance.md` are excluded on purpose: they describe Claude Code primitive...

(ON_PURPOSE_DELIBERATELY)


[style] ~125-~125: Consider an alternative for the overused word “exactly”.
Context: ...and ~/.config/mcp/mcp.json — which is exactly the format ECC already uses in `.mcp.js...

(EXACTLY_PRECISELY)

🔇 Additional comments (21)
docs/architecture/harness-adapter-compliance.md (1)

42-42: LGTM!

scripts/lib/harness-adapter-compliance.js (2)

133-140: LGTM!


147-150: LGTM!

.pi/README.md (3)

16-26: LGTM!


81-97: LGTM!


102-119: LGTM!

.pi/extensions/index.ts (6)

112-136: LGTM!


220-236: LGTM!


327-349: LGTM!


350-376: LGTM!


427-442: LGTM!


549-553: LGTM!

Also applies to: 571-589

tests/pi/pi-extension-adapter.test.js (9)

23-59: LGTM!


108-184: LGTM!


186-234: LGTM!


236-284: LGTM!


486-540: LGTM!


542-628: LGTM!


632-746: LGTM!


748-991: LGTM!


995-1253: LGTM!

Comment thread .pi/extensions/index.ts
Comment thread .pi/extensions/index.ts
Comment thread .pi/README.md
Two reporting defects in /ecc-doctor, the command whose whole job is telling
a user what is actually installed.

Pi's settings accept a `packages` entry in two shapes: the bare source string
("npm:pi-subagents") and an object carrying that source alongside resource
filters ({ source: "npm:pi-subagents", skills: [] }). normalizePiPackageName
only recognized the string, so a user who narrowed which resources a companion
contributes was told the companion was not installed, along with an install
command for something already present. The source type still decides whether a
name is comparable, so an object wrapping a git source or a path stays
unrecognized exactly as before.

loadPortableRules drops rule files it cannot read, drops empty ones, and stops
at MAX_RULES_BYTES, but describeRulesStatus reported PORTABLE_RULE_FILES.length
regardless. A partial install that loaded 3 of 7 files reported "7 rule file(s)"
to the one command a user runs to find a partial install. The loaded count is
now tracked next to the cache and reported as a ratio, with the shortfall named.

Also reconciles the Notes bullet in .pi/README.md, which still called MCP out of
scope after the MCP section landed documenting that ECC's configs load in Pi
through pi-mcp-adapter.

Both defects were reported by CodeRabbit and verified against Pi's own
packages.md before fixing. Adapter tests go from 24 to 26; the two source
contracts that pinned the previous spellings now pin the new guards, so the
object-form unwrapping and the loaded-count reporting cannot be silently
reverted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ecc-tools

ecc-tools Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@Renan-Olovics

Copy link
Copy Markdown
Contributor Author

All three findings from the latest review addressed in 11ad39f6. All three were real, and all three landed in the same place: /ecc-doctor telling a user something false about their own install.

Finding Verdict
normalizePiPackageName ignores object-form packages entries Correct. Verified against Pi's own packages.md: an entry is either the bare source string or { source: "npm:x", skills: [] } with resource filters. Only the string was recognized, so a user who narrowed which resources a companion contributes was told it was not installed — and handed an install command for something already present. The source type still decides whether a name is comparable, so an object wrapping a git source or a path stays unrecognized exactly as before.
describeRulesStatus reports the allowlist length, not what loaded Correct. loadPortableRules drops unreadable files, drops empty ones, and breaks at MAX_RULES_BYTES, so an install that loaded 3 of 7 reported 7 rule file(s) — to the one command a user runs to find a partial install. Now reported as a ratio with the shortfall named: 3/7 rule file(s), … (4 unreadable, empty, or past the size cap).
.pi/README.md Notes bullet contradicts the MCP section Correct. Leftover from before the MCP section landed. The bullet now points at that section instead of calling MCP out of scope.

Tests

Adapter tests go from 24 to 26. Both new ones are behavioral, and the two source contracts that pinned the previous spellings now pin the new guards — including the typeof null === "object" case an unguarded object branch would throw on — so neither fix can be silently reverted.

Full suite: 3,759 passed, 0 failed. npm run lint clean.

No other actionable items in the review; the remaining entries are the inference-engine notes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/pi/pi-extension-adapter.test.js`:
- Around line 1186-1230: Split tests/pi/pi-extension-adapter.test.js into
focused test files by moving the companion-package and rule-diagnostics groups
out of its oversized main function. Extract shared setup and assertion helpers
for reuse, ensuring each resulting file is under 800 lines and every function is
under 50 lines while preserving the existing test behavior.
- Around line 946-983: Refactor the new normalizePiPackageName test cases into
Arrange/Act/Assert structure: define each input in an Arrange step, assign the
normalizePiPackageName result in a separate Act step, and assert that result
afterward. Preserve the existing descriptive test name and expected values for
all object, invalid-source, missing-source, and null cases.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dfce9ad7-b179-42ff-ac7f-13840dc623b1

📥 Commits

Reviewing files that changed from the base of the PR and between 8587a0a and 11ad39f.

📒 Files selected for processing (3)
  • .pi/README.md
  • .pi/extensions/index.ts
  • tests/pi/pi-extension-adapter.test.js
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{js,ts,jsx,tsx,py,java,cs,go,rb,php,scala,kt}

📄 CodeRabbit inference engine (.cursor/rules/common-coding-style.md)

**/*.{js,ts,jsx,tsx,py,java,cs,go,rb,php,scala,kt}: Always create new objects, never mutate existing ones. Use immutable patterns to prevent hidden side effects and enable safe concurrency
Organize code into many small files (200-400 lines typical, 800 lines max) organized by feature/domain rather than by type
Always handle errors explicitly at every level and never silently swallow errors
Always validate all user input before processing at system boundaries
Use schema-based validation where available
Fail fast with clear error messages when validation fails
Never trust external data (API responses, user input, file content)
Ensure code is readable and well-named
Keep functions small (less than 50 lines)
Keep files focused (less than 800 lines)
Avoid deep nesting (more than 4 levels)
Do not use hardcoded values; use constants or configuration instead

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

No hardcoded secrets (API keys, passwords, tokens) - validate before any commit

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php}: All user inputs must be validated
Enable CSRF protection on all state-changing endpoints
Verify authentication and authorization for all protected endpoints
Implement rate limiting on all endpoints to prevent abuse
Ensure error messages do not leak sensitive data in responses

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,sql}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

Use parameterized queries to prevent SQL injection

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,html,php,java,cs,rb,go}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

Implement XSS prevention by sanitizing HTML output

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,swift,kt,rs,c,cpp,h,hpp,properties,yml,yaml,json,env,config}

📄 CodeRabbit inference engine (.cursor/rules/common-security.md)

NEVER hardcode secrets in source code - ALWAYS use environment variables or a secret manager

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/typescript-coding-style.md)

**/*.{ts,tsx,js,jsx}: Use spread operator for immutable updates in TypeScript/JavaScript instead of direct mutation
Use async/await with try-catch for error handling in TypeScript/JavaScript
Use Zod for schema-based input validation in TypeScript/JavaScript
No console.log statements in production code; use proper logging libraries instead

**/*.{ts,tsx,js,jsx}: Auto-format JavaScript/TypeScript files using Prettier after edit
Warn about console.log statements in edited files
Check all modified files for console.log statements before session ends

**/*.{ts,tsx,js,jsx}: Use the ApiResponse interface pattern with generic type parameter: interface ApiResponse<T> { success: boolean; data?: T; error?: string; meta?: { total: number; page: number; limit: number; } }
Implement custom React hooks following the pattern: export a named function with use prefix, generic type parameters, and proper useEffect cleanup for side effects

**/*.{ts,tsx,js,jsx}: Never hardcode secrets; always use environment variables for sensitive credentials like API keys
Throw an error when required environment variables are not configured to fail fast and ensure security prerequisites are met

Use Playwright as the E2E testing framework for critical user flows in TypeScript/JavaScript

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{test,spec}.{js,ts,jsx,tsx}: Write tests before implementation (test-driven development); target 80%+ coverage
Achieve minimum 80% test coverage across all three layers: Unit, Integration, and E2E
Use AAA structure (Arrange / Act / Assert) in tests with descriptive test names that explain behavior under test

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,jsx,tsx}: Always create new objects and never mutate in place; return new copies instead
Keep files between 200–400 lines typical, with a maximum of 800 lines
Extract helpers when a file exceeds 200 lines
Handle errors explicitly at every level; never swallow errors silently
Validate all user input before processing; use schema-based validation where available
Never trust external data (API responses, file content, query params); always validate
All user inputs must be validated and sanitized
Error messages must be scrubbed of sensitive internals
Use readable, well-named identifiers in all code
Keep functions under 50 lines
Keep files under 800 lines
Avoid nesting deeper than 4 levels
Implement comprehensive error handling in all code
Do not hardcode values; use constants or environment configuration instead
Do not use in-place mutation; always return new objects or state

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,jsx,tsx,json,env*}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not hardcode secrets, API keys, passwords, or tokens

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts}: Use parameterized queries for all database writes (no string interpolation)
Auth/authz must be checked server-side for every sensitive path
Rate limiting must be applied to all public endpoints

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

HTML output must be sanitized where applicable

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*.{js,ts,env*}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Required environment variables must be validated at startup

Files:

  • tests/pi/pi-extension-adapter.test.js
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Delegate complex, domain-specific, architectural, security-sensitive, review, build, and testing tasks to the appropriate specialized agent; use parallel execution for independent operations.
For new features and bug fixes, follow TDD: write a failing test first, implement the minimum solution, then refactor and verify coverage.
Maintain at least 80% test coverage and provide unit, integration, and end-to-end tests, including critical user flows.
Never compromise security: validate all inputs, prevent SQL injection with parameterized queries, sanitize HTML against XSS, enable CSRF protection, verify authentication and authorization, rate-limit endpoints, and avoid leaking sensitive data in errors.
Never hardcode API keys, passwords, tokens, or other secrets; use environment variables or a secret manager, validate required secrets at startup, and rotate exposed secrets immediately.
If a security issue is found, stop, use the security-reviewer agent, fix critical issues, rotate exposed secrets, and review the codebase for similar issues.
Always create new objects and return new copies with changes applied; never mutate existing objects.
Organize code into many small, focused files by feature or domain rather than by type; target 200–400 lines and keep files below 800 lines where practical.
Handle errors at every level, show user-friendly messages in UI code, log detailed context server-side, and never silently swallow errors.
Validate all external and user input at system boundaries using schema-based validation; fail fast with clear messages and never trust external data.
Keep functions under 50 lines, files focused and under 800 lines, avoid nesting deeper than four levels, avoid hardcoded values, and use readable, well-named identifiers.
Plan complex features before implementation, identifying dependencies and risks and breaking work into phases.
After modifying code, run code review immediately and address critical and high-severity issues.
Store personal ...

Files:

  • tests/pi/pi-extension-adapter.test.js
🔇 Additional comments (2)
.pi/README.md (1)

189-189: LGTM!

.pi/extensions/index.ts (1)

334-342: LGTM!

Also applies to: 358-358, 386-386, 442-460, 500-502

Comment on lines +946 to +983
["companion package name normalization (behavioral mirror): an object entry with resource filters resolves to the same name as the bare source string", () => {
assert.strictEqual(
normalizePiPackageName({ source: "npm:pi-subagents", skills: [] }),
"pi-subagents",
"expected the object form Pi documents for filtered packages to resolve to the " +
"same name as the bare string; a user who narrows which resources pi-subagents " +
"contributes still has it installed, and /ecc-doctor exists to report exactly that"
)
assert.strictEqual(
normalizePiPackageName({ source: "npm:@juicesharp/rpiv-todo@1.4.2", prompts: ["prompts/review.md"] }),
"@juicesharp/rpiv-todo",
"expected an object entry to go through the same version-stripping path as a " +
"string entry, scope intact"
)
assert.strictEqual(
normalizePiPackageName({ source: "git:github.com/example/pi-plugin@v1" }),
undefined,
"expected an object entry wrapping a git source to stay unrecognized; the source " +
"type decides, not the entry shape"
)
assert.strictEqual(
normalizePiPackageName({ extensions: ["extensions/*.ts"] }),
undefined,
"expected an object entry with no source field to normalize to undefined instead " +
"of throwing"
)
assert.strictEqual(
normalizePiPackageName({ source: 42 }),
undefined,
"expected a non-string source to normalize to undefined instead of throwing"
)
assert.strictEqual(
normalizePiPackageName(null),
undefined,
"expected a null entry to normalize to undefined; typeof null is \"object\", so " +
"this is the case an unguarded object branch would throw on"
)
}],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Arrange / Act / Assert for these new test cases.

Assign each normalizePiPackageName(...) result in an Act step. Assert that value in a separate Assert step. This makes each expected behavior clear when an assertion fails.

As per coding guidelines, "Use AAA structure (Arrange / Act / Assert) in tests with descriptive test names that explain behavior under test."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/pi/pi-extension-adapter.test.js` around lines 946 - 983, Refactor the
new normalizePiPackageName test cases into Arrange/Act/Assert structure: define
each input in an Arrange step, assign the normalizePiPackageName result in a
separate Act step, and assert that result afterward. Preserve the existing
descriptive test name and expected values for all object, invalid-source,
missing-source, and null cases.

Source: Coding guidelines

Comment on lines +1186 to +1230
["/ecc-doctor reports rule files actually loaded, not the allowlist length (source contract)", () => {
assert.ok(
/let\s+cachedRuleFileCount\s*=\s*0/.test(extensionSource),
"expected .pi/extensions/index.ts to track how many rule files actually loaded in a " +
"cachedRuleFileCount counter alongside cachedRules"
)
assert.ok(
/cachedRuleFileCount\s*=\s*sections\.length/.test(extensionSource),
"expected loadPortableRules in .pi/extensions/index.ts to set cachedRuleFileCount " +
"from sections.length, which is what survived the read failures, the empty-file " +
"skip, and the MAX_RULES_BYTES break"
)

const disabledBranch = extensionSource.slice(
extensionSource.indexOf("isDisabledByEnv(process.env.ECC_PI_RULES)"),
extensionSource.indexOf("const sections: string[] = []")
)
assert.ok(
/cachedRuleFileCount\s*=\s*0/.test(disabledBranch),
"expected the ECC_PI_RULES disable branch of loadPortableRules in " +
".pi/extensions/index.ts to reset cachedRuleFileCount to 0, so the counter can " +
"never survive from a prior load into a disabled session"
)

const statusStart = extensionSource.indexOf("function describeRulesStatus")
assert.ok(
statusStart !== -1,
"expected .pi/extensions/index.ts to define a function named describeRulesStatus"
)
const nextFunctionStart = extensionSource.indexOf("\nfunction ", statusStart + 1)
const statusSource =
nextFunctionStart === -1
? extensionSource.slice(statusStart)
: extensionSource.slice(statusStart, nextFunctionStart)

assert.ok(
/\$\{cachedRuleFileCount\}\/\$\{PORTABLE_RULE_FILES\.length\}\s+rule file/.test(statusSource),
"expected describeRulesStatus in .pi/extensions/index.ts to report the loaded count " +
"over the allowlist length (`${cachedRuleFileCount}/${PORTABLE_RULE_FILES.length} " +
"rule file(s)`); loadPortableRules silently skips unreadable and empty files and " +
"breaks out of the loop at MAX_RULES_BYTES, so reporting the allowlist length " +
"alone makes an install that loaded 3 of 7 report 7 -- and /ecc-doctor is the one " +
"place a user looks to find a partial install"
)
}],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Split this test script before adding more test groups.

tests/pi/pi-extension-adapter.test.js is 1,350 lines. Its main function spans more than 1,000 lines. Move the companion-package and rule-diagnostics groups into focused test files. Extract shared helpers so each resulting file stays below 800 lines and each function stays below 50 lines.

As per coding guidelines, "Keep functions small (less than 50 lines)" and "Keep files focused (less than 800 lines)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/pi/pi-extension-adapter.test.js` around lines 1186 - 1230, Split
tests/pi/pi-extension-adapter.test.js into focused test files by moving the
companion-package and rule-diagnostics groups out of its oversized main
function. Extract shared setup and assertion helpers for reuse, ensuring each
resulting file is under 800 lines and every function is under 50 lines while
preserving the existing test behavior.

Source: Coding guidelines

@affaan-m

Copy link
Copy Markdown
Owner

think this is good to merge in. nice work!

@haelyra
haelyra merged commit eb49702 into affaan-m:main Aug 12, 2026
78 of 79 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants