Skip to content

feat: list folders with the children command - #224

Merged
pchuri merged 4 commits into
mainfrom
fm/ccli-children-folders
Aug 6, 2026
Merged

feat: list folders with the children command#224
pchuri merged 4 commits into
mainfrom
fm/ccli-children-folders

Conversation

@pchuri

@pchuri pchuri commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Intent

Implement GitHub issue #223: add support for listing folders as children in the 'confluence children ' command.

Goal: Confluence Cloud supports folders as a content type in page hierarchies, but 'children' only returned child pages, so folders could not be discovered/traversed. Requester mirrors repo docs to Confluence via CI/CD and needs folders visible to replicate directory structure.

What was implemented:

  • Added a '--type <pages|folders|all>' option to the children command. Default is DELIBERATELY 'pages' to keep existing output byte-compatible (a hard backward-compat requirement). 'folders' lists only folders, 'all' lists both.
  • Folders are a Confluence CLOUD concept only. The existing client talks to the v1 REST API (/content/{id}/child/page). Folders are not exposed there, so folder listing uses the v2 endpoint /pages/{id}/direct-children (via a new v2BaseUrl() derived from apiPath by swapping /rest/api -> /api/v2), filtering results to type==='folder'. Passing an absolute URL to the shared axios instance intentionally reuses its auth headers/TLS while bypassing the v1 baseURL.
  • Graceful degradation was a stated requirement: on Server/DC (client.isCloud() false), '--type folders|all' must NOT crash; it prints a stderr warning and lists no folders (empty-with-warning), keeping stdout JSON valid.
  • Content type is surfaced in every output format: JSON already has a 'type' field per item (now 'folder' for folders); the human list format adds a '[folder]'/'[page]' tag but ONLY when folders can appear (type folders/all) to preserve byte-compat for the default; the tree format uses a folder icon vs page icon.

Deliberate scope decision: folder listing covers DIRECT children. Combining --recursive with --type folders/all recurses pages as before but lists only top-level folders; deep folder-tree traversal was intentionally left out as it would need a separate v2 traversal engine and is beyond the issue's acceptance criteria.

Docs: README (usage + --type explanation + Cloud-only note), plugin SKILL.md option table, and --help all document the option.
Tests: added client coverage for getChildFolders (v2 endpoint + folder filtering) and CLI coverage for default, folders, all, non-Cloud graceful degradation, and invalid --type. Full suite (857 tests) and eslint pass locally.

Constraint: public open-source repo; all public text is English with no company-internal references. Conventional commit, no AI/tool co-author.

What Changed

  • Add --type <pages|folders|all> to children, preserving page-only output by default.
  • Fetch paginated direct-child folders through Confluence Cloud’s v2 API, with graceful Server/Data Center degradation.
  • Distinguish pages and folders across JSON, list, and tree output; update documentation and test coverage.

Risk Assessment

✅ Low: The change is well-bounded, satisfies the stated intent, preserves legacy page output, and resolves the previously identified pagination, warning, URL, and scope-documentation risks.

Testing

Focused and full Jest suites passed, while real CLI verification demonstrated legacy-compatible default output, v2 folder discovery, content-type presentation, direct-only folder traversal, valid Server/DC degradation, validation errors, and documented help.

Evidence: End-to-end CLI transcript
=== Default remains byte-compatible and page-only ===
$ confluence children 123
exit: 0
stdout:
Child pages:

1. Release Notes

Total: 1 child page
stderr:
(empty)
requests:
GET /wiki/rest/api/content/123/child/page?limit=500&expand=space,version

=== Cloud folders JSON uses v2 and returns folders only ===
$ confluence --json children 123 --type folders
exit: 0
stdout:
{
  "pageId": "123",
  "childCount": 2,
  "children": [
    {
      "id": "400",
      "title": "API Guides",
      "type": "folder",
      "status": "current",
      "spaceKey": null,
      "parentId": "123",
      "version": null,
      "url": null
    },
    {
      "id": "500",
      "title": "Runbooks",
      "type": "folder",
      "status": "current",
      "spaceKey": null,
      "parentId": "123",
      "version": null,
      "url": null
    }
  ]
}
stderr:
(empty)
requests:
GET /wiki/api/v2/pages/123/direct-children?limit=250

=== Combined human list tags pages and folders ===
$ confluence children 123 --type all --show-id
exit: 0
stdout:
Children:

1. Release Notes [page] (ID: 200)
2. API Guides [folder] (ID: 400)
3. Runbooks [folder] (ID: 500)

Total: 3 items
stderr:
(empty)
requests:
GET /wiki/rest/api/content/123/child/page?limit=500&expand=space,version
GET /wiki/api/v2/pages/123/direct-children?limit=250

=== Recursive tree recurses pages and keeps folders direct-only ===
$ confluence children 123 --type all --recursive --max-depth 2 --format tree
exit: 0
stdout:
📁 Documentation Home
├── 📄 Release Notes
  └── 📄 Version 2026.8
├── 📁 API Guides
└── 📁 Runbooks

Total: 4 items
stderr:
(empty)
requests:
GET /wiki/rest/api/content/123/child/page?limit=500&expand=space,version
GET /wiki/rest/api/content/200/child/page?limit=500&expand=space,version
GET /wiki/api/v2/pages/123/direct-children?limit=250
GET /wiki/rest/api/content/123?expand=space,history,version,ancestors

=== Server/DC degrades to valid empty JSON plus stderr warning ===
$ confluence --json children 123 --type folders
exit: 0
stdout:
{
  "pageId": "123",
  "childCount": 0,
  "children": []
}
stderr:
Folders are only supported on Confluence Cloud; none were listed.
requests:
(none)

=== Invalid type fails before any API call ===
$ confluence children 123 --type bogus
exit: 1
stdout:
(empty)
stderr:
Error: Invalid --type "bogus". Valid values are: pages, folders, all.
requests:
(none)

=== CLI help documents the new option ===
$ confluence children --help
exit: 0
stdout:
Usage: confluence children [options] <pageId>

List child pages of a Confluence page

Options:
  -r, --recursive       List all descendants recursively (default: false)
  --max-depth <number>  Maximum depth for recursive listing (default: "10")
  --type <type>         Content type to list: pages, folders, all (folders are
                        Cloud-only) (default: "pages")
  --format <format>     Output format (list, tree). "json" is deprecated — use
                        --json (default: "list")
  --show-url            Show page URLs (default: false)
  --show-id             Show page IDs (default: false)
  -h, --help            display help for command
stderr:
(empty)
requests:
(none)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 4 issues found → auto-fixed (2) ✅
  • 🚨 bin/confluence.js:824 - The required criterion says Server/DC folders|all “prints a stderr warning,” but else if (!jsonMode) suppresses it for JSON output. Remove this guard or explicitly revise the acceptance criterion.
  • 🚨 lib/confluence-client.js:1793 - Only the first v2 response is processed. The API paginates via its Link header, so folders after the first 250 children are silently omitted—potentially all folders if pages precede them. Follow next until exhausted before filtering. See Atlassian’s API documentation.
  • ⚠️ bin/confluence.js:822 - Folders are passed into renderers that build URLs from page.space.key, but normalized folders have no space; --show-url therefore prints /spaces/undefined/pages/&lt;folder-id&gt; in list and tree output. Preserve a usable folder URL or omit the URL when unavailable.
  • ⚠️ README.md:591 - The v2 direct-children endpoint requires read:hierarchical-content:confluence, but the documented scoped-token minimum omits it, so least-privilege users following the README/SKILL guidance may receive 401 responses. Document the scope and include it in the scoped-token 401 hint. See Atlassian’s endpoint documentation.

🔧 Fix: Fix folder pagination and output edge cases
2 issues (1 error, 1 warning) still open:

  • 🚨 bin/confluence.js:928 - Required criterion: default pages output must remain “byte-compatible.” The new if (child.url) return child.url changes children --show-url output from the legacy synthesized /spaces/&lt;key&gt;/pages/&lt;id&gt; URL to the API-provided URL, commonly including the page title. Preserve legacy URL generation for pages and only suppress unavailable folder URLs.
  • ⚠️ README.md:361 - read:hierarchical-content:confluence is a granular scope, but README and SKILL now include it under “classic scopes.” Rename the guidance to distinguish the required granular scope so scoped-token users can locate it correctly.

🔧 Fix: Preserve legacy page URLs and clarify folder scope
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • npm test -- --runInBand tests/confluence-client.test.js tests/metadata-cli.test.js
  • Real node bin/index.js processes against a local mock Confluence API: default pages, folders JSON, combined list, recursive tree, Server/DC degradation, invalid type, and help
  • npm test -- --runInBand
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

pchuri added 4 commits August 6, 2026 19:08
Add a --type <pages|folders|all> option to the children command. The
default remains pages for full backward compatibility. Folders are a
Confluence Cloud content type, fetched via the v2 direct-children
endpoint; on Server/DC the CLI warns and lists no folders instead of
failing. Each item's content type is surfaced in both JSON (type field)
and human-readable output (a [folder] tag and folder tree icon).
@pchuri
pchuri merged commit 91a3b9d into main Aug 6, 2026
6 checks passed
github-actions Bot pushed a commit that referenced this pull request Aug 6, 2026
# [2.20.0](v2.19.1...v2.20.0) (2026-08-06)

### Features

* list folders with the children command ([#224](#224)) ([91a3b9d](91a3b9d))
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 2.20.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant