Skip to content

Load installed-plugin skills and align cua-cli startup with pi#32

Merged
rgarcia merged 4 commits into
mainfrom
hypeship/cua-cli-pi-resources
Jun 13, 2026
Merged

Load installed-plugin skills and align cua-cli startup with pi#32
rgarcia merged 4 commits into
mainfrom
hypeship/cua-cli-pi-resources

Conversation

@rgarcia

@rgarcia rgarcia commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Root cause

cua loaded skills with a hand-rolled scan in packages/cli/src/harness-skills.ts that only looked at ~/.agents/skills/ and <cwd>/.agents/skills/. It never consulted pi's package registry, so skills bundled by a pi-installed plugin (pi install … records the package in pi's settings.json and clones it under the agent dir; the skills live inside that package) were invisible to cua even though pi's own TUI showed them. Startup was also visually out of step with pi: no [Context] section, mismatched colors, a thin header.

The fix (cli-only)

  • Skills + context via pi's DefaultResourceLoader. harness-skills.ts now builds a DefaultResourceLoader (the same loader pi's TUI uses) over getAgentDir() and the cwd. Project settings stay untrusted to avoid a startup trust prompt, which means pi's trusted project scan is off — so <cwd>/.agents/skills is passed through additionalSkillPaths (which loads unconditionally and never binds extensions) alongside --skill paths. The resulting set is a superset of the old discovery: ~/.agents/skills, <cwd>/.agents/skills, and installed-package skills all load. The loader resolves skill file paths but not the file body, so we bridge back through cua-agent's loadSkills to recover the content the harness needs, deduping by file path so a skills root mixing a loose .md and a nested SKILL.md never double-loads.
  • Non-interactive, no auto-install. Project settings start untrusted (no trust prompt) and package resolution runs offline (PI_OFFLINE), so a configured-but-not-installed package is skipped gracefully rather than triggering a network install or a hang.
  • No extensions. noExtensions: true. cua drives the lower-level AgentHarness, which cannot bind pi AgentSession extensions — so extensions are never loaded, bound, or displayed. There is no [Extensions] section anywhere. This is a deliberate decision.
  • Context injected into the system prompt. buildCuaHarness now appends the loaded context files (AGENTS.md/CLAUDE.md) to the system prompt, so the [Context] section reflects what the model actually sees. contextFiles are threaded through setupHarnessRuntime into both the print and interactive paths. --no-skills disables skill discovery only; context files still load.
  • Styling + preamble. The interactive TUI adopts pi's theme system (init once at startup; palette via theme.fg(...) for the header, section labels, message list, status line, editor). The preamble renders a cua v<version> logo (bold accent + dim version), a dim key-hint row reflecting cua's real bindings (esc/ctrl+c to interrupt; ctrl+c/ctrl+d to exit; / for commands), and [Context] / [Skills] sections styled like pi (label in mdHeading, dim body). The version is inlined by a tsdown define so the bundled bin never reads package.json at runtime.

Tested

  • Hermetic unit tests (isolated HOME, temp agent dir + cwd, local package fixture — no network): a package skill is discovered via the package path; a project-local skill under <cwd>/.agents/skills is discovered; a not-installed npm: package is skipped without throwing/hanging; a mixed loose/nested skills root loads each skill exactly once; --skill paths still load; --no-skills still disables skills while context still loads; AGENTS.md is loaded and injected into the system prompt.
  • ptywright TUI fixtures extended to assert the new preamble (cua v…, key hints) and that [Context] / [Skills] render while [Extensions] never does.
  • npm run build, npx tsc -b, PTYWRIGHT_REQUIRED=1 npm test --workspace @onkernel/cua-cli (42 passing), and the CLI bin smoke test (npm pack → install into a fresh temp project → cua --help exits 0) all pass locally. The ptywright native binding was built with Zig 0.15.2 for these runs.

Scope

cli-only: packages/agent and packages/ai are untouched. No .js extensions added to relative imports (the cli is bundled by tsdown). Generated files untouched.


Note

Medium Risk
Changes startup resource loading and system-prompt composition (what the model sees), including reading user/project agent files; behavior is guarded by tests and offline package skipping, but discovery semantics now follow pi’s loader rather than the old fixed paths.

Overview
Skill and context discovery now goes through pi’s DefaultResourceLoader (same as pi’s TUI), so cua picks up pi-installed package skills, the agent dir, and the usual ~/.agents/skills paths—not only a manual directory scan. Project .agents/skills is wired via additionalSkillPaths so skills load without a trust prompt; startup sets PI_OFFLINE so missing packages are skipped instead of auto-installing. Pi extensions stay off (noExtensions). Discovered skill paths are re-read with cua-agent loadSkills for full content, with deduping for mixed loose/nested layouts.

Context files (AGENTS.md, etc.) are discovered alongside skills, appended to the system prompt in buildCuaHarness, and passed into the interactive TUI. --no-skills still disables skills only; context keeps loading.

TUI adopts pi’s theme (initTheme, palette via pi Theme), shows a cua v… header, key hints, and startup [Context] / [Skills] sections (no [Extensions]). Version is compile-time inlined via tsdown __CUA_VERSION__.

Docs and CLI help are updated; new unit and ptywright tests cover discovery, prompt injection, and preamble rendering.

Reviewed by Cursor Bugbot for commit d5491d6. Bugbot is set up for automated code reviews on this repo. Configure here.

rgarcia and others added 3 commits June 13, 2026 20:14
Replace the hand-rolled skill scan in harness-skills.ts with pi's
DefaultResourceLoader so installed-package skills load (the loader is a
strict superset of the old ~/.agents/skills discovery). Bridge pi's
skill file paths back through cua-agent's loadSkills to recover the
skill body the harness needs, deduping by file path so a skills root
that mixes loose .md and nested SKILL.md never double-loads.

Startup stays non-interactive: project settings start untrusted (no
trust prompt) and package resolution runs offline so a configured-but-
not-installed package is skipped instead of triggering a network
install. pi extensions are not loaded — cua drives the lower-level
AgentHarness, which cannot bind pi AgentSession extensions.

Also load context files (AGENTS.md/CLAUDE.md) and inject their content
into the system prompt so the model sees what the [Context] section
advertises; thread contextFiles through setupHarnessRuntime and
buildCuaHarness.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adopt pi's theme system in place of the hand-rolled ANSI: initialize
the theme once at TUI startup and route the message list, status line,
editor, and section labels through pi's palette (accent, dim,
mdHeading, muted, success, error, warning).

Rework the startup preamble to match pi: a "cua v<version>" logo
(bold accent name + dim version), a dim key-hint row reflecting cua's
real bindings, and [Context] / [Skills] sections styled like pi (label
in mdHeading, dim body). No [Extensions] section. The version is
inlined by a tsdown define so the bundled bin never reads package.json
at runtime.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add hermetic tests (isolated HOME, temp agentDir/cwd) that prove a
skill bundled in a pi-installed package is discovered, a configured-
but-not-installed package is skipped without throwing or hanging, a
mixed loose/nested skills root loads each skill once, and AGENTS.md
context is injected into the system prompt. Extend the ptywright
fixtures to assert the new preamble and the [Context]/[Skills]
sections, and that no [Extensions] section renders.

Update the --skill help text, README, and architecture.md to say
skills and context come from pi's resource loader (including installed
packages and the pi agent dir) and that pi extensions are not executed
by cua.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@rgarcia

rgarcia commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Independent review — request changes

I cloned fresh, checked out the branch, and built + tested everything myself (zig 0.15.2 on PATH for the ptywright native binding). Most of the PR is solid and the plan is largely followed, but I found one blocking behavior regression that contradicts the PR's "strict superset of the old discovery" claim.

Blocker — <cwd>/.agents/skills project-local skills are no longer discovered

packages/cli/src/harness-skills.ts:50 forces projectTrusted: false on the SettingsManager. In pi's loader, the project-local .agents/skills scan is gated behind project trust (package-manager.js: projectAgentsSkillDirs = projectTrusted ? collectAncestorAgentsSkillDirs(cwd) : []). With trust hardcoded off, <cwd>/.agents/skills is never scanned — yet the old discoverCuaSkills loaded exactly that path (projectAgentsDir = join(cwd, ".agents", "skills")), and both README.md and the --skill help in cli.ts still advertise it as a default. Users with project-local skills silently lose them, with no warning.

I proved this empirically with a hermetic probe (isolated HOME, temp cwd + agentDir) against the actual discoverCuaSkills:

  • skill in ~/.agents/skills → discovered (ok)
  • skill in <cwd>/.agents/skills[], not discovered (regression)

This breaks plan item 1, which explicitly requires project .agents/skills to keep working alongside packages and ~/.agents/skills.

Fix (validated locally — restores discovery with the same probe, and keeps projectTrusted: false so no trust prompt and no untrusted project .pi/ extensions load): add the project skills dir to additionalSkillPaths instead of relying on the trust-gated path. At harness-skills.ts:48-55:

const extras = (opts.extraPaths ?? []).filter((p) => p && p.trim().length > 0);
const projectSkillDir = join(opts.cwd, ".agents", "skills"); // add `join` to the node:path import
const additionalSkillPaths = [...extras, projectSkillDir];
// ...
additionalSkillPaths,

additionalSkillPaths load unconditionally (not trust-gated, extensions never bound), so this restores parity without re-enabling extension loading. Please also add a hermetic test asserting a <cwd>/.agents/skills skill is discovered — the current harness-skills.test.ts only covers package, --skill, and ~/.agents/skills-adjacent paths, so this gap wasn't caught.

What I verified (all pass)

  • npm ci, npm run build (incl. ptywright native via zig 0.15.2), npx tsc -b — clean.
  • PTYWRIGHT_REQUIRED=1 npm test --workspace @onkernel/cua-cli — 41/41 pass, including the ptywright fixtures and the new [Context]/[Skills]/no-[Extensions] scenario.
  • Bin smoke: npm pack → install tarball into a fresh temp project → cua --help exits 0.
  • Context injection is real: harness-assembly.test.ts:77 captures the actual systemPrompt via before_agent_start and asserts the AGENTS.md content + path reach the model (not just the TUI). Verified in harness.ts composeSystemPrompt/formatContextFiles.
  • Missing-package handling is graceful: with PI_OFFLINE the package manager skips a not-installed npm: source (continue, no install, no throw) — confirmed in pi's resolvePackageSources and the hermetic test.
  • Preamble reads cua's own version: tsdown define inlines __CUA_VERSION__ → the bundled chunk has cuaVersion(){ return "0.1.0"; } and renders accent("cua") + dim(" v0.1.0"). No pi VERSION import anywhere.
  • No [Extensions] section; noExtensions: true; docs explicitly state extensions are not executed.
  • Scope is clean: all 17 files under packages/cli/ + docs/architecture.md. packages/agent and packages/ai untouched, no dist/ changes, no .js relative-import extensions, no new any casts, no leaked internal context.

Minor (non-blocking)

  • discoverCuaSkills returns sources and diagnostics that no caller consumes (cli-harness only destructures skills/contextFiles). Pre-existing (the old function did too), but the new code still builds sources: skillDirs for nothing — drop them or surface diagnostics.
  • --no-skills now still loads context files (old behavior returned early with neither). This is deliberate and documented/tested, and defensible (context != skills), but it is a behavior change from the old --no-skills; just flagging for awareness.

Happy to re-review once the project-local skills path is restored. :bufo-thumbsup:

Forcing projectTrusted:false gated off pi's trusted project scan, which
dropped `<cwd>/.agents/skills` from discovery. Load that directory through
additionalSkillPaths instead: it resolves unconditionally and never binds
extensions, so project skills work without a trust prompt. Add a hermetic
test for the project-local path, drop the unused `sources` return field,
and document that --no-skills leaves context files intact.
@rgarcia

rgarcia commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review in d5491d6.

BLOCKER — project-local <cwd>/.agents/skills regression: fixed. Confirmed the root cause against pi's package-manager.js: with projectTrusted: false, projectAgentsSkillDirs is [], so the trusted project scan never reaches <cwd>/.agents/skills. Applied the recommended fix — kept projectTrusted: false (no trust prompt, no untrusted .pi/ extensions) and added the project skills dir to additionalSkillPaths, which resource-loader.js merges into the skill set unconditionally (independent of noSkills/trust) and routes through updateSkillsFromPaths only — no extension binding. Added a hermetic test (discovers a project-local skill from <cwd>/.agents/skills) that writes a skill under the cwd and asserts it loads while agentDir/~/.agents/skills stay empty. The PR body's "strict superset" claim is corrected to describe this mechanism.

MINOR — unused sources/diagnostics return fields: dropped the unused sources field to keep the return shape honest (no caller consumed it; cli-harness.ts only destructures skills/contextFiles). Kept diagnostics: it carries real skill-load errors and is the standard pi/cua diagnostic shape a --verbose path would surface, so it's a deliberate forward-looking field rather than dead weight.

MINOR — --no-skills still loads context files: intended and tested; documented it explicitly in the README ("--no-skills disables skill discovery only; context files still load") so the UX is unambiguous.

Full verification re-run locally and green: npm ci, npm run build, npx tsc -b, PTYWRIGHT_REQUIRED=1 npm test --workspace @onkernel/cua-cli (42 passing, ptywright fixtures ran with Zig 0.15.2), and the bin smoke (npm pack → temp install → cua --help exits 0).

@rgarcia rgarcia marked this pull request as ready for review June 13, 2026 23:27

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: No-skills still loads project skills
    • I made project .agents/skills conditional on disabled and added a test fixture proving --no-skills now excludes project-local skills while context files still load.

Create PR

Or push these changes by commenting:

@cursor push edf93567ee
Preview (edf93567ee)
diff --git a/packages/cli/src/harness-skills.ts b/packages/cli/src/harness-skills.ts
--- a/packages/cli/src/harness-skills.ts
+++ b/packages/cli/src/harness-skills.ts
@@ -50,10 +50,10 @@
 	// Project-local `<cwd>/.agents/skills` is loaded explicitly rather than via
 	// pi's trusted project scan. That scan only runs when the project is trusted
 	// (which would mean prompting the user and binding untrusted `.pi/`
-	// extensions); `additionalSkillPaths` loads the directory unconditionally and
-	// never binds extensions, so project skills work without a trust prompt.
+	// extensions); `additionalSkillPaths` loads the directory without enabling
+	// extensions, so project skills work without a trust prompt.
 	const projectSkillDir = join(opts.cwd, ".agents", "skills");
-	const additionalSkillPaths = [...extras, projectSkillDir];
+	const additionalSkillPaths = opts.disabled === true ? extras : [...extras, projectSkillDir];
 	const loader = new DefaultResourceLoader({
 		cwd: opts.cwd,
 		agentDir,

diff --git a/packages/cli/test/harness-skills.test.ts b/packages/cli/test/harness-skills.test.ts
--- a/packages/cli/test/harness-skills.test.ts
+++ b/packages/cli/test/harness-skills.test.ts
@@ -113,6 +113,7 @@
 		const pkgDir = join(agentDir, "weather-pkg");
 		writeSkill(join(pkgDir, "skills", "weather"), "weather", "Check the weather forecast.", "Run the weather workflow.");
 		writeSettings([pkgDir]);
+		writeSkill(join(cwd, ".agents", "skills", "lint"), "lint", "Run the linter.", "Run the lint workflow.");
 		writeFileSync(join(cwd, "AGENTS.md"), "# Project context\n\nBe concise.\n");
 
 		const env = new NodeExecutionEnv({ cwd });

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit d5491d6. Configure here.

// extensions); `additionalSkillPaths` loads the directory unconditionally and
// never binds extensions, so project skills work without a trust prompt.
const projectSkillDir = join(opts.cwd, ".agents", "skills");
const additionalSkillPaths = [...extras, projectSkillDir];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No-skills still loads project skills

Medium Severity

With --no-skills, pi still loads paths passed via additionalSkillPaths (same mechanism as explicit --skill). This code always appends <cwd>/.agents/skills to that list, so project-local skills can remain in the system prompt even when automatic discovery is meant to be off.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d5491d6. Configure here.

@firetiger-agent

Copy link
Copy Markdown

Created a monitoring plan for this PR.

What this PR does: Users of the cua CLI now automatically get skills bundled in any pi-installed package, and their AGENTS.md/CLAUDE.md context files appear in the new [Context] section of the TUI and are injected into the model's system prompt. Previously only ~/.agents/skills/ and project-local .agents/skills/ were scanned. This is a CLI-only change; no server-side services are affected.

Intended effect: No production server telemetry exists for the CLI (it runs locally). Confirmation is manual:

  • Installed-package skill visibility: baseline 0 (previously invisible); confirmed if [Skills] section shows skills from a pi-installed package after npm update @onkernel/cua-cli
  • Context injection: baseline 0 (context files not loaded); confirmed if [Context] section appears and AGENTS.md is present in the model's system prompt
  • npm publish workflow: baseline N/A (new release); confirmed if gh run list --repo kernel/cua --workflow publish shows a green run

Risks:

  • Theme init ordering — if any code path calls colors.* before initTheme() runs, the CLI crashes with "pi theme not initialized". Alert on any user report containing that error string.
  • Startup hang — if PI_OFFLINE env var is stripped by a wrapper, a configured-but-uninstalled pi package could trigger a network install and hang startup indefinitely. Alert on any user report of the CLI hanging before the prompt appears.
  • Skill dedup on case-insensitive FS — on macOS, path-casing differences could cause the same skill to appear twice in [Skills]. Alert on any user report of duplicate skill names.

Status updates will be posted automatically on this PR as monitoring progresses.

View monitor

@rgarcia rgarcia merged commit 3712e73 into main Jun 13, 2026
7 checks passed
@rgarcia rgarcia deleted the hypeship/cua-cli-pi-resources branch June 13, 2026 23:44
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.

1 participant