Load installed-plugin skills and align cua-cli startup with pi#32
Conversation
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>
Independent review — request changesI 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 —
|
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.
|
Addressed the review in d5491d6. BLOCKER — project-local MINOR — unused MINOR — Full verification re-run locally and green: |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: No-skills still loads project skills
- I made project
.agents/skillsconditional ondisabledand added a test fixture proving--no-skillsnow excludes project-local skills while context files still load.
- I made project
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]; |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit d5491d6. Configure here.
|
Created a monitoring plan for this PR. What this PR does: Users of the Intended effect: No production server telemetry exists for the CLI (it runs locally). Confirmation is manual:
Risks:
Status updates will be posted automatically on this PR as monitoring progresses. |



Root cause
cualoaded skills with a hand-rolled scan inpackages/cli/src/harness-skills.tsthat 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'ssettings.jsonand clones it under the agent dir; the skills live inside that package) were invisible tocuaeven 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)
DefaultResourceLoader.harness-skills.tsnow builds aDefaultResourceLoader(the same loader pi's TUI uses) overgetAgentDir()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/skillsis passed throughadditionalSkillPaths(which loads unconditionally and never binds extensions) alongside--skillpaths. 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'sloadSkillsto recover thecontentthe harness needs, deduping by file path so a skills root mixing a loose.mdand a nestedSKILL.mdnever double-loads.PI_OFFLINE), so a configured-but-not-installed package is skipped gracefully rather than triggering a network install or a hang.noExtensions: true. cua drives the lower-levelAgentHarness, which cannot bind piAgentSessionextensions — so extensions are never loaded, bound, or displayed. There is no[Extensions]section anywhere. This is a deliberate decision.buildCuaHarnessnow appends the loaded context files (AGENTS.md/CLAUDE.md) to the system prompt, so the[Context]section reflects what the model actually sees.contextFilesare threaded throughsetupHarnessRuntimeinto both the print and interactive paths.--no-skillsdisables skill discovery only; context files still load.theme.fg(...)for the header, section labels, message list, status line, editor). The preamble renders acua 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 inmdHeading, dim body). The version is inlined by a tsdowndefineso the bundled bin never readspackage.jsonat runtime.Tested
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/skillsis discovered; a not-installednpm:package is skipped without throwing/hanging; a mixed loose/nested skills root loads each skill exactly once;--skillpaths still load;--no-skillsstill disables skills while context still loads;AGENTS.mdis loaded and injected into the system prompt.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 --helpexits 0) all pass locally. The ptywright native binding was built with Zig 0.15.2 for these runs.Scope
cli-only:
packages/agentandpackages/aiare untouched. No.jsextensions 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), socuapicks up pi-installed package skills, the agent dir, and the usual~/.agents/skillspaths—not only a manual directory scan. Project.agents/skillsis wired viaadditionalSkillPathsso skills load without a trust prompt; startup setsPI_OFFLINEso missing packages are skipped instead of auto-installing. Pi extensions stay off (noExtensions). Discovered skill paths are re-read with cua-agentloadSkillsfor fullcontent, with deduping for mixed loose/nested layouts.Context files (
AGENTS.md, etc.) are discovered alongside skills, appended to the system prompt inbuildCuaHarness, and passed into the interactive TUI.--no-skillsstill disables skills only; context keeps loading.TUI adopts pi’s theme (
initTheme, palette via piTheme), shows acua 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.