@unbrained/pm-cli 2026.3.12 → 2026.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/.agents/pm/extensions/.managed-extensions.json +42 -0
  2. package/.agents/pm/extensions/beads/index.js +109 -0
  3. package/.agents/pm/extensions/beads/manifest.json +7 -0
  4. package/{dist/cli/commands/beads.js → .agents/pm/extensions/beads/runtime.js} +31 -21
  5. package/.agents/pm/extensions/beads/runtime.ts +702 -0
  6. package/.agents/pm/extensions/todos/index.js +126 -0
  7. package/.agents/pm/extensions/todos/manifest.json +7 -0
  8. package/{dist/extensions/builtins/todos/import-export.js → .agents/pm/extensions/todos/runtime.js} +39 -29
  9. package/.agents/pm/extensions/todos/runtime.ts +568 -0
  10. package/AGENTS.md +196 -92
  11. package/CHANGELOG.md +399 -0
  12. package/CODE_OF_CONDUCT.md +42 -0
  13. package/CONTRIBUTING.md +144 -0
  14. package/PRD.md +512 -164
  15. package/README.md +1053 -2
  16. package/SECURITY.md +51 -0
  17. package/dist/cli/commands/activity.d.ts +5 -0
  18. package/dist/cli/commands/activity.js +66 -3
  19. package/dist/cli/commands/activity.js.map +1 -1
  20. package/dist/cli/commands/aggregate.d.ts +54 -0
  21. package/dist/cli/commands/aggregate.js +181 -0
  22. package/dist/cli/commands/aggregate.js.map +1 -0
  23. package/dist/cli/commands/append.js +4 -1
  24. package/dist/cli/commands/append.js.map +1 -1
  25. package/dist/cli/commands/calendar.d.ts +109 -0
  26. package/dist/cli/commands/calendar.js +797 -0
  27. package/dist/cli/commands/calendar.js.map +1 -0
  28. package/dist/cli/commands/claim.d.ts +5 -1
  29. package/dist/cli/commands/claim.js +42 -21
  30. package/dist/cli/commands/claim.js.map +1 -1
  31. package/dist/cli/commands/close.d.ts +1 -0
  32. package/dist/cli/commands/close.js +54 -5
  33. package/dist/cli/commands/close.js.map +1 -1
  34. package/dist/cli/commands/comments-audit.d.ts +91 -0
  35. package/dist/cli/commands/comments-audit.js +195 -0
  36. package/dist/cli/commands/comments-audit.js.map +1 -0
  37. package/dist/cli/commands/comments.d.ts +1 -0
  38. package/dist/cli/commands/comments.js +70 -21
  39. package/dist/cli/commands/comments.js.map +1 -1
  40. package/dist/cli/commands/completion.d.ts +10 -4
  41. package/dist/cli/commands/completion.js +1184 -137
  42. package/dist/cli/commands/completion.js.map +1 -1
  43. package/dist/cli/commands/config.d.ts +35 -3
  44. package/dist/cli/commands/config.js +968 -13
  45. package/dist/cli/commands/config.js.map +1 -1
  46. package/dist/cli/commands/context.d.ts +86 -0
  47. package/dist/cli/commands/context.js +299 -0
  48. package/dist/cli/commands/context.js.map +1 -0
  49. package/dist/cli/commands/contracts.d.ts +78 -0
  50. package/dist/cli/commands/contracts.js +920 -0
  51. package/dist/cli/commands/contracts.js.map +1 -0
  52. package/dist/cli/commands/create.d.ts +48 -14
  53. package/dist/cli/commands/create.js +1331 -160
  54. package/dist/cli/commands/create.js.map +1 -1
  55. package/dist/cli/commands/dedupe-audit.d.ts +81 -0
  56. package/dist/cli/commands/dedupe-audit.js +330 -0
  57. package/dist/cli/commands/dedupe-audit.js.map +1 -0
  58. package/dist/cli/commands/deps.d.ts +52 -0
  59. package/dist/cli/commands/deps.js +204 -0
  60. package/dist/cli/commands/deps.js.map +1 -0
  61. package/dist/cli/commands/docs.d.ts +19 -0
  62. package/dist/cli/commands/docs.js +212 -13
  63. package/dist/cli/commands/docs.js.map +1 -1
  64. package/dist/cli/commands/extension.d.ts +122 -0
  65. package/dist/cli/commands/extension.js +1850 -0
  66. package/dist/cli/commands/extension.js.map +1 -0
  67. package/dist/cli/commands/files.d.ts +52 -1
  68. package/dist/cli/commands/files.js +443 -13
  69. package/dist/cli/commands/files.js.map +1 -1
  70. package/dist/cli/commands/gc.d.ts +11 -1
  71. package/dist/cli/commands/gc.js +89 -11
  72. package/dist/cli/commands/gc.js.map +1 -1
  73. package/dist/cli/commands/get.d.ts +13 -0
  74. package/dist/cli/commands/get.js +35 -3
  75. package/dist/cli/commands/get.js.map +1 -1
  76. package/dist/cli/commands/health.d.ts +10 -2
  77. package/dist/cli/commands/health.js +774 -23
  78. package/dist/cli/commands/health.js.map +1 -1
  79. package/dist/cli/commands/history.d.ts +20 -0
  80. package/dist/cli/commands/history.js +152 -6
  81. package/dist/cli/commands/history.js.map +1 -1
  82. package/dist/cli/commands/index.d.ts +16 -3
  83. package/dist/cli/commands/index.js +16 -3
  84. package/dist/cli/commands/index.js.map +1 -1
  85. package/dist/cli/commands/init.d.ts +7 -2
  86. package/dist/cli/commands/init.js +137 -5
  87. package/dist/cli/commands/init.js.map +1 -1
  88. package/dist/cli/commands/learnings.d.ts +17 -0
  89. package/dist/cli/commands/learnings.js +129 -0
  90. package/dist/cli/commands/learnings.js.map +1 -0
  91. package/dist/cli/commands/list.d.ts +29 -1
  92. package/dist/cli/commands/list.js +289 -53
  93. package/dist/cli/commands/list.js.map +1 -1
  94. package/dist/cli/commands/normalize.d.ts +51 -0
  95. package/dist/cli/commands/normalize.js +298 -0
  96. package/dist/cli/commands/normalize.js.map +1 -0
  97. package/dist/cli/commands/notes.d.ts +17 -0
  98. package/dist/cli/commands/notes.js +129 -0
  99. package/dist/cli/commands/notes.js.map +1 -0
  100. package/dist/cli/commands/reindex.d.ts +1 -0
  101. package/dist/cli/commands/reindex.js +208 -32
  102. package/dist/cli/commands/reindex.js.map +1 -1
  103. package/dist/cli/commands/restore.js +164 -30
  104. package/dist/cli/commands/restore.js.map +1 -1
  105. package/dist/cli/commands/search.d.ts +14 -1
  106. package/dist/cli/commands/search.js +475 -81
  107. package/dist/cli/commands/search.js.map +1 -1
  108. package/dist/cli/commands/stats.js +26 -10
  109. package/dist/cli/commands/stats.js.map +1 -1
  110. package/dist/cli/commands/templates.d.ts +26 -0
  111. package/dist/cli/commands/templates.js +179 -0
  112. package/dist/cli/commands/templates.js.map +1 -0
  113. package/dist/cli/commands/test-all.d.ts +19 -1
  114. package/dist/cli/commands/test-all.js +161 -13
  115. package/dist/cli/commands/test-all.js.map +1 -1
  116. package/dist/cli/commands/test-runs.d.ts +63 -0
  117. package/dist/cli/commands/test-runs.js +179 -0
  118. package/dist/cli/commands/test-runs.js.map +1 -0
  119. package/dist/cli/commands/test.d.ts +75 -1
  120. package/dist/cli/commands/test.js +1360 -41
  121. package/dist/cli/commands/test.js.map +1 -1
  122. package/dist/cli/commands/update-many.d.ts +57 -0
  123. package/dist/cli/commands/update-many.js +631 -0
  124. package/dist/cli/commands/update-many.js.map +1 -0
  125. package/dist/cli/commands/update.d.ts +30 -0
  126. package/dist/cli/commands/update.js +1393 -84
  127. package/dist/cli/commands/update.js.map +1 -1
  128. package/dist/cli/commands/validate.d.ts +30 -0
  129. package/dist/cli/commands/validate.js +1140 -0
  130. package/dist/cli/commands/validate.js.map +1 -0
  131. package/dist/cli/error-guidance.d.ts +33 -0
  132. package/dist/cli/error-guidance.js +337 -0
  133. package/dist/cli/error-guidance.js.map +1 -0
  134. package/dist/cli/extension-command-options.d.ts +1 -0
  135. package/dist/cli/extension-command-options.js +92 -0
  136. package/dist/cli/extension-command-options.js.map +1 -1
  137. package/dist/cli/help-content.d.ts +20 -0
  138. package/dist/cli/help-content.js +543 -0
  139. package/dist/cli/help-content.js.map +1 -0
  140. package/dist/cli/main.js +3625 -445
  141. package/dist/cli/main.js.map +1 -1
  142. package/dist/core/extensions/index.d.ts +13 -1
  143. package/dist/core/extensions/index.js +108 -1
  144. package/dist/core/extensions/index.js.map +1 -1
  145. package/dist/core/extensions/item-fields.d.ts +2 -0
  146. package/dist/core/extensions/item-fields.js +79 -0
  147. package/dist/core/extensions/item-fields.js.map +1 -0
  148. package/dist/core/extensions/loader.d.ts +322 -9
  149. package/dist/core/extensions/loader.js +911 -20
  150. package/dist/core/extensions/loader.js.map +1 -1
  151. package/dist/core/extensions/runtime-registrations.d.ts +5 -0
  152. package/dist/core/extensions/runtime-registrations.js +51 -0
  153. package/dist/core/extensions/runtime-registrations.js.map +1 -0
  154. package/dist/core/history/history-stream-policy.d.ts +20 -0
  155. package/dist/core/history/history-stream-policy.js +53 -0
  156. package/dist/core/history/history-stream-policy.js.map +1 -0
  157. package/dist/core/history/history.js +90 -1
  158. package/dist/core/history/history.js.map +1 -1
  159. package/dist/core/item/id.js +4 -1
  160. package/dist/core/item/id.js.map +1 -1
  161. package/dist/core/item/index.d.ts +1 -0
  162. package/dist/core/item/index.js +1 -0
  163. package/dist/core/item/index.js.map +1 -1
  164. package/dist/core/item/item-format.d.ts +11 -5
  165. package/dist/core/item/item-format.js +507 -24
  166. package/dist/core/item/item-format.js.map +1 -1
  167. package/dist/core/item/parent-reference-policy.d.ts +6 -0
  168. package/dist/core/item/parent-reference-policy.js +32 -0
  169. package/dist/core/item/parent-reference-policy.js.map +1 -0
  170. package/dist/core/item/parse.d.ts +5 -0
  171. package/dist/core/item/parse.js +216 -19
  172. package/dist/core/item/parse.js.map +1 -1
  173. package/dist/core/item/sprint-release-format.d.ts +6 -0
  174. package/dist/core/item/sprint-release-format.js +33 -0
  175. package/dist/core/item/sprint-release-format.js.map +1 -0
  176. package/dist/core/item/status.d.ts +3 -0
  177. package/dist/core/item/status.js +24 -0
  178. package/dist/core/item/status.js.map +1 -0
  179. package/dist/core/item/type-registry.d.ts +37 -0
  180. package/dist/core/item/type-registry.js +706 -0
  181. package/dist/core/item/type-registry.js.map +1 -0
  182. package/dist/core/lock/lock.d.ts +1 -1
  183. package/dist/core/lock/lock.js +101 -12
  184. package/dist/core/lock/lock.js.map +1 -1
  185. package/dist/core/output/command-aware.d.ts +1 -0
  186. package/dist/core/output/command-aware.js +394 -0
  187. package/dist/core/output/command-aware.js.map +1 -0
  188. package/dist/core/output/output.d.ts +3 -0
  189. package/dist/core/output/output.js +124 -6
  190. package/dist/core/output/output.js.map +1 -1
  191. package/dist/core/schema/runtime-field-filters.d.ts +3 -0
  192. package/dist/core/schema/runtime-field-filters.js +39 -0
  193. package/dist/core/schema/runtime-field-filters.js.map +1 -0
  194. package/dist/core/schema/runtime-field-values.d.ts +8 -0
  195. package/dist/core/schema/runtime-field-values.js +154 -0
  196. package/dist/core/schema/runtime-field-values.js.map +1 -0
  197. package/dist/core/schema/runtime-schema.d.ts +68 -0
  198. package/dist/core/schema/runtime-schema.js +554 -0
  199. package/dist/core/schema/runtime-schema.js.map +1 -0
  200. package/dist/core/search/cache.d.ts +13 -1
  201. package/dist/core/search/cache.js +123 -14
  202. package/dist/core/search/cache.js.map +1 -1
  203. package/dist/core/search/semantic-defaults.d.ts +6 -0
  204. package/dist/core/search/semantic-defaults.js +120 -0
  205. package/dist/core/search/semantic-defaults.js.map +1 -0
  206. package/dist/core/search/vector-stores.js +3 -1
  207. package/dist/core/search/vector-stores.js.map +1 -1
  208. package/dist/core/shared/command-types.d.ts +2 -0
  209. package/dist/core/shared/conflict-markers.d.ts +7 -0
  210. package/dist/core/shared/conflict-markers.js +27 -0
  211. package/dist/core/shared/conflict-markers.js.map +1 -0
  212. package/dist/core/shared/constants.d.ts +15 -4
  213. package/dist/core/shared/constants.js +141 -1
  214. package/dist/core/shared/constants.js.map +1 -1
  215. package/dist/core/shared/errors.d.ts +10 -1
  216. package/dist/core/shared/errors.js +3 -1
  217. package/dist/core/shared/errors.js.map +1 -1
  218. package/dist/core/shared/text-normalization.d.ts +4 -0
  219. package/dist/core/shared/text-normalization.js +33 -0
  220. package/dist/core/shared/text-normalization.js.map +1 -0
  221. package/dist/core/shared/time.d.ts +1 -2
  222. package/dist/core/shared/time.js +98 -11
  223. package/dist/core/shared/time.js.map +1 -1
  224. package/dist/core/store/index.d.ts +1 -0
  225. package/dist/core/store/index.js +1 -0
  226. package/dist/core/store/index.js.map +1 -1
  227. package/dist/core/store/item-format-migration.d.ts +9 -0
  228. package/dist/core/store/item-format-migration.js +87 -0
  229. package/dist/core/store/item-format-migration.js.map +1 -0
  230. package/dist/core/store/item-store.d.ts +13 -4
  231. package/dist/core/store/item-store.js +238 -51
  232. package/dist/core/store/item-store.js.map +1 -1
  233. package/dist/core/store/paths.d.ts +21 -3
  234. package/dist/core/store/paths.js +59 -4
  235. package/dist/core/store/paths.js.map +1 -1
  236. package/dist/core/store/settings.d.ts +14 -1
  237. package/dist/core/store/settings.js +463 -7
  238. package/dist/core/store/settings.js.map +1 -1
  239. package/dist/core/telemetry/consent.d.ts +2 -0
  240. package/dist/core/telemetry/consent.js +79 -0
  241. package/dist/core/telemetry/consent.js.map +1 -0
  242. package/dist/core/telemetry/runtime.d.ts +38 -0
  243. package/dist/core/telemetry/runtime.js +733 -0
  244. package/dist/core/telemetry/runtime.js.map +1 -0
  245. package/dist/core/test/background-runs.d.ts +117 -0
  246. package/dist/core/test/background-runs.js +760 -0
  247. package/dist/core/test/background-runs.js.map +1 -0
  248. package/dist/core/test/item-test-run-tracking.d.ts +9 -0
  249. package/dist/core/test/item-test-run-tracking.js +50 -0
  250. package/dist/core/test/item-test-run-tracking.js.map +1 -0
  251. package/dist/sdk/cli-contracts.d.ts +92 -0
  252. package/dist/sdk/cli-contracts.js +2357 -0
  253. package/dist/sdk/cli-contracts.js.map +1 -0
  254. package/dist/sdk/index.d.ts +34 -0
  255. package/dist/sdk/index.js +23 -0
  256. package/dist/sdk/index.js.map +1 -0
  257. package/dist/types.d.ts +197 -3
  258. package/dist/types.js +48 -1
  259. package/dist/types.js.map +1 -1
  260. package/docs/ARCHITECTURE.md +368 -39
  261. package/docs/EXTENSIONS.md +454 -49
  262. package/docs/RELEASING.md +68 -19
  263. package/docs/SDK.md +123 -0
  264. package/docs/examples/starter-extension/README.md +48 -0
  265. package/docs/examples/starter-extension/index.js +191 -0
  266. package/docs/examples/starter-extension/manifest.json +17 -0
  267. package/docs/examples/starter-extension/package.json +10 -0
  268. package/package.json +33 -6
  269. package/.pi/extensions/pm-cli/index.ts +0 -778
  270. package/dist/cli/commands/beads.d.ts +0 -16
  271. package/dist/cli/commands/beads.js.map +0 -1
  272. package/dist/cli/commands/install.d.ts +0 -18
  273. package/dist/cli/commands/install.js +0 -87
  274. package/dist/cli/commands/install.js.map +0 -1
  275. package/dist/core/extensions/builtins.d.ts +0 -3
  276. package/dist/core/extensions/builtins.js +0 -47
  277. package/dist/core/extensions/builtins.js.map +0 -1
  278. package/dist/extensions/builtins/beads/index.d.ts +0 -8
  279. package/dist/extensions/builtins/beads/index.js +0 -33
  280. package/dist/extensions/builtins/beads/index.js.map +0 -1
  281. package/dist/extensions/builtins/todos/import-export.d.ts +0 -26
  282. package/dist/extensions/builtins/todos/import-export.js.map +0 -1
  283. package/dist/extensions/builtins/todos/index.d.ts +0 -8
  284. package/dist/extensions/builtins/todos/index.js +0 -38
  285. package/dist/extensions/builtins/todos/index.js.map +0 -1
@@ -1,6 +1,8 @@
1
1
  # pm-cli Extension Development Guide
2
2
 
3
- Extensions let you add commands, renderers, importers, exporters, schema fields, search providers, and lifecycle hooks to `pm-cli` without modifying core.
3
+ Extensions let you add commands, parser/preflight lifecycle control, core service overrides, renderers, importers, exporters, schema fields, item-type definitions, search providers, and lifecycle hooks to `pm-cli` without modifying core.
4
+
5
+ For a quick SDK-first authoring path, start with `docs/SDK.md`.
4
6
 
5
7
  ## Extension Locations
6
8
 
@@ -9,7 +11,146 @@ Extensions let you add commands, renderers, importers, exporters, schema fields,
9
11
  | Global | `~/.pm-cli/extensions/<name>/` (override: `PM_GLOBAL_PATH/extensions/<name>/`) |
10
12
  | Project | `.agents/pm/extensions/<name>/` (override: `PM_PATH/extensions/<name>/`) |
11
13
 
12
- **Load order:** core built-ins → global → project. Project-local extensions take precedence over global when they declare the same command name or renderer key.
14
+ **Load order:** global → project. Project-local extensions take precedence over global when they declare the same command name or renderer key.
15
+
16
+ ## Linked-Test Sandbox Parity
17
+
18
+ `pm test --run` and `pm test-all` execute linked commands in temporary sandbox roots (`PM_PATH`, `PM_GLOBAL_PATH`) to avoid mutating live tracker data. Before command execution, the runtime seeds sandbox project/global `settings.json` and `extensions/` directories from the source roots.
19
+
20
+ This preserves extension-defined schema behavior (including custom item type validation/filtering) while retaining sandbox isolation for linked-test execution.
21
+
22
+ Linked-test runtime controls are additive: run-level `--env-set`/`--env-clear`/`--shared-host-safe` flags and per-linked-test metadata directives (`env_set`, `env_clear`, `shared_host_safe`) apply before sandbox-protected `PM_PATH`/`PM_GLOBAL_PATH` overrides.
23
+
24
+ PM-command linked tests can opt into tracker-data parity via `--pm-context tracker` (default `schema` keeps isolated tracker data while still seeding settings/extensions). `--pm-context auto` automatically routes PM tracker-read commands through tracker-seeded context while keeping other commands in schema context. Per-linked-test metadata can override run-level mode with `pm_context_mode=schema|tracker|auto`. In default `schema` mode, PM tracker-read linked commands fail on context mismatch by default; strict guards remain available for other PM command shapes via `--fail-on-context-mismatch` plus `--fail-on-skipped` and `--require-assertions-for-pm`.
25
+
26
+ Context ergonomics flags are additive:
27
+
28
+ - `--check-context` emits deterministic preflight diagnostics/warnings (`context_preflight`) for PM command context mismatches
29
+ - `--auto-pm-context` auto-remediates tracker-read mismatches by routing those commands to tracker context
30
+
31
+ Per-run `execution_context` metadata includes resolved roots, item counts, mismatch signal, PM tracker-read classification, `requested_pm_context_mode`, and `auto_pm_context_applied`.
32
+
33
+ Linked-test assertion metadata is optional and additive (`assert_stdout_contains`, `assert_stdout_regex`, `assert_stderr_contains`, `assert_stderr_regex`, `assert_stdout_min_lines`, `assert_json_field_equals`, `assert_json_field_gte`).
34
+
35
+ ## Lifecycle Manager CLI
36
+
37
+ `pm extension` is the canonical lifecycle manager for custom extensions.
38
+
39
+ Top-level action and subcommand forms are flag-equivalent for lifecycle operations (for example `pm extension --doctor --detail deep --trace` and `pm extension doctor --detail deep --trace` resolve the same behavior).
40
+
41
+ ### Actions
42
+
43
+ Pass exactly one action flag:
44
+
45
+ - `--init` (alias: `--scaffold`)
46
+ - `--install`
47
+ - `--uninstall`
48
+ - `--explore`
49
+ - `--manage`
50
+ - `--doctor`
51
+ - `--adopt`
52
+ - `--adopt-all`
53
+ - `--activate`
54
+ - `--deactivate`
55
+
56
+ ### Scope selection
57
+
58
+ - `--project` (default)
59
+ - `--local` (alias for `--project`)
60
+ - `--global`
61
+
62
+ Project scope resolves against `.agents/pm/extensions`. Global scope resolves against `~/.pm-cli/extensions` (or `PM_GLOBAL_PATH/extensions`).
63
+
64
+ ### Install source normalization
65
+
66
+ `pm extension --install` accepts:
67
+
68
+ - local directory paths
69
+ - GitHub HTTPS URLs
70
+ - `github.com/<owner>/<repo>[/path]` shorthand
71
+ - `--gh <owner>/<repo>[/path]` (alias: `--github`)
72
+ - optional `--ref <branch|tag|sha>` for GitHub sources
73
+
74
+ When the source path is shorthand (for example `owner/repo/my-ext`), install resolution probes in this order:
75
+
76
+ 1. `<clone>/<subpath>`
77
+ 2. `<clone>/.agents/pm/extensions/<subpath>`
78
+ 3. `<clone>/.custom/pm-extensions/<subpath>`
79
+ 4. `<clone>/.custom/pm-extension/<subpath>`
80
+
81
+ If no subpath is supplied, the resolver accepts either:
82
+
83
+ - repo root containing one extension (`manifest.json` at root), or
84
+ - exactly one extension under default roots (`.agents/pm/extensions`, `.custom/pm-extensions`, `.custom/pm-extension`)
85
+
86
+ If multiple extension manifests are discovered, install fails with deterministic guidance to provide an explicit path.
87
+
88
+ ### Requested equivalence examples
89
+
90
+ ```bash
91
+ # Scaffold a starter extension project in your workspace
92
+ pm extension --init ./my-extension
93
+ pm extension scaffold ./my-extension
94
+
95
+ # Bundled aliases shipped with pm-cli (not auto-installed)
96
+ pm extension --install --project beads
97
+ pm extension --install --project todos
98
+
99
+ # Equivalent bundled local paths
100
+ pm extension --install --project .agents/pm/extensions/beads
101
+ pm extension --install --project .agents/pm/extensions/todos
102
+
103
+ # Custom roots
104
+ pm extension --install --project https://github.com/unbraind/pm-cli/tree/main/.custom/pm-extensions/my-ext
105
+ pm extension --install --project github.com/unbraind/pm-cli/.custom/pm-extensions/my-ext
106
+ pm extension --install --project --gh unbraind/pm-cli/.custom/pm-extensions/my-ext
107
+
108
+ # Single-extension repo or extension rooted at repository top-level
109
+ pm extension --install --project https://github.com/unbraind/pm-cli
110
+ pm extension --install --project github.com/unbraind/pm-cli
111
+ pm extension --install --project --gh unbraind/pm-cli
112
+ ```
113
+
114
+ Canonical public shorthand examples in this repository use `unbraind/pm-cli`. For private repositories, make sure git authentication is configured before using `--gh` or `github.com/...` selectors.
115
+
116
+ ### Managed extension state
117
+
118
+ Each scope maintains a lifecycle state file:
119
+
120
+ - `<scope-extension-root>/.managed-extensions.json`
121
+
122
+ State records include deterministic source metadata (`local` or `github`), install timestamps, manifest summary, and update-check metadata.
123
+
124
+ Lifecycle semantics:
125
+
126
+ - Init/scaffold creates an idempotent starter extension project (`manifest.json`, `index.js`, `README.md`) and fails fast on conflicting pre-existing file contents.
127
+ - Install copies/clones into the selected extension root, validates `manifest.json` and `entry`, updates managed state, and activates the extension in settings.
128
+ - Uninstall removes extension files, removes managed-state entry, and clears settings references.
129
+ - Activate/deactivate updates `settings.extensions.enabled[]` / `settings.extensions.disabled[]`.
130
+ - Explore returns discovered extensions + compatibility/runtime status fields (`active`, `enabled`, `runtime_active`, `activation_status`) and managed state.
131
+ - Manage performs GitHub update checks (`git ls-remote`) for managed GitHub entries and persists update metadata (`last_update_check_at`, `last_update_remote_commit`, `update_available`, `update_error`).
132
+ - Manage supports `--runtime-probe` to opt into doctor-equivalent runtime activation semantics for `runtime_active`/`activation_status`.
133
+ - Manage and doctor support `--fix-managed-state` to adopt unmanaged extensions before diagnostics/update checks.
134
+ - Adopt records an already-installed unmanaged extension into managed state metadata without reinstalling files (supports local source metadata or explicit GitHub provenance via `--gh`/`--github` and optional `--ref`).
135
+ - Adopt-all bulk-records all unmanaged installed extensions in selected scope into managed state metadata without reinstalling files.
136
+ - Explore/manage extension rows include explicit update-check fields:
137
+ - `update_check_status`: `checked`, `failed`, `skipped_unmanaged`, `skipped_non_github`, or `not_checked`
138
+ - `update_check_reason`: deterministic reason/code for that status (for example `up_to_date`, `update_available`, `extension_not_managed`, or the update failure text)
139
+ - Manage triage includes `update_check_status_totals`, `update_check_failed_total`, and update-health coverage signals (`update_health_coverage`, `update_health_partial`) for operator-friendly rollups.
140
+ - Manage/doctor warning-code rollups include `extension_update_health_partial_coverage` only when unmanaged extensions are action-required for update-check coverage. Expected bundled/local unmanaged installs are surfaced as informational triage metadata.
141
+ - Doctor consolidates diagnostics into summary/deep modes (`--detail summary|deep`) with normalized warning codes, canonical extension load roots, consistency diagnostics for active-vs-loaded project extensions, update-health coverage telemetry, remediation hints, strict warning exits (`--strict-exit`, alias `--fail-on-warn`), blocking-failure indicators (`blocking_failure_count`, `has_blocking_failures`), capability guidance metadata (`pm extension --doctor` or `pm extension doctor`), and capability contract metadata (`details.capability_contract`).
142
+ - Doctor `--trace` (with `--detail deep`) includes actionable activation trace payloads for registration validation failures (method, command, registration index, expected schema, and sanitized received payload).
143
+
144
+ ### Health integration
145
+
146
+ `pm health` includes managed extension diagnostics for project and global scope:
147
+
148
+ - managed-state file path
149
+ - managed entry count
150
+ - managed entry summaries
151
+ - managed-state read/schema warnings
152
+ - update-coverage parity warnings (`extension_update_health_partial_coverage`) when loaded unmanaged extensions are action-required and update checks are partial
153
+ - For focused extension triage, use `pm extension --doctor` for consolidated diagnostics without traversing full health payloads.
13
154
 
14
155
  ## Manifest
15
156
 
@@ -23,6 +164,9 @@ Every extension directory must contain a `manifest.json`:
23
164
  "priority": 100,
24
165
  "capabilities": [
25
166
  "commands",
167
+ "parser",
168
+ "preflight",
169
+ "services",
26
170
  "renderers",
27
171
  "hooks",
28
172
  "schema",
@@ -34,18 +178,43 @@ Every extension directory must contain a `manifest.json`:
34
178
 
35
179
  - `entry` must resolve inside the extension directory (no path traversal).
36
180
  - `capabilities` declares what the extension will register. API calls that exceed declared capabilities fail activation deterministically.
37
- - Unknown capability names are silently ignored for gating but emit discovery diagnostics.
181
+ - Unknown capability names are ignored for gating but emit discovery diagnostics that include allowed-capability lists and nearest-match suggestions when confidence is high.
182
+ - Legacy manifest capability aliases `migration` and `validation` are remapped to `schema` and emit consolidated warning `extension_capability_legacy_alias` so compatibility remains additive while migration intent stays visible.
183
+
184
+ ## SDK Dependency Setup
185
+
186
+ External extensions should declare an explicit dependency on the pm package so `@unbrained/pm-cli/sdk` resolves at runtime.
187
+
188
+ ```json
189
+ {
190
+ "name": "my-pm-extension",
191
+ "type": "module",
192
+ "dependencies": {
193
+ "@unbrained/pm-cli": "^2026.3.12"
194
+ }
195
+ }
196
+ ```
197
+
198
+ This keeps extension imports stable and avoids relying on internal `src/core/...` paths.
199
+
200
+ ## Starter Extension Reference
201
+
202
+ For a complete example that demonstrates all 9 capabilities via the public SDK, see:
203
+
204
+ - `docs/examples/starter-extension/`
38
205
 
39
206
  ## Extension Module
40
207
 
41
208
  The entry module must export an `activate` function:
42
209
 
43
210
  ```ts
44
- import type { ExtensionApi } from "pm-cli";
211
+ import { defineExtension, type ExtensionApi } from "@unbrained/pm-cli/sdk";
45
212
 
46
- export function activate(api: ExtensionApi): void {
47
- // register commands, hooks, renderers, etc.
48
- }
213
+ export default defineExtension({
214
+ activate(api: ExtensionApi): void {
215
+ // register commands, hooks, renderers, etc.
216
+ },
217
+ });
49
218
  ```
50
219
 
51
220
  `activate` may be synchronous or return `Promise<void>`.
@@ -54,65 +223,204 @@ export function activate(api: ExtensionApi): void {
54
223
 
55
224
  ### `api.registerCommand(def)`
56
225
 
57
- Register a new command or override an existing core command's result.
226
+ Register a new command handler path or replace an existing core command at dispatch time.
58
227
 
59
228
  **New command path:**
60
229
 
61
230
  ```ts
62
231
  api.registerCommand({
63
232
  name: "acme sync",
64
- run: async (args, options, global) => {
65
- // args: string[] positional CLI arguments
66
- // options: Record<string,unknown> parsed flags
67
- // global: GlobalOptions --json, --quiet, --path, etc.
233
+ action: "acme-sync",
234
+ description: "Synchronize ACME assets into PM items.",
235
+ intent: "Run a deterministic import/sync pass before release prep.",
236
+ examples: ["pm acme sync --source ./assets.json --dry-run"],
237
+ failure_hints: ["Ensure --source points to a readable JSON file."],
238
+ arguments: [
239
+ { name: "sourceId", required: false, description: "Optional source identifier override." },
240
+ ],
241
+ flags: [
242
+ { long: "--source", value_name: "path", description: "Input file path", required: true },
243
+ { long: "--dry-run", description: "Preview without writing items" },
244
+ ],
245
+ run: async (context) => {
246
+ // context.command: normalized command path
247
+ // context.args: string[] positional args
248
+ // context.options: Record<string, unknown> command-scoped flags
249
+ // context.global: GlobalOptions (--json/--quiet/--path/--no-extensions/--profile)
250
+ // context.pm_root: resolved PM root
68
251
  return { ok: true, synced: 42 };
69
252
  },
70
253
  });
71
254
  ```
72
255
 
73
- The command name is canonicalized (trimmed, lowercased, repeated whitespace collapsed). The handler receives cloned snapshots so mutation cannot leak into caller state.
256
+ If the command path matches a core command (for example `list-open`), the extension handler runs first and the core action is skipped. Command names are canonicalized (trimmed, lowercased, repeated whitespace collapsed). Handlers receive cloned snapshots so mutation cannot leak into caller state.
257
+
258
+ Capability requirements:
259
+
260
+ - `registerCommand(...)` requires `commands` capability.
261
+ - Defining inline command metadata `flags` inside `registerCommand({ ... })` also requires `schema` capability (same gate as `registerFlags(...)`).
262
+
263
+ Optional command metadata (`action`, `description`, `intent`, `examples`, `failure_hints`, `arguments`, and inline `flags`) is consumed by runtime help surfaces and contracts:
264
+
265
+ - `pm <extension-command> --help` / `--help --json`
266
+ - `pm contracts --command <extension-command>`
267
+ - `pm contracts --action <extension-action>`
268
+
269
+ For backward compatibility, command definitions that still use `handler` are accepted and mapped to `run`, with warning `extension_command_definition_legacy_handler_alias` to guide migration.
74
270
 
75
271
  **Override existing core command result:**
76
272
 
77
273
  ```ts
78
- api.registerCommand("list", (priorResult, args, options, global, pmRoot) => {
79
- // priorResult: the core command's output object (cloned)
80
- // return a modified result object, or undefined to use priorResult as-is
81
- return { ...priorResult, _ext: "annotated" };
274
+ api.registerCommand("list", (context) => {
275
+ return {
276
+ ...(context.result as Record<string, unknown>),
277
+ _ext: "annotated",
278
+ command: context.command,
279
+ pm_root: context.pm_root,
280
+ };
82
281
  });
83
282
  ```
84
283
 
284
+ Result override callbacks are synchronous. Returning a Promise is ignored and emits `extension_command_override_async_unsupported:<layer>:<name>:<command>`.
285
+
286
+ ### `api.registerParser(command, override)`
287
+
288
+ Register command-scoped parser overrides for core or dynamic command paths. Parser overrides run before command handler dispatch and can rewrite `args`, `options`, and `global` values.
289
+
290
+ ```ts
291
+ api.registerParser("acme sync", (context) => {
292
+ return {
293
+ options: {
294
+ ...context.options,
295
+ limit: Number(context.options.limit),
296
+ },
297
+ };
298
+ });
299
+ ```
300
+
301
+ Notes:
302
+
303
+ - Requires `parser` capability.
304
+ - Resolution is deterministic (last registered override for the command path wins).
305
+ - Parser handlers can be async.
306
+ - Failed parser overrides fall back to the original command context and emit deterministic warnings.
307
+
308
+ ### `api.registerPreflight(override)`
309
+
310
+ Register a preflight override to control command mutation gates and migration execution.
311
+
312
+ ```ts
313
+ api.registerPreflight((context) => ({
314
+ enforce_item_format_gate: false,
315
+ run_preflight_item_format_sync: false,
316
+ run_extension_migrations: false,
317
+ enforce_mandatory_migration_gate: false,
318
+ }));
319
+ ```
320
+
321
+ `context.decision` contains the default gate/migration plan:
322
+
323
+ - `enforce_item_format_gate`
324
+ - `run_preflight_item_format_sync`
325
+ - `run_extension_migrations`
326
+ - `enforce_mandatory_migration_gate`
327
+
328
+ Notes:
329
+
330
+ - Requires `preflight` capability.
331
+ - Only the latest registered preflight override is active (deterministic last-wins behavior).
332
+ - Use this API carefully: disabling gates/migrations can bypass core safety rails.
333
+
334
+ ### `api.registerService(service, override)`
335
+
336
+ Register service-level overrides for deep runtime behavior.
337
+
338
+ ```ts
339
+ api.registerService("output_format", (context) => {
340
+ return JSON.stringify({
341
+ rendered_by: "acme-service",
342
+ payload: context.payload.result,
343
+ });
344
+ });
345
+ ```
346
+
347
+ Supported service keys:
348
+
349
+ - `output_format`
350
+ - `error_format`
351
+ - `help_format`
352
+ - `lock_acquire`
353
+ - `lock_release`
354
+ - `history_append`
355
+ - `item_store_write`
356
+ - `item_store_delete`
357
+
358
+ Notes:
359
+
360
+ - Requires `services` capability.
361
+ - Service resolution is deterministic (last registration for each service key wins).
362
+ - `output_format` and `error_format` are synchronous call sites; async returns are ignored with deterministic warnings.
363
+ - `error_format` receives the final rendered error string. When callers use `--json`, that string is a JSON error envelope.
364
+ - `help_format` applies to text help/usage rendering paths; machine-readable `--json` errors and `--help --json` payloads bypass `help_format` and emit canonical JSON diagnostics/help data directly.
365
+
85
366
  ### `api.registerFlags(targetCommand, flags)`
86
367
 
87
368
  Declare flags for a command (displayed in `--help` for dynamic extension commands):
88
369
 
89
370
  ```ts
90
371
  api.registerFlags("acme sync", [
91
- { name: "--dry-run", description: "Simulate without writing" },
92
- { name: "--org <name>", description: "Organization name" },
372
+ { long: "--dry-run", short: "-d", description: "Simulate without writing" },
373
+ { long: "--org", value_name: "name", description: "Organization name", required: true },
374
+ { long: "--legacy-mode", enabled: false },
375
+ { long: "--internal-debug", visible: false },
93
376
  ]);
94
377
  ```
95
378
 
379
+ Supported metadata for dynamic extension help/contract rendering:
380
+
381
+ - `required: true` appends a `[required]` marker in help.
382
+ - `enabled: false` appends a `[disabled]` marker in help.
383
+ - `visible: false` hides the flag from dynamic help output.
384
+ - `type` / `value_type` (`string` | `number` | `boolean`) enables runtime loose-option coercion for matching command flags.
385
+ - Validation contract: each entry must provide at least one of `long` or `short`; optional metadata fields must match expected scalar types.
386
+
387
+ Core help output appends command-level guidance with compact defaults (`Intent` + one example) and supports deep help via `--explain`. Dynamic extension commands receive flag-level rendering from both `registerFlags(...)` and `registerCommand({ flags: [...] })`; provide explicit `description` text on each flag to keep help/contracts high-signal.
388
+
96
389
  ### `api.registerRenderer(format, renderer)`
97
390
 
98
391
  Override TOON or JSON output for a command:
99
392
 
100
393
  ```ts
101
- api.registerRenderer("toon", (command, result, args, options, global, pmRoot) => {
102
- if (command !== "stats") return undefined; // pass through
103
- return customToonFormat(result);
394
+ api.registerRenderer("toon", (context) => {
395
+ if (context.command !== "stats") {
396
+ return `noop: ${JSON.stringify(context.result)}`;
397
+ }
398
+ return customToonFormat(context.result);
104
399
  });
105
400
  ```
106
401
 
107
- Return `undefined` to fall back to the built-in renderer.
402
+ Renderer overrides must return a string. Non-string return values are ignored and produce a deterministic `extension_renderer_invalid_result:<layer>:<name>:<format>` warning.
403
+
404
+ Without a renderer override, core TOON fallback output renders the command payload directly and applies sparse compaction:
405
+
406
+ - omits `null` and `undefined`
407
+ - omits empty arrays and empty objects
408
+ - preserves meaningful scalar values
409
+
410
+ If your extension needs a different shape (or must include fields omitted by sparse fallback), register a TOON renderer override.
108
411
 
109
412
  ### `api.registerImporter(name, importer)`
110
413
 
111
414
  Register an importer (also wires `<name> import` command path):
112
415
 
113
416
  ```ts
114
- api.registerImporter("jira", async (options, global) => {
115
- // options: parsed flags from `pm jira import ...`
417
+ api.registerImporter("jira", async (context) => {
418
+ // context.registration: normalized importer name
419
+ // context.action: "import"
420
+ // context.command: command path
421
+ // context.options: parsed command flags
422
+ // context.global: GlobalOptions
423
+ // context.pm_root: resolved PM root
116
424
  return { ok: true, imported: 5, skipped: 0, ids: ["pm-xxxx"], warnings: [] };
117
425
  });
118
426
  ```
@@ -122,11 +430,16 @@ api.registerImporter("jira", async (options, global) => {
122
430
  Register an exporter (also wires `<name> export` command path):
123
431
 
124
432
  ```ts
125
- api.registerExporter("jira", async (options, global) => {
433
+ api.registerExporter("jira", async (context) => {
434
+ // context.action: "export"
126
435
  return { ok: true, exported: 5, ids: ["pm-xxxx"], warnings: [] };
127
436
  });
128
437
  ```
129
438
 
439
+ Notes:
440
+
441
+ - `registerExporter(...)` uses the same capability gate as importers. Declare `importers` in `manifest.json` to use both `registerImporter(...)` and `registerExporter(...)`.
442
+
130
443
  ### `api.registerItemFields(fields)`
131
444
 
132
445
  Declare additional front-matter fields for schema-awareness:
@@ -137,6 +450,57 @@ api.registerItemFields([
137
450
  ]);
138
451
  ```
139
452
 
453
+ Validation contract: each field entry must include non-empty `name` and `type`; `optional` must be boolean when provided.
454
+
455
+ ### `api.registerItemTypes(types)`
456
+
457
+ Register custom item types and per-type create/type-option rules:
458
+
459
+ ```ts
460
+ api.registerItemTypes([
461
+ {
462
+ name: "Asset",
463
+ folder: "assets",
464
+ aliases: ["assets", "3d-asset"],
465
+ required_create_fields: ["title", "description", "status", "priority", "message"],
466
+ required_create_repeatables: [],
467
+ command_option_policies: [
468
+ { command: "create", option: "severity", enabled: false },
469
+ { command: "create", option: "reporter", enabled: false },
470
+ { command: "create", option: "goal", visible: false },
471
+ { command: "update", option: "message", required: true },
472
+ ],
473
+ options: [
474
+ {
475
+ key: "category",
476
+ values: ["Map", "Character", "Prop", "VFX"],
477
+ required: true,
478
+ aliases: ["asset_category"],
479
+ },
480
+ {
481
+ key: "pipeline",
482
+ values: ["Blockout", "Modeling", "Rigging", "Texturing", "Done"],
483
+ },
484
+ ],
485
+ },
486
+ ]);
487
+ ```
488
+
489
+ Validation contract highlights:
490
+
491
+ - each type entry must include non-empty `name`
492
+ - `aliases`, `required_create_fields`, and `required_create_repeatables` must be arrays of non-empty strings when provided
493
+ - `options[]` entries require non-empty `key`
494
+ - `command_option_policies[]` entries require non-empty `command` and `option`
495
+ - optional boolean toggles (`enabled`, `required`, `visible`) must be booleans when provided
496
+
497
+ Notes:
498
+
499
+ - Requires `schema` capability in the extension manifest.
500
+ - Type names and aliases are resolved by the runtime type registry and become available to `--type` filters and completion.
501
+ - Option definitions are validated by `pm create` / `pm update` through `--type-option` flags.
502
+ - `command_option_policies` are enforced by core create/update runtime and surfaced in policy-aware help sections.
503
+
140
504
  ### `api.registerMigration(def)`
141
505
 
142
506
  Declare a schema migration (tracked in `pm health`):
@@ -153,6 +517,8 @@ api.registerMigration({
153
517
  });
154
518
  ```
155
519
 
520
+ Validation contract: migration definitions must be objects; when provided, `id`/`description`/`status` must be strings, `mandatory` must be boolean, and `run` must be a function.
521
+
156
522
  Migrations with `mandatory: true` and `status` not `"applied"` block write commands until resolved (bypass with `--force`).
157
523
 
158
524
  ### `api.registerSearchProvider(provider)`
@@ -162,12 +528,16 @@ Register a custom search provider:
162
528
  ```ts
163
529
  api.registerSearchProvider({
164
530
  name: "elastic",
165
- query: async (query, options, settings) => {
166
- return [{ id: "pm-xxxx", score: 0.95 }];
531
+ query: async (context) => {
532
+ // context.query, context.mode, context.tokens
533
+ // context.options, context.settings, context.documents
534
+ return [{ id: "pm-xxxx", score: 0.95, matched_fields: ["provider:elastic"] }];
167
535
  },
168
536
  });
169
537
  ```
170
538
 
539
+ Use `settings.search.provider` to select the active extension provider for live `pm search` execution.
540
+
171
541
  ### `api.registerVectorStoreAdapter(adapter)`
172
542
 
173
543
  Register a custom vector store:
@@ -175,12 +545,18 @@ Register a custom vector store:
175
545
  ```ts
176
546
  api.registerVectorStoreAdapter({
177
547
  name: "pinecone",
178
- upsert: async (records, settings) => { ... },
179
- query: async (vector, topK, settings) => { ... },
180
- delete: async (ids, settings) => { ... },
548
+ upsert: async (context) => {
549
+ // context.points, context.settings
550
+ },
551
+ query: async (context) => {
552
+ // context.vector, context.limit, context.settings
553
+ return [{ id: "pm-xxxx", score: 0.87 }];
554
+ },
181
555
  });
182
556
  ```
183
557
 
558
+ Use `settings.vector_store.adapter` to select the active extension adapter for `pm search` query and `pm reindex` upsert.
559
+
184
560
  ## Lifecycle Hooks
185
561
 
186
562
  Hooks run for every applicable core operation. Hook handlers receive cloned context snapshots — mutations do not leak back into caller state.
@@ -208,7 +584,7 @@ Runs after a command completes (even on failure):
208
584
  api.hooks.afterCommand((ctx) => {
209
585
  // ctx.ok: boolean
210
586
  // ctx.result?: unknown
211
- // ctx.error?: unknown
587
+ // ctx.error?: string
212
588
  // same fields as beforeCommand
213
589
  });
214
590
  ```
@@ -220,8 +596,8 @@ Runs before each item file write:
220
596
  ```ts
221
597
  api.hooks.onWrite((ctx) => {
222
598
  // ctx.path: string
599
+ // ctx.scope: "project" | "global"
223
600
  // ctx.op: string (create, update, restore, etc.)
224
- // ctx.item_id: string
225
601
  });
226
602
  ```
227
603
 
@@ -232,7 +608,7 @@ Runs after each item file read:
232
608
  ```ts
233
609
  api.hooks.onRead((ctx) => {
234
610
  // ctx.path: string
235
- // ctx.item_id: string
611
+ // ctx.scope: "project" | "global"
236
612
  });
237
613
  ```
238
614
 
@@ -243,7 +619,7 @@ Runs during reindex/gc operations:
243
619
  ```ts
244
620
  api.hooks.onIndex((ctx) => {
245
621
  // ctx.mode: "keyword" | "semantic" | "hybrid" | "gc"
246
- // ctx.total?: number
622
+ // ctx.total_items?: number
247
623
  });
248
624
  ```
249
625
 
@@ -252,9 +628,21 @@ api.hooks.onIndex((ctx) => {
252
628
  `pm health` probes all loaded extensions and surfaces:
253
629
 
254
630
  - `extension_load_failed:<layer>:<name>` — manifest parse or module import error
631
+ - `extension_load_failed_sdk_dependency_missing:<name>` — doctor detected missing `@unbrained/pm-cli` dependency resolution in extension runtime imports
632
+ - `extension_load_failed_module_mode_mismatch:<name>` — doctor detected ESM/CJS mode mismatch (for example missing `"type": "module"` for ESM-style entrypoints)
255
633
  - `extension_activate_failed:<layer>:<name>` — exception in `activate()`
256
634
  - `extension_entry_outside_extension:<layer>:<name>` — entry path escapes directory
257
- - `extension_capability_unknown:<layer>:<name>:<capability>` — unknown capability in manifest
635
+ - `extension_capability_unknown:<layer>:<name>:<capability>:allowed=<csv>:suggested=<capability|none>` — unknown capability in manifest with inline guidance (including legacy alias replacements where applicable)
636
+ - `extension_capability_legacy_alias:<layer>:<name>:aliases=<csv>` — legacy manifest capability aliases were remapped (for example `migration`/`validation` -> `schema`)
637
+ - `extension_command_definition_legacy_handler_alias:<layer>:<name>:<command>` — command definition used legacy `handler` key; runtime mapped it to `run`
638
+ - collision warnings when multiple extensions target the same command/parser/preflight/service/renderer key (last registration wins)
639
+
640
+ `pm health` and `pm extension --doctor` also surface machine-readable capability contract metadata in diagnostics payloads:
641
+
642
+ - `details.capability_contract.version`
643
+ - `details.capability_contract.capabilities`
644
+ - `details.capability_contract.legacy_aliases`
645
+ - parsed guidance entries in `details.capability_guidance` include `capability_contract_version`, `suggestion_source`, and `legacy_alias_target` when available
258
646
 
259
647
  Use `pm health --json` to parse diagnostics programmatically.
260
648
 
@@ -294,7 +682,7 @@ Or configure per-project in `.agents/pm/settings.json`:
294
682
  export function activate(api) {
295
683
  api.registerCommand({
296
684
  name: "hello",
297
- run: async (_args, _options, _global) => {
685
+ run: async (_context) => {
298
686
  return { message: "Hello from extension!" };
299
687
  },
300
688
  });
@@ -306,24 +694,41 @@ pm hello
306
694
  # => message: Hello from extension!
307
695
  ```
308
696
 
309
- ## Built-in Extensions
697
+ ## Bundled Managed Extensions
310
698
 
311
- `pm-cli` ships two built-in extensions compiled into the package:
699
+ `pm-cli` ships two bundled extension sources in `.agents/pm/extensions`, but they are not auto-installed in project or global scope.
312
700
 
313
- | Extension | Commands | Purpose |
314
- |-----------|----------|---------|
315
- | `builtin-beads-import` | `pm beads import` | Import Beads JSONL records into pm items |
316
- | `builtin-todos-import-export` | `pm todos import`, `pm todos export` | Round-trip todos markdown format |
701
+ | Extension | Alias | Commands (after install) | Purpose |
702
+ |-----------|-------|---------------------------|---------|
703
+ | `builtin-beads-import` | `beads` | `pm beads import` | Import Beads JSONL records into pm items |
704
+ | `builtin-todos-import-export` | `todos` | `pm todos import`, `pm todos export` | Round-trip todos markdown format |
317
705
 
318
- Built-in extensions are loaded automatically and cannot be disabled via settings (use `--no-extensions` to disable all extensions including built-ins).
706
+ Install either via alias or explicit bundled path:
707
+
708
+ ```bash
709
+ # Alias installs
710
+ pm extension --install --project beads
711
+ pm extension --install --project todos
712
+
713
+ # Equivalent local bundled paths
714
+ pm extension --install --project .agents/pm/extensions/beads
715
+ pm extension --install --project .agents/pm/extensions/todos
716
+ ```
717
+
718
+ The command paths `pm beads ...` and `pm todos ...` are available only after the extension is installed and active for the selected scope.
319
719
 
320
720
  ## Pi Agent Extension
321
721
 
322
- The bundled Pi tool wrapper lives at `.pi/extensions/pm-cli/index.ts` and is a Pi agent extension (not a pm-cli extension). Install it with:
722
+ The Pi wrapper implementation source remains at `.pi/extensions/pm-cli/index.ts` and is a Pi agent extension (not a pm-cli runtime extension managed by `pm extension`).
323
723
 
324
- ```bash
325
- pm install pi # to current project .pi/extensions/pm-cli/index.ts
326
- pm install pi --global # to PI_CODING_AGENT_DIR/extensions/pm-cli/index.ts
327
- ```
724
+ Current wrapper parity includes:
725
+
726
+ - `action: "calendar"` for `pm calendar` / `pm cal` (`view`, `date`, `from`, `to`, `past`, `type`, `tag`, `priority`, `status`, `assignee`, `sprint`, `release`, `limit`, `format`)
727
+ - `create`/`update` reminder forwarding via repeatable `reminder` values (`at=<iso|relative>,text=<text>`)
728
+ - `create`/`update` custom type-option forwarding via repeatable `typeOption` values
729
+ - `create`/`update` audit controls including `allowAuditUpdate` and dependency-only `allowAuditDepUpdate`
730
+ - linked-test context preflight/remediation forwarding via `checkContext` and `autoPmContext` (`test` / `test-all`)
731
+ - cache cleanup forwarding via `dryRun` and repeatable `gcScope` (`gc`)
732
+ - extension lifecycle forwarding via `extension-install`, `extension-uninstall`, `extension-explore`, `extension-manage`, `extension-doctor`, `extension-adopt`, `extension-adopt-all`, `extension-activate`, and `extension-deactivate` actions (`target`, `scope`, `github`, `ref`, `detail`, `trace`, `runtimeProbe`, `fixManagedState`, `strictExit`, `failOnWarn`)
328
733
 
329
734
  See [AGENTS.md](../AGENTS.md) section 9 for full usage details.