Repository navigation
feat(sdk): pm package init --declarative scaffolds a composeExtension blueprint starter - #358
Conversation
…on blueprint starter
The scaffold matrix only demonstrated imperative `defineExtension({ activate })`
authoring + the runtime test helpers. The SDK's flagship declarative loop
(`defineExtensionBlueprint` → `composeExtension`) and the author-time preflight
capstone (`assertExtensionPreflight`) — which SDK.md calls "the capstone of the
author → register → test loop" — were demonstrated by no generated starter, so a
new author running `pm package init` never discovered the easiest, best-practice
path. Discovered by dogfooding the full 9-capability matrix in a temp workspace
(every imperative starter typechecks and passes its generated tests).
Add `pm package init <path> --declarative`:
- `index.ts`: `defineCommand` → `defineExtensionBlueprint({ commands, deactivate })`
→ `export default composeExtension(blueprint)`.
- `index.test.ts`: author-time `assertExtensionPreflight(blueprint, { identity,
target })` (asserts derived + synthesized capabilities) + runtime
`createExtensionTestHarness` (`assertCommandContract`/`runCommand`/`deactivate`).
- README documents the declarative loop; manifest/package.json/tsconfig/.gitignore
stay byte-identical to the imperative commands starter.
Scoped to package-mode (composeExtension is a runtime SDK *value* import; the
import-free extension-only starters must load without it) and the commands
capability (the canonical composeExtension example). Extension-mode, non-commands
`--capability`, and non-init use each return a clear USAGE error. Full
capability-matrix generalization is tracked in pm-lfdv.
Wired the flag through register-setup (commander + options reader),
extension.ts (validation, passthrough, `style` in result), completion
(bash/zsh/fish), and cli-contracts (+regenerated contracts snapshot); documented
in SDK.md. pm-8mxg (epic pm-ugqx).
5049 tests, 100/100/100/100 coverage; typecheck/static-quality/docs-skills/
contracts gates green. Manually dogfooded end-to-end: scaffold → `tsc --noEmit`
→ `node --test` → `pm install --project` → command dispatches the composed module.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HEUoAbSunPoG3Xt83VYd4k
|
@coderabbitai full review |
|
To use Codex here, create a Codex account and connect to github. |
|
Warning Review limit reached
More reviews will be available in 51 minutes and 11 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a ChangesDeclarative extension scaffolding
Sequence Diagram(s)sequenceDiagram
participant indexTestTs
participant assertExtensionPreflight
participant createExtensionTestHarness
indexTestTs->>assertExtensionPreflight: validate the exported blueprint
indexTestTs->>createExtensionTestHarness: run the generated starter
createExtensionTestHarness-->>indexTestTs: execute command behavior
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Code Review
This pull request adds a --declarative flag to pm package init to scaffold a declarative composeExtension blueprint starter for the commands capability in package-mode. It wires the flag through the CLI options, completion scripts, and contracts, and updates the documentation. Feedback on the changes includes a recommendation to use 'junction' instead of 'dir' for directory symlinks on Windows in the test suite to ensure compatibility without requiring administrator privileges.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| // resolve the subpath, then prove the module loads and dispatches for real. | ||
| const sdkLinkDir = path.join(context.pmPath, "node_modules", "@unbrained"); | ||
| await mkdir(sdkLinkDir, { recursive: true }); | ||
| await symlink(process.cwd(), path.join(sdkLinkDir, "pm-cli"), "dir"); |
There was a problem hiding this comment.
On Windows, creating a directory symlink using 'dir' requires administrator privileges or Developer Mode. Using 'junction' as the symlink type when running on Windows allows the test suite to run reliably across all developer environments and CI platforms without requiring special privileges.
| await symlink(process.cwd(), path.join(sdkLinkDir, "pm-cli"), "dir"); | |
| await symlink(process.cwd(), path.join(sdkLinkDir, "pm-cli"), process.platform === "win32" ? "junction" : "dir"); |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== PR Info ==="
gh pr view 358 --repo unbraind/pm-cli --json title,body,files,additions,deletions,changedFiles 2>/dev/null | jq '{title, additions, deletions, changedFiles, files: [.files[].path]}' || true
echo ""
echo "=== Changed Files ==="
gh pr diff 358 --repo unbraind/pm-cli 2>/dev/null | head -3000 || trueLength of output: 68579
Review:
|
There was a problem hiding this comment.
Code Review
This pull request adds a --declarative flag to pm package init to scaffold a declarative composeExtension blueprint starter with an author-time preflight test and a runtime test harness. The flag is wired through CLI commands, autocompletion scripts, and SDK contracts, and is fully documented and tested. Feedback on the test suite suggests using a directory junction instead of a standard directory symlink to ensure compatibility on Windows without requiring elevated privileges.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| // resolve the subpath, then prove the module loads and dispatches for real. | ||
| const sdkLinkDir = path.join(context.pmPath, "node_modules", "@unbrained"); | ||
| await mkdir(sdkLinkDir, { recursive: true }); | ||
| await symlink(process.cwd(), path.join(sdkLinkDir, "pm-cli"), "dir"); |
There was a problem hiding this comment.
On Windows, creating directory symbolic links using the 'dir' type requires administrative privileges or Developer Mode to be enabled. Using 'junction' instead allows the directory link to be created without elevated privileges, making the test suite more robust and developer-friendly across different Windows environments.
| await symlink(process.cwd(), path.join(sdkLinkDir, "pm-cli"), "dir"); | |
| await symlink(process.cwd(), path.join(sdkLinkDir, "pm-cli"), "junction"); |
There was a problem hiding this comment.
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 `@docs/SDK.md`:
- Around line 568-570: Clarify that `--declarative` is only supported for `pm
package init`/`--scaffold` flows, not as a general `--capability` toggle. Update
the usage note near the `composeExtension` guidance to explicitly mention the
`pm package init` scope and keep the wording aligned with the CLI contract.
Refer to the `--declarative`, `--init`, and `--scaffold` options in the
`Declarative Authoring` note so readers understand where the flag applies.
In `@tests/unit/extensions/extension-command.spec.ts`:
- Around line 2184-2186: The workspace link creation in the extension command
test is using a directory symlink type that can fail on Windows CI. Update the
symlink call in the test setup around sdkLinkDir to use a Windows-safe directory
link, such as a junction, or add a platform-specific fallback so the fixture
works on windows-latest. Keep the change localized to the test helper logic near
the symlink invocation.
🪄 Autofix (Beta)
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: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: f66b18fc-f952-43b0-aaf8-1b1780dbdbb8
📒 Files selected for processing (15)
.agents/pm/features/pm-8mxg.toon.agents/pm/history/pm-8mxg.jsonl.agents/pm/history/pm-lfdv.jsonl.agents/pm/tasks/pm-lfdv.toonCHANGELOG.mddocs/SDK.mdsrc/cli/commands/completion.tssrc/cli/commands/extension.tssrc/cli/commands/extension/scaffold.tssrc/cli/register-setup.tssrc/sdk/cli-contracts.tstests/fixtures/contracts/full.jsontests/unit/commands/completion-command.spec.tstests/unit/extensions/extension-command.spec.tstests/unit/extensions/extension-scaffold-define-guidance.spec.ts
… + SDK.md flag scope - Use a Windows junction (over the absolute cwd) instead of a "dir" symlink in the declarative end-to-end test so it does not need admin / Developer Mode on Windows; Node ignores the type argument on POSIX, so Linux/macOS are unchanged. (Gemini + CodeRabbit, tests/unit/extensions/extension-command.spec.ts) - Clarify in SDK.md that `--declarative` is a `pm package init`/`pm package scaffold` flag (package-mode, commands capability), not a general `--capability` toggle. (CodeRabbit, docs/SDK.md) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEUoAbSunPoG3Xt83VYd4k
|
Thanks for the reviews! Addressed in 21f98dd:
Re-requesting review: |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== PR Diff (latest) ==="
gh pr diff 358 --repo unbraind/pm-cli 2>/dev/null | head -5000 || true
echo ""
echo "=== Commit 21f98dd7 details ==="
gh api repos/unbraind/pm-cli/commits/21f98dd7 2>/dev/null | jq '{sha, commit: {message: .commit.message}, files: [.files[] | {filename, additions, deletions, patch}]}' | head -300 || trueLength of output: 70952
Review Update — post-21f98dd7✅ Addressed
🔴 Still open: missing blank line in
|
There was a problem hiding this comment.
Code Review
This pull request introduces the --declarative flag to pm package init and pm package scaffold, allowing developers to scaffold a declarative composeExtension blueprint starter package. This starter generates an index.ts using defineExtensionBlueprint and composeExtension, an index.test.ts utilizing assertExtensionPreflight and createExtensionTestHarness, and an updated README.md. The flag is restricted to package-mode and the commands capability, with appropriate validation and usage errors implemented. The changes also include updates to shell completions, CLI contracts, documentation, and comprehensive unit tests. There are no review comments, so no feedback is provided.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Summary
pm package initscaffolded only the imperativedefineExtension({ activate })authoring style across all 9--capabilityvariants. The SDK's flagship declarative loop (defineExtensionBlueprint→composeExtension) and the author-time preflight capstone (assertExtensionPreflight) — whichdocs/SDK.mdcalls "the capstone of the author → register → test loop" — were demonstrated by no generated starter. A new author runningpm package initnever discovered the easiest, best-practice path.This was found by dogfooding the full scaffold matrix in a temp workspace: all 9 imperative starters typecheck cleanly and pass their generated
node:testsuites, but none exercisescomposeExtension/assertExtensionPreflight/createExtensionTestHarness.What this adds —
pm package init <path> --declarativeindex.ts:defineCommand→defineExtensionBlueprint({ commands, deactivate })→export default composeExtension(blueprint)(exportspingCommand/blueprint/default module).index.test.ts: author-timeassertExtensionPreflight(blueprint, { identity, target })(asserts the derived + synthesizedcapabilities) and the ergonomic runtimecreateExtensionTestHarness(assertCommandContract/runCommand/deactivate).README.md: a "Declarative Authoring" section;package.json/manifest.json/tsconfig.json/.gitignorestay byte-identical to the imperative commands starter, so only the authoring style differs.Scope & constraints (each a clear USAGE error)
composeExtensionis a runtime SDK value import, so it belongs where the SDK is a linked dependency; the import-free extension-only starters must load without it.pm extension init --declarative→ error.composeExtensionexample.--declarative --capability search→ error. Full capability-matrix generalization is tracked in pm-lfdv.--declarativeis scaffold-only — using it outside--init/--scaffold→ error.Wiring
register-setup.ts(commander option on the lifecycle +initsub-command, options reader),extension.ts(option type, validation, passthrough,stylein the result),completion.ts(bash/zsh/fish),cli-contracts.ts(EXTENSION_FLAG_CONTRACTS+EXTENSION_INIT_FLAG_CONTRACTS) + regeneratedtests/fixtures/contracts/full.json, anddocs/SDK.md.Verification
typecheck(src + packages + examples),quality:static(100% docstrings),quality:docs-skills, and contracts snapshot gates all green.tsc --noEmitclean →node --test2/2 pass →pm install --project→pm <cmd> pingdispatches the composed module (SDK linked). All three error paths return exit code 2 with actionable messages.pm items
--declarativeto the full capability matrix.🤖 Generated with Claude Code