Skip to content

feat: add protected Pi Confluence tools - #225

Open
arc-terry wants to merge 19 commits into
pchuri:mainfrom
arc-terry:feature/pi-confluence-tools
Open

feat: add protected Pi Confluence tools#225
arc-terry wants to merge 19 commits into
pchuri:mainfrom
arc-terry:feature/pi-confluence-tools

Conversation

@arc-terry

Copy link
Copy Markdown

Summary

Add a typed, protected Confluence integration for the Pi Coding Agent.

Changes

  • Add 13 typed Confluence read tools.
  • Add protected Confluence mutation tools with:
    • Explicit write opt-in.
    • Space allowlisting.
    • Interactive Pi confirmation.
    • Exact confirmation phrases for destructive operations.
    • Canonical page title, ID, and space validation.
  • Add mandatory previews and one-use approvals for bulk copy and version purge operations.
  • Add package-local, shell-free CLI execution with bounded output, timeouts, abort handling, and credential redaction.
  • Add payload limits and project-contained file validation.
  • Add preflight ownership and target verification.
  • Add comprehensive tests and update Pi/Confluence documentation.

Security

  • Writes are disabled by default.
  • Generic API, arbitrary argv, shell commands, and model-controlled HTTP methods are not exposed.
  • Automated tests do not perform authenticated Confluence mutations.
  • Confluence responses are treated as untrusted external content.

Testing

  • npm test -- --runInBand — 32 suites passed, 1,060 tests passed.
  • npm run lint — passed.

Notes

The feature branch contains the sanitized history and excludes Superpowers artifacts.

@arc-terry
arc-terry marked this pull request as ready for review August 23, 2026 13:02

@pchuri pchuri left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thank you for this contribution — the scale and care here are impressive. The protection design (write opt-in, space allowlisting, one-use preview approvals, exact confirmation phrases, project-path containment) is genuinely well thought out, all 1,060 tests pass locally, and I found no classic path-traversal / shell-injection / prefix-match issues. The copy-tree dry-run filter change is also a correct fix — it aligns the dry-run count with what copyPageTree actually creates.

Before I can merge, a few critical issues need fixing. I'm deliberately limiting this round to blockers only:

1. Packaging: mandatory peer deps reach every npm user (package.json)
@earendil-works/pi-coding-agent and typebox are non-optional peers, so npm ≥7 auto-installs them for every npm install -g confluence-cli. The lockfile grows 409 → 551 packages (AWS SDK, Anthropic SDK, Google GenAI, native binaries), and scripts/generate-prod-shrinkwrap.sh filters only dev: true, so the published shrinkwrap would pin all of it as production deps. The test suite passes with only typebox present, so the agent peer is type-only. Please mark both peers optional via peerDependenciesMeta, and exclude peer: true entries in the shrinkwrap script.

2. Credentials can reach model context (lib/pi/command-runner.js redactText; lib/confluence-client.js:380)
redactText only masks values present in env — with credentials in ~/.confluence-cli/config.json or ~/.netrc (HOME is forwarded) it's a no-op. And the display-URL error path console.errors the full AxiosError (~11KB), which includes the Authorization header; for basic auth it's base64, so even env-based substring redaction misses it. Reproduced against axios 1.18.1. Please stop dumping the raw AxiosError and redact Authorization-header forms.

3. Read tools can overwrite project files with writes disabled (.pi/extensions/confluence-cli.ts normalizeReadInput; bin/commands/export.js:251)
confluence_convert is registered unconditionally (even under CONFLUENCE_READ_ONLY), and resolveProjectOutputPath returns an existing regular file unchecked — confluence_convert {outputFile: "package.json"} silently replaces it (.git/hooks is reachable too). Separately, export --dry-run is honored only in the recursive branch, so a non-recursive dryRun: true still writes files. Please refuse to overwrite existing files from read tools (or gate them behind the write opt-in), and fix the non-recursive dry-run.

4. The fixed 48KB output cap makes core tools structurally unusable at real-world scale (lib/pi/preflight.js)
Measured with the CLI's pretty-printed JSON: confluence_create fails permanently on instances with ~500+ spaces (spaces --all enumeration), copy_tree_preview/copy_tree break at ~328 descendants, and comment_delete/attachment_delete are blocked on heavy pages (25 comments × ~400-char bodies; ~141 attachments). Also comment_delete of a reply always fails because the ownership scan never passes --depth all. Targeted lookups (single-space check instead of full enumeration, threading depth) and per-operation output limits would resolve these.

5. Space keys are force-uppercased into outbound mutations (lib/pi/preflight.js requireSpaceKey / handleCreate)
handleCreate writes the uppercased key back into input.spaceKey, so on Server/DC a space with real key dev or ~jsmith gets DEV/~JSMITH forwarded to create — wrong space or not-found. Case-folding is fine for allowlist matching, but the original casing must be preserved in the executed args.

I have a longer list of non-blocking findings (diagnosability, efficiency, contract-test coverage); happy to file those as follow-up issues so this PR stays focused. Thanks again — this is a strong foundation.

@arc-terry

Copy link
Copy Markdown
Author

Hi Pchuri,

My original goal was to make the repository directly installable through the Pi agent command:
pi install "git@github.com:pchuri/confluence-cli.git"
That was the main motivation behind these changes. I’ll work on addressing the five blocking issues you identified in the near future.
Thank you for the quick and detailed review.

@pchuri

pchuri commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Thanks for the context — making the repo directly installable via pi install "git@github.com:pchuri/confluence-cli.git" is a use case I'm happy to support.

One reassurance on blocker #1: marking the peers optional won't break your goal. The pi install flow (clone + npm ci) installs from the lockfile regardless of peer optionality, so the local-checkout path keeps working — the change only protects ordinary npm install -g confluence-cli users from pulling the Pi agent tree. When you update, please also regenerate package-lock.json so the Pi dependency tree drops out of it, and add a peer: true filter to scripts/generate-prod-shrinkwrap.sh.

No rush — ping me when it's ready and I'll re-review promptly.

@arc-terry

Copy link
Copy Markdown
Author

Blocker #1 is ready in c18b17f.

Installation behavior

User / command Before After
Normal npm user: npm install -g confluence-cli npm auto-installed @earendil-works/pi-coding-agent, typebox, and the large Pi dependency tree. Installs only Confluence CLI production dependencies; no Pi coding agent or TypeBox.
Pi local-Git user: pi install "git@github.com:pchuri/confluence-cli.git" Clone + npm ci installed the larger 550-package lockfile. Clone + npm ci still loads the Pi extension; lockfile is 410 packages. typebox is available as a dev dependency for the extension tests, while Pi supplies its own host runtime.

Changes

  • Removed @earendil-works/pi-coding-agent as a package peer: the extension imports it only as a type, and Pi already provides the host runtime.
  • Kept typebox as the sole optional runtime peer.
  • Regenerated package-lock.json, removing the Pi dependency tree (550 → 410 packages).
  • Updated scripts/generate-prod-shrinkwrap.sh to exclude peer:true entries.
  • Added regression coverage for optional peer metadata and shrinkwrap filtering.

Verification: clean npm ci plus Pi-extension tests passed; a packed normal-user install contains neither Pi coding agent nor TypeBox.

@pchuri

pchuri commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Thanks — the packaging fix looks good, and dropping the pi-coding-agent peer entirely (it's type-only) while adding typebox as a devDependency is a better solution than what I suggested: the test suite is now self-contained. Previously I had to vendor typebox by hand to run it; now a plain npm ci gives 33 suites / 1,063 tests passing.

I verified the three install paths end to end:

  • package-lock.json: 551 → 411 packages (main is 409; the +2 are typebox and jiti, both dev), with zero AWS / Anthropic / GenAI entries
  • scripts/generate-prod-shrinkwrap.sh: 100 production packages, typebox and jiti both excluded, root devDependencies cleared
  • npm pack + install into a clean project: 99 packages, no typebox, no @earendil-works

The regression test in tests/prod-shrinkwrap.test.js is a nice touch.

Two small non-blocking notes for whenever you next touch this:

  1. "typebox": "*" in devDependencies is looser than the rest of this repo's dependency policy (we pin transitives via overrides). The lockfile and npm ci protect CI, but a contributor running plain npm install would pick up a future 2.x major. ^1.3.18 would be safer.
  2. .pi/ and the pi manifest field are still published, but typebox is an optional peer and therefore won't be installed for npm consumers — so loading the extension from an npm-installed copy would fail on a missing typebox. The documented local-checkout path (npm cipi install <path>) is unaffected; a one-line note in the README would save someone the confusion.

No rush on the remaining four blockers — take the time you need, and ping me when they're ready.

@arc-terry

Copy link
Copy Markdown
Author

I'm glad to see your comment.
My plan is to address the major blockers first. Blocker 2 is already in progress.
I'd prefer to keep the non-blocking notes as items for future follow-up.

Thanks!

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.

3 participants