Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .agents/pm/features/pm-8mxg.toon
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
id: pm-8mxg
title: "pm package init --declarative: scaffold a composeExtension blueprint starter + author-time preflight test"
description: "Add a `--declarative` flag to `pm package init` that scaffolds the SDK's flagship declarative authoring loop. Today every scaffolded starter (all 9 --capability variants) authors via imperative `defineExtension({ activate })` and tests via the runtime helpers (activateExtensionForTest/assertRegistered*/runRegistered*/deactivateExtensionForTest). NONE demonstrates `composeExtension`/`defineExtensionBlueprint` (the declarative loop SDK.md calls 'the capstone of the author -> register -> test loop') or the author-time preflight capstone (`assertExtensionPreflight`/`preflightExtension`) or the ergonomic runtime capstone (`createExtensionTestHarness`). A new author who runs `pm package init` never discovers the easiest, best-practice authoring path. The `--declarative` starter generates index.ts as `defineExtensionBlueprint` + `composeExtension(blueprint)` and index.test.ts as `assertExtensionPreflight(blueprint, { identity, target })` (author-time) + `createExtensionTestHarness(module)` (runtime) + teardown. Scoped to package-mode (composeExtension is a runtime SDK value import; bundled extension-only installs stay import-free per SDK.md) and the commands capability (the canonical composeExtension example); other capabilities and extension-mode error clearly. Discovered by dogfooding the full scaffold matrix in a temp workspace (all 9 imperative starters typecheck + pass their generated tests). Goal: project management = context management — make creating new packages as easy and customizable as possible."
type: Feature
status: closed
priority: 2
tags[5]: "area:extensions","area:packages","area:sdk",dx,pm-cli
created_at: "2026-06-24T18:26:57.582Z"
updated_at: "2026-06-24T18:46:03.585Z"
author: claude-code-agent
goal: project management = context management
value: "Surfaces the declarative composeExtension authoring loop + author-time preflight capstone in the scaffold so package authors discover and adopt the easiest, best-practice path on first `pm package init`."
parent: pm-ugqx
resolution: "Added `pm package init --declarative` — a package-mode, commands-capability blueprint starter that scaffolds `defineExtensionBlueprint` → `composeExtension(blueprint)` (index.ts) plus an index.test.ts exercising the author-time `assertExtensionPreflight` capstone and the runtime `createExtensionTestHarness`. Wired the flag through register-setup/extension/completion/cli-contracts; documented in SDK.md."
expected_result: "`pm package init <path> --declarative` generates a typecheck-clean, test-passing composeExtension starter; extension-mode, non-commands `--capability`, and non-init use each return a clear USAGE error; 100% coverage and all quality gates stay green."
actual_result: "Delivered as specified: scaffold generates the declarative starter (style=declarative), all three error paths return exit 2, 5049 tests pass at 100/100/100/100 coverage, typecheck/static-quality/docs-skills/contracts gates green, and the composed module installs + dispatches end-to-end in a dogfooded temp workspace."
dependencies[6]{id,kind,created_at,author,source_kind}:
pm-iqq0,related,"2026-06-24T18:27:35.960Z",null,null
pm-jcyn,related,"2026-06-24T18:27:35.960Z",null,null
pm-lfdv,related,"2026-06-24T18:27:35.960Z",null,null
pm-nhby,related,"2026-06-24T18:27:35.960Z",null,null
pm-nvgy,related,"2026-06-24T18:27:35.960Z",null,null
pm-ozaf,related,"2026-06-24T18:27:35.960Z",null,null
learnings[1]{created_at,author,text}:
"2026-06-24T18:45:22.620Z",claude-code-agent,"A declarative scaffolded package's index.ts has a runtime VALUE import of composeExtension from @unbrained/pm-cli/sdk (unlike the imperative starter, whose only SDK import is the erased `ExtensionApi` type). So an end-to-end spawnSync dispatch test must link the host SDK into the temp workspace (node_modules/@unbrained/pm-cli -> repo root) or the composed module fails to load — this is exactly why --declarative is package-mode-only (the SDK is a linked dependency) and why the import-free extension-only starters must NOT use composeExtension. The imperative spawn test needs no such link because its type-only import is stripped on load."
files[6]{path,scope,note}:
docs/SDK.md,project,null
src/cli/commands/completion.ts,project,null
src/cli/commands/extension.ts,project,null
src/cli/commands/extension/scaffold.ts,project,null
src/cli/register-setup.ts,project,null
src/sdk/cli-contracts.ts,project,null
tests[2]{command,path,scope,timeout_seconds,pm_context_mode,env_set,env_clear,shared_host_safe,assert_stdout_contains,assert_stdout_regex,assert_stderr_contains,assert_stderr_regex,assert_stdout_min_lines,assert_json_field_equals,assert_json_field_gte,note}:
tests/unit/extensions/extension-command.spec.ts,null,project,null,null,null,null,null,null,null,null,null,null,null,null,null
tests/unit/extensions/extension-scaffold-define-guidance.spec.ts,null,project,null,null,null,null,null,null,null,null,null,null,null,null,null
close_reason: "Shipped `pm package init --declarative`: scaffolds a composeExtension blueprint starter (defineExtensionBlueprint → composeExtension) with an author-time assertExtensionPreflight + createExtensionTestHarness test. Package-mode + commands only (composeExtension is a runtime SDK value import), with clear USAGE errors for extension-mode, non-commands capabilities, and non-init use. Wired through register-setup/extension/completion/cli-contracts (+regenerated contracts snapshot) and documented in SDK.md. 5049 tests, 100/100/100/100 coverage, all quality gates green; manually dogfooded end-to-end (scaffold → typecheck → node --test → install → dispatch). Full-capability-matrix generalization tracked in pm-lfdv."
body: "## Implementation (2026-06-24)\n\n`pm package init <path> --declarative` scaffolds the SDK's declarative authoring loop.\n\n**Generated files (declarative, package-mode + commands):**\n- `index.ts`: `defineCommand` → `defineExtensionBlueprint({ commands, deactivate })` → `export default composeExtension(blueprint)` (exports `pingCommand`/`blueprint`/default module).\n- `index.test.ts`: author-time `assertExtensionPreflight(blueprint, { identity, target })` (asserts derived + synthesized `capabilities: [\"commands\"]`) + runtime `createExtensionTestHarness(extension)` (`assertCommandContract`/`runCommand`/`deactivate`).\n- `README.md`: \"## Declarative Authoring\" section; package.json/manifest.json/tsconfig.json/.gitignore byte-identical to the imperative commands starter.\n\n**Constraints (enforced in `scaffoldExtensionProject`, USAGE exit):**\n- package-mode only — `composeExtension` is a runtime SDK value import (extension-only installs stay import-free per SDK.md).\n- commands capability only (canonical example); follow-up [[pm-lfdv]] generalizes to all 9 capabilities.\n- `--declarative` only valid with `--init/--scaffold`.\n\n**Wiring:** `register-setup.ts` (commander option on both lifecycle + init sub-command, options reader), `extension.ts` (option type, validation, passthrough, `style` in result), `completion.ts` (bash/zsh/fish), `cli-contracts.ts` (`EXTENSION_FLAG_CONTRACTS` + `EXTENSION_INIT_FLAG_CONTRACTS`) + regenerated `tests/fixtures/contracts/full.json`.\n\n**Verification:** 5049 tests, 100/100/100/100 coverage; typecheck (src+packages+examples), static-quality (100% docstrings), docs-skills gates green. Manual dogfood in a temp workspace: scaffold → `tsc --noEmit` clean → `node --test` 2/2 pass → `pm install --project` → `pm <cmd> ping` dispatches the composed module (SDK linked). All three error paths return exit 2 with clear messages."
8 changes: 8 additions & 0 deletions .agents/pm/history/pm-8mxg.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{"ts":"2026-06-24T18:26:57.582Z","author":"claude-code-agent","op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-8mxg"},{"op":"add","path":"/metadata/title","value":"pm package init --declarative: scaffold a composeExtension blueprint starter + author-time preflight test"},{"op":"add","path":"/metadata/description","value":"Add a `--declarative` flag to `pm package init` that scaffolds the SDK's flagship declarative authoring loop. Today every scaffolded starter (all 9 --capability variants) authors via imperative `defineExtension({ activate })` and tests via the runtime helpers (activateExtensionForTest/assertRegistered*/runRegistered*/deactivateExtensionForTest). NONE demonstrates `composeExtension`/`defineExtensionBlueprint` (the declarative loop SDK.md calls 'the capstone of the author -> register -> test loop') or the author-time preflight capstone (`assertExtensionPreflight`/`preflightExtension`) or the ergonomic runtime capstone (`createExtensionTestHarness`). A new author who runs `pm package init` never discovers the easiest, best-practice authoring path. The `--declarative` starter generates index.ts as `defineExtensionBlueprint` + `composeExtension(blueprint)` and index.test.ts as `assertExtensionPreflight(blueprint, { identity, target })` (author-time) + `createExtensionTestHarness(module)` (runtime) + teardown. Scoped to package-mode (composeExtension is a runtime SDK value import; bundled extension-only installs stay import-free per SDK.md) and the commands capability (the canonical composeExtension example); other capabilities and extension-mode error clearly. Discovered by dogfooding the full scaffold matrix in a temp workspace (all 9 imperative starters typecheck + pass their generated tests). Goal: project management = context management — make creating new packages as easy and customizable as possible."},{"op":"add","path":"/metadata/type","value":"Feature"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":["area:extensions","area:packages","area:sdk","dx","pm-cli"]},{"op":"add","path":"/metadata/created_at","value":"2026-06-24T18:26:57.582Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-06-24T18:26:57.582Z"},{"op":"add","path":"/metadata/author","value":"claude-code-agent"},{"op":"add","path":"/metadata/goal","value":"project management = context management"},{"op":"add","path":"/metadata/value","value":"Surfaces the declarative composeExtension authoring loop + author-time preflight capstone in the scaffold so package authors discover and adopt the easiest, best-practice path on first `pm package init`."},{"op":"add","path":"/metadata/parent","value":"pm-ugqx"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"302a0aeafa49aefceb7fd6cdb1c84bfdc7cabc7bcd0f2db7dd4a8bc48dd1b532","message":""}
{"ts":"2026-06-24T18:27:35.968Z","author":"claude-code-agent","op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-06-24T18:27:35.968Z"},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-iqq0","kind":"related","created_at":"2026-06-24T18:27:35.960Z"},{"id":"pm-jcyn","kind":"related","created_at":"2026-06-24T18:27:35.960Z"},{"id":"pm-lfdv","kind":"related","created_at":"2026-06-24T18:27:35.960Z"},{"id":"pm-nhby","kind":"related","created_at":"2026-06-24T18:27:35.960Z"},{"id":"pm-nvgy","kind":"related","created_at":"2026-06-24T18:27:35.960Z"},{"id":"pm-ozaf","kind":"related","created_at":"2026-06-24T18:27:35.960Z"}]}],"before_hash":"302a0aeafa49aefceb7fd6cdb1c84bfdc7cabc7bcd0f2db7dd4a8bc48dd1b532","after_hash":"caa27435f3a7859ce7b5fc30c37ecb1e78409dd545d491ee871287a557c7eb47"}
{"ts":"2026-06-24T18:45:18.168Z","author":"claude-code-agent","op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-06-24T18:45:18.168Z"},{"op":"add","path":"/metadata/files","value":[{"path":"docs/SDK.md","scope":"project"},{"path":"src/cli/commands/completion.ts","scope":"project"},{"path":"src/cli/commands/extension.ts","scope":"project"},{"path":"src/cli/commands/extension/scaffold.ts","scope":"project"},{"path":"src/cli/register-setup.ts","scope":"project"},{"path":"src/sdk/cli-contracts.ts","scope":"project"}]}],"before_hash":"caa27435f3a7859ce7b5fc30c37ecb1e78409dd545d491ee871287a557c7eb47","after_hash":"9ca6c00cdc56aadf517fce52ccca10b1975c63ef39a0bb37cda66ef2bb8892d0"}
{"ts":"2026-06-24T18:45:19.578Z","author":"claude-code-agent","op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-06-24T18:45:19.578Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"tests/unit/extensions/extension-command.spec.ts","scope":"project"},{"command":"tests/unit/extensions/extension-scaffold-define-guidance.spec.ts","scope":"project"}]}],"before_hash":"9ca6c00cdc56aadf517fce52ccca10b1975c63ef39a0bb37cda66ef2bb8892d0","after_hash":"bed2996e2a3637cdf43002bd64bc088f72f3b47f8b0df7503f145e1401062bc3"}
{"ts":"2026-06-24T18:45:22.620Z","author":"claude-code-agent","op":"learning_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-06-24T18:45:22.620Z"},{"op":"add","path":"/metadata/learnings","value":[{"created_at":"2026-06-24T18:45:22.620Z","author":"claude-code-agent","text":"A declarative scaffolded package's index.ts has a runtime VALUE import of composeExtension from @unbrained/pm-cli/sdk (unlike the imperative starter, whose only SDK import is the erased `ExtensionApi` type). So an end-to-end spawnSync dispatch test must link the host SDK into the temp workspace (node_modules/@unbrained/pm-cli -> repo root) or the composed module fails to load — this is exactly why --declarative is package-mode-only (the SDK is a linked dependency) and why the import-free extension-only starters must NOT use composeExtension. The imperative spawn test needs no such link because its type-only import is stripped on load."}]}],"before_hash":"bed2996e2a3637cdf43002bd64bc088f72f3b47f8b0df7503f145e1401062bc3","after_hash":"d70f9a5f7c49f2aceff9a1c84109e626435d9e67a0e891f18b79f4f86ec6ce2b"}
{"ts":"2026-06-24T18:45:37.579Z","author":"claude-code-agent","op":"append","patch":[{"op":"replace","path":"/body","value":"## Implementation (2026-06-24)\n\n`pm package init <path> --declarative` scaffolds the SDK's declarative authoring loop.\n\n**Generated files (declarative, package-mode + commands):**\n- `index.ts`: `defineCommand` → `defineExtensionBlueprint({ commands, deactivate })` → `export default composeExtension(blueprint)` (exports `pingCommand`/`blueprint`/default module).\n- `index.test.ts`: author-time `assertExtensionPreflight(blueprint, { identity, target })` (asserts derived + synthesized `capabilities: [\"commands\"]`) + runtime `createExtensionTestHarness(extension)` (`assertCommandContract`/`runCommand`/`deactivate`).\n- `README.md`: \"## Declarative Authoring\" section; package.json/manifest.json/tsconfig.json/.gitignore byte-identical to the imperative commands starter.\n\n**Constraints (enforced in `scaffoldExtensionProject`, USAGE exit):**\n- package-mode only — `composeExtension` is a runtime SDK value import (extension-only installs stay import-free per SDK.md).\n- commands capability only (canonical example); follow-up [[pm-lfdv]] generalizes to all 9 capabilities.\n- `--declarative` only valid with `--init/--scaffold`.\n\n**Wiring:** `register-setup.ts` (commander option on both lifecycle + init sub-command, options reader), `extension.ts` (option type, validation, passthrough, `style` in result), `completion.ts` (bash/zsh/fish), `cli-contracts.ts` (`EXTENSION_FLAG_CONTRACTS` + `EXTENSION_INIT_FLAG_CONTRACTS`) + regenerated `tests/fixtures/contracts/full.json`.\n\n**Verification:** 5049 tests, 100/100/100/100 coverage; typecheck (src+packages+examples), static-quality (100% docstrings), docs-skills gates green. Manual dogfood in a temp workspace: scaffold → `tsc --noEmit` clean → `node --test` 2/2 pass → `pm install --project` → `pm <cmd> ping` dispatches the composed module (SDK linked). All three error paths return exit 2 with clear messages."},{"op":"replace","path":"/metadata/updated_at","value":"2026-06-24T18:45:37.579Z"}],"before_hash":"d70f9a5f7c49f2aceff9a1c84109e626435d9e67a0e891f18b79f4f86ec6ce2b","after_hash":"4d40e68044a44568ff6e2809a28a78cee78b35aa6d220adc4bbe139c42201aaf"}
{"ts":"2026-06-24T18:45:52.691Z","author":"claude-code-agent","op":"close","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-06-24T18:45:52.691Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/close_reason","value":"Shipped `pm package init --declarative`: scaffolds a composeExtension blueprint starter (defineExtensionBlueprint → composeExtension) with an author-time assertExtensionPreflight + createExtensionTestHarness test. Package-mode + commands only (composeExtension is a runtime SDK value import), with clear USAGE errors for extension-mode, non-commands capabilities, and non-init use. Wired through register-setup/extension/completion/cli-contracts (+regenerated contracts snapshot) and documented in SDK.md. 5049 tests, 100/100/100/100 coverage, all quality gates green; manually dogfooded end-to-end (scaffold → typecheck → node --test → install → dispatch). Full-capability-matrix generalization tracked in pm-lfdv."}],"before_hash":"4d40e68044a44568ff6e2809a28a78cee78b35aa6d220adc4bbe139c42201aaf","after_hash":"75c7313a280af5f864a7404396613e720b3f9c9f9eb9ce6dfec2dc3df71808d5"}
{"ts":"2026-06-24T18:46:03.585Z","author":"claude-code-agent","op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-06-24T18:46:03.585Z"},{"op":"add","path":"/metadata/resolution","value":"Added `pm package init --declarative` — a package-mode, commands-capability blueprint starter that scaffolds `defineExtensionBlueprint` → `composeExtension(blueprint)` (index.ts) plus an index.test.ts exercising the author-time `assertExtensionPreflight` capstone and the runtime `createExtensionTestHarness`. Wired the flag through register-setup/extension/completion/cli-contracts; documented in SDK.md."},{"op":"add","path":"/metadata/expected_result","value":"`pm package init <path> --declarative` generates a typecheck-clean, test-passing composeExtension starter; extension-mode, non-commands `--capability`, and non-init use each return a clear USAGE error; 100% coverage and all quality gates stay green."},{"op":"add","path":"/metadata/actual_result","value":"Delivered as specified: scaffold generates the declarative starter (style=declarative), all three error paths return exit 2, 5049 tests pass at 100/100/100/100 coverage, typecheck/static-quality/docs-skills/contracts gates green, and the composed module installs + dispatches end-to-end in a dogfooded temp workspace."}],"before_hash":"75c7313a280af5f864a7404396613e720b3f9c9f9eb9ce6dfec2dc3df71808d5","after_hash":"52d10f7e7fa9c641a96c6d825c4755d1b221c7120c2769d31a2c77c3a72388b5"}
Loading
Loading