@unbrained/pm-cli 2026.5.24 → 2026.5.28

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 (246) hide show
  1. package/CHANGELOG.md +953 -522
  2. package/README.md +2 -10
  3. package/dist/cli/bootstrap-args.d.ts +18 -1
  4. package/dist/cli/bootstrap-args.js +143 -3
  5. package/dist/cli/bootstrap-args.js.map +1 -1
  6. package/dist/cli/commander-usage.js +134 -11
  7. package/dist/cli/commander-usage.js.map +1 -1
  8. package/dist/cli/commands/append.js +4 -3
  9. package/dist/cli/commands/append.js.map +1 -1
  10. package/dist/cli/commands/claim.js +5 -4
  11. package/dist/cli/commands/claim.js.map +1 -1
  12. package/dist/cli/commands/close.d.ts +3 -0
  13. package/dist/cli/commands/close.js +26 -3
  14. package/dist/cli/commands/close.js.map +1 -1
  15. package/dist/cli/commands/completion.d.ts +2 -2
  16. package/dist/cli/commands/completion.js +109 -56
  17. package/dist/cli/commands/completion.js.map +1 -1
  18. package/dist/cli/commands/config.d.ts +1 -1
  19. package/dist/cli/commands/config.js +82 -4
  20. package/dist/cli/commands/config.js.map +1 -1
  21. package/dist/cli/commands/create.js +7 -272
  22. package/dist/cli/commands/create.js.map +1 -1
  23. package/dist/cli/commands/delete.js +4 -3
  24. package/dist/cli/commands/delete.js.map +1 -1
  25. package/dist/cli/commands/docs.d.ts +1 -12
  26. package/dist/cli/commands/docs.js +8 -312
  27. package/dist/cli/commands/docs.js.map +1 -1
  28. package/dist/cli/commands/extension/bundled-catalog.d.ts +14 -0
  29. package/dist/cli/commands/extension/bundled-catalog.js +268 -0
  30. package/dist/cli/commands/extension/bundled-catalog.js.map +1 -0
  31. package/dist/cli/commands/extension/doctor.d.ts +31 -0
  32. package/dist/cli/commands/extension/doctor.js +345 -0
  33. package/dist/cli/commands/extension/doctor.js.map +1 -0
  34. package/dist/cli/commands/extension/install-sources.d.ts +37 -0
  35. package/dist/cli/commands/extension/install-sources.js +384 -0
  36. package/dist/cli/commands/extension/install-sources.js.map +1 -0
  37. package/dist/cli/commands/extension/managed-state.d.ts +48 -0
  38. package/dist/cli/commands/extension/managed-state.js +172 -0
  39. package/dist/cli/commands/extension/managed-state.js.map +1 -0
  40. package/dist/cli/commands/extension/scaffold.d.ts +14 -0
  41. package/dist/cli/commands/extension/scaffold.js +202 -0
  42. package/dist/cli/commands/extension/scaffold.js.map +1 -0
  43. package/dist/cli/commands/extension/shared.d.ts +14 -0
  44. package/dist/cli/commands/extension/shared.js +106 -0
  45. package/dist/cli/commands/extension/shared.js.map +1 -0
  46. package/dist/cli/commands/extension.d.ts +36 -68
  47. package/dist/cli/commands/extension.js +143 -1422
  48. package/dist/cli/commands/extension.js.map +1 -1
  49. package/dist/cli/commands/files.d.ts +1 -12
  50. package/dist/cli/commands/files.js +11 -308
  51. package/dist/cli/commands/files.js.map +1 -1
  52. package/dist/cli/commands/get.js +4 -3
  53. package/dist/cli/commands/get.js.map +1 -1
  54. package/dist/cli/commands/health.js +17 -3
  55. package/dist/cli/commands/health.js.map +1 -1
  56. package/dist/cli/commands/history-redact.js +23 -18
  57. package/dist/cli/commands/history-redact.js.map +1 -1
  58. package/dist/cli/commands/history-repair.js +24 -18
  59. package/dist/cli/commands/history-repair.js.map +1 -1
  60. package/dist/cli/commands/legacy-none-tokens.d.ts +3 -0
  61. package/dist/cli/commands/legacy-none-tokens.js +39 -0
  62. package/dist/cli/commands/legacy-none-tokens.js.map +1 -0
  63. package/dist/cli/commands/linked-artifacts.d.ts +96 -0
  64. package/dist/cli/commands/linked-artifacts.js +335 -0
  65. package/dist/cli/commands/linked-artifacts.js.map +1 -0
  66. package/dist/cli/commands/linked-test-parsers.d.ts +28 -0
  67. package/dist/cli/commands/linked-test-parsers.js +192 -0
  68. package/dist/cli/commands/linked-test-parsers.js.map +1 -0
  69. package/dist/cli/commands/list.js +19 -5
  70. package/dist/cli/commands/list.js.map +1 -1
  71. package/dist/cli/commands/normalize.js +4 -3
  72. package/dist/cli/commands/normalize.js.map +1 -1
  73. package/dist/cli/commands/plan.d.ts +5 -0
  74. package/dist/cli/commands/plan.js +56 -8
  75. package/dist/cli/commands/plan.js.map +1 -1
  76. package/dist/cli/commands/recurrence-parsers.d.ts +26 -0
  77. package/dist/cli/commands/recurrence-parsers.js +98 -0
  78. package/dist/cli/commands/recurrence-parsers.js.map +1 -0
  79. package/dist/cli/commands/restore.js +19 -8
  80. package/dist/cli/commands/restore.js.map +1 -1
  81. package/dist/cli/commands/search.js +5 -8
  82. package/dist/cli/commands/search.js.map +1 -1
  83. package/dist/cli/commands/test/linked-command-detection.d.ts +37 -0
  84. package/dist/cli/commands/test/linked-command-detection.js +200 -0
  85. package/dist/cli/commands/test/linked-command-detection.js.map +1 -0
  86. package/dist/cli/commands/test.d.ts +1 -2
  87. package/dist/cli/commands/test.js +8 -350
  88. package/dist/cli/commands/test.js.map +1 -1
  89. package/dist/cli/commands/update-many.js +4 -3
  90. package/dist/cli/commands/update-many.js.map +1 -1
  91. package/dist/cli/commands/update.js +83 -356
  92. package/dist/cli/commands/update.js.map +1 -1
  93. package/dist/cli/commands/validate.js +32 -12
  94. package/dist/cli/commands/validate.js.map +1 -1
  95. package/dist/cli/error-guidance.d.ts +1 -0
  96. package/dist/cli/error-guidance.js +6 -2
  97. package/dist/cli/error-guidance.js.map +1 -1
  98. package/dist/cli/main.d.ts +11 -0
  99. package/dist/cli/main.js +76 -28
  100. package/dist/cli/main.js.map +1 -1
  101. package/dist/cli/register-list-query.d.ts +4 -1
  102. package/dist/cli/register-list-query.js +242 -203
  103. package/dist/cli/register-list-query.js.map +1 -1
  104. package/dist/cli/register-mutation.js +73 -11
  105. package/dist/cli/register-mutation.js.map +1 -1
  106. package/dist/cli/register-operations.js +3 -3
  107. package/dist/cli/register-operations.js.map +1 -1
  108. package/dist/cli/register-setup.js +12 -7
  109. package/dist/cli/register-setup.js.map +1 -1
  110. package/dist/cli/registration-helpers.js +3 -2
  111. package/dist/cli/registration-helpers.js.map +1 -1
  112. package/dist/cli.js +4 -3
  113. package/dist/cli.js.map +1 -1
  114. package/dist/core/config/positional-value.d.ts +44 -0
  115. package/dist/core/config/positional-value.js +109 -0
  116. package/dist/core/config/positional-value.js.map +1 -0
  117. package/dist/core/extensions/extension-capability-aliases.d.ts +14 -0
  118. package/dist/core/extensions/extension-capability-aliases.js +159 -0
  119. package/dist/core/extensions/extension-capability-aliases.js.map +1 -0
  120. package/dist/core/extensions/extension-hook-runtime.d.ts +13 -0
  121. package/dist/core/extensions/extension-hook-runtime.js +414 -0
  122. package/dist/core/extensions/extension-hook-runtime.js.map +1 -0
  123. package/dist/core/extensions/extension-policy.d.ts +69 -0
  124. package/dist/core/extensions/extension-policy.js +481 -0
  125. package/dist/core/extensions/extension-policy.js.map +1 -0
  126. package/dist/core/extensions/extension-registries.d.ts +8 -0
  127. package/dist/core/extensions/extension-registries.js +52 -0
  128. package/dist/core/extensions/extension-registries.js.map +1 -0
  129. package/dist/core/extensions/extension-runtime-helpers.d.ts +6 -0
  130. package/dist/core/extensions/extension-runtime-helpers.js +29 -0
  131. package/dist/core/extensions/extension-runtime-helpers.js.map +1 -0
  132. package/dist/core/extensions/extension-types.d.ts +13 -39
  133. package/dist/core/extensions/extension-types.js +34 -2
  134. package/dist/core/extensions/extension-types.js.map +1 -1
  135. package/dist/core/extensions/index.d.ts +7 -0
  136. package/dist/core/extensions/index.js +11 -2
  137. package/dist/core/extensions/index.js.map +1 -1
  138. package/dist/core/extensions/loader.d.ts +4 -22
  139. package/dist/core/extensions/loader.js +22 -1139
  140. package/dist/core/extensions/loader.js.map +1 -1
  141. package/dist/core/history/drift-scan.d.ts +11 -0
  142. package/dist/core/history/drift-scan.js +114 -32
  143. package/dist/core/history/drift-scan.js.map +1 -1
  144. package/dist/core/history/history-rewrite.d.ts +43 -0
  145. package/dist/core/history/history-rewrite.js +48 -0
  146. package/dist/core/history/history-rewrite.js.map +1 -0
  147. package/dist/core/history/history.js +5 -4
  148. package/dist/core/history/history.js.map +1 -1
  149. package/dist/core/history/replay.js +4 -3
  150. package/dist/core/history/replay.js.map +1 -1
  151. package/dist/core/item/item-record.d.ts +19 -0
  152. package/dist/core/item/item-record.js +24 -0
  153. package/dist/core/item/item-record.js.map +1 -0
  154. package/dist/core/output/mutation-projection.d.ts +31 -0
  155. package/dist/core/output/mutation-projection.js +103 -0
  156. package/dist/core/output/mutation-projection.js.map +1 -0
  157. package/dist/core/output/output.d.ts +2 -0
  158. package/dist/core/output/output.js +5 -3
  159. package/dist/core/output/output.js.map +1 -1
  160. package/dist/core/schema/runtime-schema.js +8 -38
  161. package/dist/core/schema/runtime-schema.js.map +1 -1
  162. package/dist/core/search/vector-stores.js +46 -9
  163. package/dist/core/search/vector-stores.js.map +1 -1
  164. package/dist/core/sentry/helpers.d.ts +1 -1
  165. package/dist/core/sentry/helpers.js +20 -3
  166. package/dist/core/sentry/helpers.js.map +1 -1
  167. package/dist/core/shared/command-types.d.ts +1 -0
  168. package/dist/core/shared/command-types.js +2 -2
  169. package/dist/core/shared/command-types.js.map +1 -1
  170. package/dist/core/shared/constants.d.ts +10 -1
  171. package/dist/core/shared/constants.js +56 -58
  172. package/dist/core/shared/constants.js.map +1 -1
  173. package/dist/core/shared/levenshtein.js +23 -7
  174. package/dist/core/shared/levenshtein.js.map +1 -1
  175. package/dist/core/shared/primitives.d.ts +23 -0
  176. package/dist/core/shared/primitives.js +39 -2
  177. package/dist/core/shared/primitives.js.map +1 -1
  178. package/dist/core/store/front-matter-cache.d.ts +16 -2
  179. package/dist/core/store/front-matter-cache.js +99 -33
  180. package/dist/core/store/front-matter-cache.js.map +1 -1
  181. package/dist/core/store/item-store.js +8 -73
  182. package/dist/core/store/item-store.js.map +1 -1
  183. package/dist/mcp/server.js +76 -28
  184. package/dist/mcp/server.js.map +1 -1
  185. package/dist/sdk/cli-contracts/enum-contracts.d.ts +20 -0
  186. package/dist/sdk/cli-contracts/enum-contracts.js +156 -0
  187. package/dist/sdk/cli-contracts/enum-contracts.js.map +1 -0
  188. package/dist/sdk/cli-contracts/tool-option-contracts.d.ts +14 -0
  189. package/dist/sdk/cli-contracts/tool-option-contracts.js +243 -0
  190. package/dist/sdk/cli-contracts/tool-option-contracts.js.map +1 -0
  191. package/dist/sdk/cli-contracts/tool-parameter-tables.d.ts +11 -0
  192. package/dist/sdk/cli-contracts/tool-parameter-tables.js +901 -0
  193. package/dist/sdk/cli-contracts/tool-parameter-tables.js.map +1 -0
  194. package/dist/sdk/cli-contracts.d.ts +11 -33
  195. package/dist/sdk/cli-contracts.js +30 -1356
  196. package/dist/sdk/cli-contracts.js.map +1 -1
  197. package/dist/sdk/package-import-adapters.d.ts +74 -0
  198. package/dist/sdk/package-import-adapters.js +186 -0
  199. package/dist/sdk/package-import-adapters.js.map +1 -0
  200. package/dist/sdk/package-runtime-options.d.ts +26 -0
  201. package/dist/sdk/package-runtime-options.js +71 -0
  202. package/dist/sdk/package-runtime-options.js.map +1 -0
  203. package/dist/sdk/runtime.d.ts +2 -0
  204. package/dist/sdk/runtime.js +4 -2
  205. package/dist/sdk/runtime.js.map +1 -1
  206. package/docs/AGENT_GUIDE.md +6 -10
  207. package/docs/CLAUDE_CODE_PLUGIN.md +5 -28
  208. package/docs/CODEX_PLUGIN.md +5 -5
  209. package/docs/COMMANDS.md +19 -3
  210. package/docs/CONFIGURATION.md +15 -0
  211. package/docs/EXTENSIONS.md +4 -63
  212. package/docs/RELEASING.md +4 -4
  213. package/marketplace.json +7 -3
  214. package/package.json +9 -6
  215. package/packages/pm-beads/extensions/beads/index.js +2 -49
  216. package/packages/pm-beads/extensions/beads/index.ts +2 -54
  217. package/packages/pm-beads/extensions/beads/runtime-loader.js +86 -0
  218. package/packages/pm-beads/extensions/beads/runtime-loader.ts +88 -0
  219. package/packages/pm-beads/extensions/beads/runtime.js +26 -115
  220. package/packages/pm-beads/extensions/beads/runtime.ts +33 -132
  221. package/packages/pm-calendar/extensions/calendar/index.js +47 -2
  222. package/packages/pm-calendar/extensions/calendar/index.ts +52 -2
  223. package/packages/pm-calendar/extensions/calendar/runtime.js +1 -0
  224. package/packages/pm-calendar/extensions/calendar/runtime.ts +1 -0
  225. package/packages/pm-governance-audit/extensions/governance-audit/runtime.js +14 -41
  226. package/packages/pm-governance-audit/extensions/governance-audit/runtime.ts +25 -41
  227. package/packages/pm-guide-shell/extensions/guide-shell/runtime.js +10 -50
  228. package/packages/pm-guide-shell/extensions/guide-shell/runtime.ts +17 -50
  229. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/runtime.js +8 -40
  230. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/runtime.ts +10 -40
  231. package/packages/pm-search-advanced/extensions/search-advanced/index.js +1 -1
  232. package/packages/pm-search-advanced/extensions/search-advanced/runtime.js +4 -37
  233. package/packages/pm-search-advanced/extensions/search-advanced/runtime.ts +6 -37
  234. package/packages/pm-todos/extensions/todos/index.js +3 -50
  235. package/packages/pm-todos/extensions/todos/index.ts +3 -55
  236. package/packages/pm-todos/extensions/todos/runtime-loader.js +86 -0
  237. package/packages/pm-todos/extensions/todos/runtime-loader.ts +88 -0
  238. package/packages/pm-todos/extensions/todos/runtime.js +24 -117
  239. package/packages/pm-todos/extensions/todos/runtime.ts +32 -129
  240. package/plugins/pm-claude/README.md +2 -2
  241. package/plugins/pm-claude/commands/pm-planner.md +1 -15
  242. package/plugins/pm-claude/scripts/pm-mcp-server.mjs +5 -2
  243. package/plugins/pm-claude/skills/pm-planner/SKILL.md +3 -21
  244. package/plugins/pm-codex/scripts/pm-mcp-server.mjs +15 -6
  245. package/plugins/pm-codex/skills/pm-native/SKILL.md +1 -13
  246. package/PRD.md +0 -1734
package/PRD.md DELETED
@@ -1,1734 +0,0 @@
1
- # pm-cli Product Requirements Document (PRD)
2
-
3
- Status: Draft v1 (planning reference; pm data and runtime behavior are authoritative)
4
- Project: `pm` / `pm-cli`
5
- Last Updated: 2026-05-12
6
-
7
- ## Navigation
8
-
9
- This PRD is an archival planning reference. For current user and agent documentation, use:
10
-
11
- - [README](README.md)
12
- - [Documentation index](docs/README.md)
13
- - [Agent Guide](docs/AGENT_GUIDE.md)
14
- - [Command Reference](docs/COMMANDS.md)
15
- - [Architecture](docs/ARCHITECTURE.md)
16
-
17
- Documentation refresh tracking:
18
-
19
- - [pm-3042](.agents/pm/epics/pm-3042.toon)
20
- - [pm-r9gu](.agents/pm/features/pm-r9gu.toon)
21
- - [pm-1sb2](.agents/pm/tasks/pm-1sb2.toon)
22
-
23
- ## 1) Problem Statement
24
-
25
- Coding agents and humans need a shared project-management system that is:
26
-
27
- - Git-native (diffable, reviewable, branch-friendly)
28
- - Deterministic (stable machine-readable output for automation)
29
- - Robust under concurrent edits (claiming + lock safety)
30
- - Extensible (project-local and global custom behavior)
31
- - Token-efficient for LLM workflows (TOON by default, JSON fallback)
32
-
33
- Existing trackers either rely on hosted backends, store state in non-diff-friendly formats, or do not provide first-class agent ergonomics for claiming, dependencies, history replay, and deterministic output.
34
-
35
- ## 2) Goals
36
-
37
- - Build a cross-platform TypeScript CLI named `pm`.
38
- - Store all core tracker data in project-local files under `.agents/pm` by default.
39
- - Model work as first-class items: `Epic`, `Feature`, `Task`, `Chore`, `Issue`, `Event`, `Reminder`, `Milestone`, `Meeting`.
40
- - Support full item lifecycle operations, deterministic listing/filtering, and rich metadata.
41
- - Support reminder-aware scheduling workflows with deterministic calendar views for agents and humans.
42
- - Provide append-only item history with patch-level restore.
43
- - Provide safe mutation under concurrent access (claim/release + lock + atomic writes).
44
- - Default stdout to TOON; support `--json` parity for every command.
45
- - Provide extension architecture for commands, schema, rendering, import/export, search adapters, and hooks.
46
- - Ship first-party installable package sources:
47
- - Beads import (`packages/pm-beads`, installed via `pm install`)
48
- - todos.ts import/export (`packages/pm-todos`, installed via `pm install`)
49
- - Provide optional semantic search with provider + vector-store adapters.
50
-
51
- ## 3) Explicit Non-Goals
52
-
53
- - No required UI/TUI (CLI-first only).
54
- - No required remote control plane for core tracker.
55
- - No required database for core tracker (file-backed core is mandatory).
56
- - Export to Beads is not required in v1 (import only).
57
-
58
- ## 4) Reference Inputs and Design Findings
59
-
60
- ### 4.1 Local reference inputs analyzed
61
-
62
- 1. `todos.ts` (local Pi extension implementation)
63
- 2. `.beads/issues.jsonl` (local Beads-style JSONL data)
64
-
65
- ### 4.2 Upstream inspirations analyzed (conceptual only)
66
-
67
- - mitsuhiko todos extension
68
- - beads repository/docs
69
- - TOON docs/spec guidance for LLM output conventions
70
-
71
- ### 4.3 Key findings adopted
72
-
73
- From `todos.ts`:
74
-
75
- - Legacy import format = JSON front-matter at file start, blank line, then markdown body.
76
- - ID normalization accepts optional `#` and optional prefix.
77
- - Claim/release is represented in-record (`assignee`).
78
- - Locking model:
79
- - lock file created with exclusive open (`wx`)
80
- - TTL-based stale-lock handling
81
- - lock metadata includes PID/owner/timestamp
82
- - Safe-write ergonomics should provide clear conflict errors.
83
-
84
- From local Beads JSONL:
85
-
86
- - `issue_type`, `priority`, `status`, `created_at`, `updated_at` are strongly present.
87
- - Common extra fields include: `description`, `acceptance_criteria`, `notes`, `comments`, `dependencies`, `close_reason`, `estimated_minutes`.
88
- - Dependency records frequently carry relation kinds (`blocks`, `parent-child`, `discovered-from`, `related`), timestamps, and author.
89
- - IDs may include hierarchical suffixes (`prefix-hash.1.2`), so importer must preserve non-flat IDs.
90
-
91
- From TOON guidance:
92
-
93
- - Show structure directly, keep deterministic layout, and preserve strict machine parseability.
94
- - Keep output schema stable and field ordering deterministic.
95
- - JSON fallback should preserve the full command payload; TOON may be a sparse projection optimized for token efficiency.
96
-
97
- ## 5) Core Concepts
98
-
99
- ### 5.1 Item Types (canonical)
100
-
101
- - `Epic`
102
- - `Feature`
103
- - `Task`
104
- - `Chore`
105
- - `Issue`
106
- - `Event`
107
- - `Reminder`
108
- - `Milestone`
109
- - `Meeting`
110
-
111
- ### 5.2 Status lifecycle
112
-
113
- Allowed values:
114
-
115
- - `draft`
116
- - `open`
117
- - `in_progress`
118
- - `blocked`
119
- - `closed`
120
- - `canceled`
121
-
122
- Input compatibility:
123
-
124
- - Accept `in-progress` as an input alias and normalize to canonical `in_progress` for persisted item data and command output.
125
-
126
- Lifecycle rules:
127
-
128
- - Any non-terminal status may transition to `canceled` via `pm update <ID> --status canceled`.
129
- - Any non-terminal status may transition to `closed` only via `pm close <ID> <TEXT>`.
130
- - `pm update <ID> --status closed` is invalid usage and returns exit code `2`.
131
- - `closed` and `canceled` are terminal unless explicitly restored or reopened.
132
- - `close` command must write `close_reason`.
133
- - `pm close <ID> <TEXT> --validate-close [warn|strict]` validates closure resolution metadata (`resolution`, `expected_result`, `actual_result`) in warning-first mode unless strict is explicitly requested.
134
- - `pm update <ID> --close-reason <TEXT>` explicitly sets `close_reason`; `pm update <ID> --unset close-reason` clears it.
135
- - When `pm update` reopens an item from `closed` to a non-terminal status, stale `close_reason` is auto-cleared unless `--close-reason` is explicitly supplied on that same mutation.
136
- - `claim` on terminal status fails unless explicitly overridden by `--force`.
137
-
138
- ### 5.3 Ownership model
139
-
140
- - Ownership marker is `assignee`.
141
- - `pm claim <id>` sets ownership to current mutation author identity.
142
- - `pm release <id>` clears ownership.
143
- - `pm claim <id>` may take over non-terminal items assigned to another assignee without `--force`.
144
- - Mutations other than `claim` against items assigned to another assignee return conflict unless `--force` (`comments`/`notes`/`learnings` can use additive `--allow-audit-comment` for append-only audit entries, and `release` can use `--allow-audit-release` for non-owner handoffs that only clear assignee metadata).
145
- - Ownership-conflict guidance should call out approved `--force` scenarios (for example PM audits, coordinated lead-maintainer metadata correction, or explicit ownership handoff cleanup).
146
-
147
- ### 5.4 Dependencies model
148
-
149
- Each dependency entry:
150
-
151
- - `id: string`
152
- - `kind: "blocks" | "parent" | "child" | "related" | "discovered_from"`
153
- - `created_at: ISO timestamp`
154
- - `author?: string`
155
-
156
- Semantics:
157
-
158
- - `blocks`: this item blocks target item OR is blocked by target based on command context; CLI sugar resolves direction.
159
- - `parent` / `child`: hierarchy graph links.
160
- - `related`: non-blocking relation.
161
- - `discovered_from`: provenance trail.
162
-
163
- ### 5.5 Notes, learnings, comments
164
-
165
- These are append-friendly audit fields:
166
-
167
- - `comments`: user-visible conversational updates.
168
- - `notes`: implementation observations.
169
- - `learnings`: post-task durable findings.
170
- - Existing items are extended through `pm comments`, `pm notes`, and `pm learnings` add flows; create-time seed flags only bootstrap initial values.
171
-
172
- All append operations produce history entries.
173
-
174
- ## 6) On-Disk Storage Layout
175
-
176
- Default project root: `.agents/pm`
177
- Override for command invocation: `PM_PATH` or `--path`.
178
-
179
- Global extension root: `~/.pm-cli`
180
- Override: `PM_GLOBAL_PATH`.
181
-
182
- Required baseline:
183
-
184
- ```text
185
- .agents/pm/
186
- settings.json
187
- epics/
188
- <id>.md
189
- features/
190
- <id>.md
191
- tasks/
192
- <id>.md
193
- chores/
194
- <id>.md
195
- issues/
196
- <id>.md
197
- history/
198
- <id>.jsonl
199
- index/
200
- manifest.json
201
- search/
202
- embeddings.jsonl
203
- extensions/
204
- ...
205
- locks/
206
- <id>.lock
207
- ```
208
-
209
- Notes:
210
-
211
- - `index/manifest.json` and `search/embeddings.jsonl` are optional caches and can be rebuilt.
212
- - `history/<id>.jsonl` is append-only and required once item exists.
213
- - `locks/` is the canonical lock location for v1.
214
-
215
- ### 6.1 Source layout for release-ready maintainability
216
-
217
- Implementation source tree MUST separate CLI wiring from domain logic:
218
-
219
- ```text
220
- src/
221
- cli/
222
- main.ts
223
- commands/
224
- core/
225
- fs/
226
- history/
227
- item/
228
- lock/
229
- output/
230
- store/
231
- types/
232
- tests/
233
- unit/
234
- integration/
235
- scripts/
236
- install.sh
237
- install.ps1
238
- ```
239
-
240
- Constraints:
241
-
242
- - Public CLI entry remains stable through npm `bin` mapping (`pm` -> built CLI entry).
243
- - Deterministic serialization semantics are unchanged by module movement.
244
- - Integration tests execute built CLI in subprocesses against temporary sandbox paths only.
245
-
246
- ## 7) Item File Format
247
-
248
- Each item is one TOON document at `<type-folder>/<id>.toon`. Legacy `<type-folder>/<id>.md` files are read only for migration.
249
-
250
- Format:
251
-
252
- 1. TOON root-object metadata keys.
253
- 2. Optional `body` field.
254
-
255
- ### 7.1 Canonical item-metadata schema
256
-
257
- `metadata` is the internal TypeScript field name (`ItemDocument.metadata`), and TOON documents store the same metadata as top-level keys.
258
-
259
- Required fields:
260
-
261
- - `id: string`
262
- - `title: string`
263
- - `description: string`
264
- - `tags: string[]`
265
- - `status: "draft" | "open" | "in_progress" | "blocked" | "closed" | "canceled"`
266
- - `priority: 0 | 1 | 2 | 3 | 4`
267
- - `type: "Epic" | "Feature" | "Task" | "Chore" | "Issue" | "Event" | "Reminder" | "Milestone" | "Meeting"`
268
- - `created_at: ISO string`
269
- - `updated_at: ISO string`
270
-
271
- Optional fields:
272
-
273
- - `assignee?: string`
274
- - `deadline?: ISO string` (ISO/date-string/relative input resolved to ISO at write time)
275
- - `reminders?: Reminder[]` where `Reminder = { at: ISO string; text: string }`
276
- - `dependencies?: Dependency[]`
277
- - `comments?: Comment[]`
278
- - `author?: string`
279
- - `acceptance_criteria?: string`
280
- - `definition_of_ready?: string`
281
- - `order?: number`
282
- - `goal?: string`
283
- - `objective?: string`
284
- - `value?: string`
285
- - `impact?: string`
286
- - `outcome?: string`
287
- - `why_now?: string`
288
- - `notes?: LogNote[]`
289
- - `learnings?: LogNote[]`
290
- - `files?: LinkedFile[]`
291
- - `tests?: LinkedTest[]`
292
- - `test_runs?: ItemTestRunSummary[]`
293
- - `docs?: LinkedDoc[]`
294
- - `estimated_minutes?: number`
295
- - `parent?: string` (item ID reference; shorthand for a `kind=parent` dependency)
296
- - `reviewer?: string`
297
- - `risk?: "low" | "medium" | "high" | "critical"`
298
- - `confidence?: 0..100 | "low" | "medium" | "high"`
299
- - `sprint?: string`
300
- - `release?: string`
301
- - `blocked_by?: string` (item ID reference or free-text reason)
302
- - `blocked_reason?: string`
303
- - `unblock_note?: string`
304
- - `reporter?: string`
305
- - `severity?: "low" | "medium" | "high" | "critical"`
306
- - `environment?: string`
307
- - `repro_steps?: string`
308
- - `resolution?: string`
309
- - `expected_result?: string`
310
- - `actual_result?: string`
311
- - `affected_version?: string`
312
- - `fixed_version?: string`
313
- - `component?: string`
314
- - `regression?: boolean`
315
- - `customer_impact?: string`
316
- - `close_reason?: string`
317
-
318
- Types:
319
-
320
- - `Dependency = { id: string; kind: "blocks" | "parent" | "child" | "related" | "discovered_from"; created_at: string; author?: string }`
321
- - `Comment = { created_at: string; author: string; text: string }`
322
- - `LogNote = { created_at: string; author: string; text: string }`
323
- - `LinkedFile = { path: string; scope: "project" | "global"; note?: string }`
324
- - `LinkedTest = { command?: string; path?: string; scope: "project" | "global"; timeout_seconds?: number; note?: string }`
325
- - `ItemTestRunSummary = { run_id: string; kind: "test" | "test-all"; status: "passed" | "failed" | "stopped" | "canceled"; started_at: string; finished_at: string; recorded_at: string; attempt?: number; resumed_from?: string; passed: number; failed: number; skipped: number; items?: number; linked_tests?: number; fail_on_skipped_triggered?: boolean }`
326
- - `LinkedDoc = { path: string; scope: "project" | "global"; note?: string }`
327
- - `Reminder = { at: string; text: string }`
328
- - `IssueSeverity = "low" | "medium" | "high" | "critical"`
329
-
330
- ### 7.2 Canonical key order
331
-
332
- Keys MUST serialize in this order:
333
-
334
- 1. `id`
335
- 2. `title`
336
- 3. `description`
337
- 4. `type`
338
- 5. `status`
339
- 6. `priority`
340
- 7. `tags`
341
- 8. `created_at`
342
- 9. `updated_at`
343
- 10. `deadline`
344
- 11. `reminders`
345
- 12. `assignee`
346
- 13. `author`
347
- 14. `estimated_minutes`
348
- 15. `acceptance_criteria`
349
- 16. `definition_of_ready`
350
- 17. `order`
351
- 18. `goal`
352
- 19. `objective`
353
- 20. `value`
354
- 21. `impact`
355
- 22. `outcome`
356
- 23. `why_now`
357
- 24. `parent`
358
- 25. `reviewer`
359
- 26. `risk`
360
- 27. `confidence`
361
- 28. `sprint`
362
- 29. `release`
363
- 30. `blocked_by`
364
- 31. `blocked_reason`
365
- 32. `unblock_note`
366
- 33. `reporter`
367
- 34. `severity`
368
- 35. `environment`
369
- 36. `repro_steps`
370
- 37. `resolution`
371
- 38. `expected_result`
372
- 39. `actual_result`
373
- 40. `affected_version`
374
- 41. `fixed_version`
375
- 42. `component`
376
- 43. `regression`
377
- 44. `customer_impact`
378
- 45. `dependencies`
379
- 46. `comments`
380
- 47. `notes`
381
- 48. `learnings`
382
- 49. `files`
383
- 50. `tests`
384
- 51. `test_runs`
385
- 52. `docs`
386
- 53. `close_reason`
387
-
388
- Unset optional fields are omitted.
389
-
390
- ### 7.3 Determinism rules
391
-
392
- - `updated_at` MUST change for every mutation.
393
- - Relative deadlines (`+6h`, `+1d`, `+2w`, `+6m`) and accepted date-string forms resolve on write and persist as absolute ISO.
394
- - `tags` sorted lexicographically, deduplicated.
395
- - `risk` CLI input alias `med` normalizes to canonical stored value `medium`.
396
- - `confidence` CLI input accepts integers `0..100` or `low|med|medium|high`; `med` persists as `medium`.
397
- - `severity` CLI input alias `med` normalizes to canonical stored value `medium`.
398
- - `dependencies`, `comments`, `notes`, `learnings` sorted by `created_at` ascending; stable tie-break by text/id.
399
- - `reminders` sorted by `at` ascending, then `text` ascending.
400
- - `files` preserve provided order in canonical storage; `pm files` default mutation mode writes deterministic sorted order unless `--append-stable` is explicitly selected.
401
- - `tests` sorted by `scope` asc, then `path` asc, then `command` asc, then `timeout_seconds` asc, then `note` asc.
402
- - `test_runs` sorted by `recorded_at` asc, then `run_id` asc, then `kind` asc; retention is bounded to latest N entries per item.
403
- - `docs` sorted by `scope` asc, then `path` asc, then `note` asc.
404
- - Paths normalized to forward-slash logical form for storage while preserving OS-correct access at runtime.
405
- - For optional create/update fields, explicit clear intent is supported via dedicated flags:
406
- - scalar fields use repeatable `--unset <field>` (for example `--unset deadline`, `--unset assignee`)
407
- - repeatable collections use `--clear-*` flags (for example `--clear-deps`, `--clear-comments`)
408
- - these intents MUST be represented in `changed_fields` and history `message`.
409
-
410
- ### 7.4 Example item file
411
-
412
- ```markdown
413
- {
414
- "id": "pm-a1b2",
415
- "title": "Implement restore command",
416
- "description": "Add full RFC6902 replay restore with hash verification.",
417
- "type": "Task",
418
- "status": "in_progress",
419
- "priority": 1,
420
- "tags": [
421
- "history",
422
- "reliability"
423
- ],
424
- "created_at": "2026-02-17T10:00:00.000Z",
425
- "updated_at": "2026-02-17T11:15:03.120Z",
426
- "assignee": "maintainer-agent",
427
- "author": "steve",
428
- "acceptance_criteria": "Restore reproduces exact file content at target version.",
429
- "dependencies": [
430
- {
431
- "id": "pm-9c8d",
432
- "kind": "blocks",
433
- "created_at": "2026-02-17T10:02:31.000Z",
434
- "author": "steve"
435
- }
436
- ],
437
- "tests": [
438
- {
439
- "command": "node scripts/run-tests.mjs test -- tests/unit/history-command.spec.ts",
440
- "scope": "project",
441
- "timeout_seconds": 90
442
- }
443
- ]
444
- }
445
-
446
- Implement strict replay logic and integrity checks.
447
- ```
448
-
449
- ## 8) ID Strategy
450
-
451
- ### 8.1 Format
452
-
453
- - Default prefix: `pm-`
454
- - Init-time custom prefix supported via `pm init [PREFIX]`
455
- - Canonical generated leaf: `<prefix><token>` where token is short lowercase base32/base36.
456
- - Valid imported IDs may include hierarchical suffixes (`.1`, `.1.2`) and MUST be preserved.
457
-
458
- ### 8.2 Generation
459
-
460
- - Generate cryptographically secure random bytes.
461
- - Encode to lowercase base32/base36 token (default length 4 for readability).
462
- - Validate non-existence in all type folders.
463
- - Retry with bounded attempts; on repeated collision, increase token length.
464
-
465
- ### 8.3 Normalization
466
-
467
- Input normalization MUST:
468
-
469
- - Trim whitespace
470
- - Accept optional leading `#`
471
- - Accept ID with or without configured prefix
472
- - Return canonical stored ID string
473
-
474
- Examples (prefix `pm-`):
475
-
476
- - `#a1b2` -> `pm-a1b2`
477
- - `a1b2` -> `pm-a1b2`
478
- - `pm-a1b2` -> `pm-a1b2`
479
- - `PM-A1B2` -> `pm-a1b2`
480
-
481
- ## 9) History and Restore (Hard Requirement)
482
-
483
- ### 9.1 History file
484
-
485
- Path: `.agents/pm/history/<id>.jsonl`
486
- Append-only; never rewritten for normal operations.
487
-
488
- Each line:
489
-
490
- - `ts: ISO timestamp`
491
- - `author: string`
492
- - `op: string` (`create`, `update`, `append`, `comment_add`, `files_add`, `restore`, etc.)
493
- - `patch: RFC6902[]` (from previous state to next state on canonical document object)
494
- - `before_hash: string`
495
- - `after_hash: string`
496
- - `message?: string`
497
-
498
- Canonical patch document shape:
499
-
500
- ```json
501
- {
502
- "metadata": { "...": "..." },
503
- "body": "markdown text"
504
- }
505
- ```
506
-
507
- ### 9.2 Hashing
508
-
509
- - Hash algorithm: SHA-256
510
- - Input: canonical JSON serialization of patch document (stable key order, UTF-8 LF)
511
- - Digest format: lowercase hex
512
-
513
- ### 9.3 Restore algorithm
514
-
515
- `pm restore <ID> <TIMESTAMP|VERSION>`
516
-
517
- 1. Resolve item or matching history stream for ID and load full history.
518
- 2. Replay patches from initial create through target version/timestamp.
519
- 3. Rebuild exact canonical document (`metadata` + `body`).
520
- 4. Write item atomically.
521
- 5. Append a `restore` history event with patch from pre-restore state to restored state.
522
-
523
- Guarantees:
524
-
525
- - History is immutable (restore appends, never rewrites old entries).
526
- - Restored item bytes match canonical serialization of target state exactly.
527
-
528
- ### 9.4 Missing history stream policy
529
-
530
- `settings.history.missing_stream` controls missing-stream behavior for history-touching command paths:
531
-
532
- - `auto_create` (default): create missing streams for existing item IDs, then continue command execution.
533
- - `strict_error`: fail fast when a required stream is missing.
534
-
535
- Scope: this policy applies to read/diagnostic paths (`history`, `activity`, `stats`, `health`) and existing-item mutation/restore flows.
536
-
537
- ### 9.5 Sprint/release format policy
538
-
539
- `settings.validation.sprint_release_format` controls `--sprint` and `--release` behavior for `create`/`update`:
540
-
541
- - `warn` (default): accept non-conforming values and emit deterministic warnings.
542
- - `strict_error`: reject non-conforming values with deterministic usage errors.
543
-
544
- Conforming value pattern: `^[A-Za-z0-9][A-Za-z0-9._/-]*$` (max 64 characters, no spaces).
545
-
546
- ### 9.6 Metadata validation profile policy
547
-
548
- `settings.validation.metadata_profile` controls default required-field behavior for `pm validate --check-metadata`:
549
-
550
- - `core` (default): baseline required fields (`author`, `acceptance_criteria`, `estimated_minutes`, and `close_reason` for closed items).
551
- - `strict`: extends core with additional governance fields (`reviewer`, `risk`, `confidence`, `sprint`, `release`).
552
- - `custom`: uses `settings.validation.metadata_required_fields` as the required field set.
553
-
554
- `settings.validation.metadata_required_fields` accepts deterministic required-field selectors:
555
-
556
- - `author`
557
- - `acceptance_criteria`
558
- - `estimated_minutes`
559
- - `close_reason`
560
- - `reviewer`
561
- - `risk`
562
- - `confidence`
563
- - `sprint`
564
- - `release`
565
-
566
- If `metadata_profile=custom` and `metadata_required_fields` is empty, runtime falls back to core required fields and emits warning `validate_metadata_custom_profile_missing_required_fields:0`.
567
-
568
- `pm validate --metadata-profile <core|strict|custom>` can override configured profile per invocation.
569
-
570
- ### 9.7 Test-result tracking policy
571
-
572
- `settings.testing.record_results_to_items` controls whether linked-test executions append bounded `test_runs` summaries to item front matter:
573
-
574
- - `false` (default): command output only; no item mutation for run summaries.
575
- - `true`: `pm test --run` and `pm test-all` append deterministic summary entries (`run_id`, `kind`, `status`, counts, timestamps) with bounded retention.
576
-
577
- Background executions (`--background`) reuse the same run pipeline and therefore follow the same policy gate.
578
-
579
- ## 10) Concurrency, Claiming, Locking, Safe Writes
580
-
581
- ### 10.1 Assignee identity
582
-
583
- - If `--author` is provided for a mutating command, that value is the active assignee identity.
584
- - Else if `PM_AUTHOR` is set, use it.
585
- - Else use `settings.author_default`.
586
- - Else fallback to `"unknown"`.
587
-
588
- ### 10.2 Lock file format
589
-
590
- Path: `.agents/pm/locks/<id>.lock`
591
-
592
- ```json
593
- {
594
- "id": "pm-a1b2",
595
- "pid": 12345,
596
- "owner": "maintainer-agent",
597
- "created_at": "2026-02-17T11:15:03.120Z",
598
- "ttl_seconds": 1800
599
- }
600
- ```
601
-
602
- ### 10.3 Lock behavior
603
-
604
- - Acquire lock via exclusive open.
605
- - If lock exists and not stale -> conflict exit code `4`.
606
- - If stale:
607
- - without `--force`: conflict with stale-lock hint
608
- - with `--force`: steal lock and continue
609
-
610
- ### 10.4 Atomic write contract
611
-
612
- For any mutation:
613
-
614
- 1. Acquire lock.
615
- 2. Read current item.
616
- 3. Compute `before_hash`.
617
- 4. Apply mutation to in-memory canonical model.
618
- 5. Update `updated_at`.
619
- 6. Compute patch and `after_hash`.
620
- 7. Write item to temp file in same filesystem.
621
- 8. `rename` temp -> target (atomic replace).
622
- 9. Append history line atomically.
623
- 10. Release lock.
624
-
625
- If any step fails, return non-zero exit code and preserve prior item bytes.
626
-
627
- ## 11) Command Surface and Exit Codes
628
-
629
- ### 11.1 Global flags (all commands)
630
-
631
- - `--json` output JSON instead of TOON
632
- - `--quiet` suppress stdout
633
- - `--path <dir>` override project root path for invocation
634
- - `--no-extensions` disable extension loading
635
- - `--explain` render extended rationale/examples in help output
636
- - `--profile` print deterministic timing diagnostics (stderr)
637
- - `--version` print CLI version
638
-
639
- Default output note:
640
-
641
- - Core default remains TOON.
642
- - Default TOON output renders command payloads directly and applies sparse compaction (omit `null`/`undefined`/empty arrays/empty objects).
643
- - `pm calendar` is a deliberate exception and defaults to markdown unless explicitly overridden by `--format` or `--json`.
644
- - Runtime output is terminal-neutral plain text (TOON/JSON/markdown) with no required terminal-specific OSC/ANSI control protocol.
645
- - Error handling should preserve exit-code mapping while preferring graceful process termination semantics (`process.exitCode`) over forced synchronous exits when feasible.
646
- - Linked test execution should prefer spawn-based shell-compatible orchestration over buffered one-shot capture, so long runs remain observable in emulated terminals.
647
- - Interactive linked test runs should emit deterministic stderr heartbeat progress events while commands are still running.
648
- - Long-running command paths that support explicit progress controls (`pm test`, `pm test-all`, `pm reindex`) should expose additive `--progress` behavior for non-interactive runs without changing default output contracts.
649
-
650
- Help and error UX note:
651
-
652
- - Command help should default to compact token-efficient guidance (`Intent` + one high-signal example) and support an explicit deep-help surface via `--explain`.
653
- - `pm help` and `pm help <command>` should remain deterministic success flows for known command paths; unavailable-command help requests should emit explicit `unknown command '<name>'` guidance with usage exit semantics.
654
- - `--help --json` should emit machine-readable help payloads instead of text help.
655
- - Usage and runtime errors should be rendered from one canonical guidance model:
656
- - text mode: structured sections for what happened, what is required, why, examples, and optional next steps
657
- - `--json` mode: machine-readable envelope (`type`, `code`, `title`, `detail`, `required`, `exit_code`, optional `why/examples/next_steps`)
658
-
659
- ### 11.2 Exit codes
660
-
661
- - `0` success
662
- - `1` generic failure
663
- - `2` usage / invalid args
664
- - `3` not found
665
- - `4` conflict (claim/lock/ownership)
666
- - `5` dependency failed (for orchestration failures, `pm test-all`, and `pm test --run` when linked test run results fail)
667
-
668
- ### 11.3 Core commands (required for v0.1 release-ready scope)
669
-
670
- - `pm init [<PREFIX>]`
671
- - `pm extension [target] --install|--uninstall|--explore|--manage|--doctor|--adopt|--activate|--deactivate [--project|--local|--global] [--gh|--github <owner/repo[/path]>] [--ref <ref>]`
672
- - `pm list`
673
- - `pm list-all`
674
- - `pm list-draft`
675
- - `pm list-open`
676
- - `pm list-in-progress`
677
- - `pm list-blocked`
678
- - `pm list-closed`
679
- - `pm list-canceled`
680
- - `pm aggregate`
681
- - `pm dedupe-audit`
682
- - `pm get <ID>`
683
- - `pm search <keywords>`
684
- - `pm reindex`
685
- - `pm calendar` (alias: `pm cal`)
686
- - `pm context` (alias: `pm ctx`)
687
- - `pm create`
688
- - `pm templates save <NAME>`
689
- - `pm templates list`
690
- - `pm templates show <NAME>`
691
- - `pm update <ID>`
692
- - `pm update-many`
693
- - `pm append <ID>`
694
- - `pm claim <ID>`
695
- - `pm release <ID>`
696
- - `pm start-task <ID>`
697
- - `pm pause-task <ID>`
698
- - `pm close-task <ID> <TEXT>`
699
- - `pm delete <ID>`
700
- - `pm comments <ID> [TEXT]`
701
- - `pm comments-audit`
702
- - `pm notes <ID> [TEXT]`
703
- - `pm learnings <ID> [TEXT]`
704
- - `pm files <ID>`
705
- - `pm docs <ID>`
706
- - `pm deps <ID>`
707
- - `pm test <ID>`
708
- - `pm test-all`
709
- - `pm test-runs <list|status|logs|stop|resume>`
710
- - `pm stats`
711
- - `pm health`
712
- - `pm validate`
713
- - `pm gc`
714
- - `pm history <ID>`
715
- - `pm activity`
716
- - `pm restore <ID> <TIMESTAMP|VERSION>`
717
- - `pm config <project|global> set definition-of-done --criterion <text>`
718
- - `pm config <project|global> get definition-of-done`
719
- - `pm config <project|global> set item-format --format toon`
720
- - `pm config <project|global> get item-format`
721
- - `pm config <project|global> set history-missing-stream-policy --policy auto_create|strict_error`
722
- - `pm config <project|global> get history-missing-stream-policy`
723
- - `pm config <project|global> set sprint-release-format-policy --policy warn|strict_error`
724
- - `pm config <project|global> get sprint-release-format-policy`
725
- - `pm config <project|global> set parent-reference-policy --policy warn|strict_error`
726
- - `pm config <project|global> get parent-reference-policy`
727
- - `pm config <project|global> set test-result-tracking --policy enabled|disabled`
728
- - `pm config <project|global> get test-result-tracking`
729
- - `pm config <project|global> list`
730
- - `pm config <project|global> export`
731
- - `pm close <ID> <TEXT>`
732
- - `pm beads import [--file <path>]`
733
- - `pm todos import [--folder <path>]`
734
- - `pm todos export [--folder <path>]`
735
- - `pm completion <bash|zsh|fish>`
736
- - `pm completion-tags` (internal helper command used by generated completion scripts)
737
-
738
- Roadmap commands (post-v0.1, tracked but not release blockers):
739
-
740
- - No additional command-path roadmap entries are currently defined.
741
-
742
- ### 11.4 Extended flags (minimum)
743
-
744
- Mutating `create` (all schema fields MUST be passable explicitly):
745
-
746
- - `--title`, `-t` (required)
747
- - `--description`, `-d` (required; empty string allowed when explicitly passed)
748
- - `--type` (required; allowed values are resolved from the runtime item-type registry: built-ins + `settings.item_types.definitions` + extension registrations)
749
- - `--create-mode`, `--create_mode` (optional; `strict` default, or `progressive` for staged creation that relaxes type-level required create fields/repeatables)
750
- - `--status`, `-s` (required in strict mode; defaults to `open` in progressive mode when omitted)
751
- - `--priority`, `-p` (required in strict mode; defaults to `2` in progressive mode when omitted)
752
- - `--tags` (required in strict mode; defaults to empty list in progressive mode when omitted)
753
- - `--body`, `-b` (required in strict mode; defaults to empty body in progressive mode when omitted)
754
- - `--deadline` (explicit; accepts ISO/date strings or relative `+6h/+1d/+2w/+6m`)
755
- - `--estimate`, `--estimated-minutes`, `--estimated_minutes` (explicit; accepts `0`)
756
- - `--acceptance-criteria`, `--acceptance_criteria`, `--ac` (explicit; empty allowed)
757
- - `--author` (explicit; fallback `PM_AUTHOR`/settings allowed)
758
- - `--message` (explicit history message; empty allowed)
759
- - `--template` (optional; reusable defaults loaded from `pm templates save <NAME>`)
760
- - `--assignee` (explicit; clear with `--unset assignee`)
761
- - `--parent` (optional; item ID reference; clear with `--unset parent`; missing-parent behavior controlled by `settings.validation.parent_reference`)
762
- - `--reviewer` (optional; clear with `--unset reviewer`)
763
- - `--risk` (optional; `low|med|medium|high|critical`; clear with `--unset risk`; `med` persists as `medium`)
764
- - `--confidence` (optional; `0..100|low|med|medium|high`; clear with `--unset confidence`; `med` persists as `medium`)
765
- - `--sprint` (optional; clear with `--unset sprint`; format policy controlled by `settings.validation.sprint_release_format`)
766
- - `--release` (optional; clear with `--unset release`; format policy controlled by `settings.validation.sprint_release_format`)
767
- - `--blocked-by`, `--blocked_by` (optional; item ID or free-text; clear with `--unset blocked-by`)
768
- - `--blocked-reason`, `--blocked_reason` (optional; clear with `--unset blocked-reason`)
769
- - `--unblock-note`, `--unblock_note` (optional; unblock rationale note; clear with `--unset unblock-note`)
770
- - `--reporter` (optional; issue reporter; clear with `--unset reporter`)
771
- - `--severity` (optional; `low|med|medium|high|critical`; clear with `--unset severity`; `med` persists as `medium`)
772
- - `--environment` (optional; issue environment context; clear with `--unset environment`)
773
- - `--repro-steps`, `--repro_steps` (optional; issue reproduction steps; clear with `--unset repro-steps`)
774
- - `--resolution` (optional; issue resolution summary; clear with `--unset resolution`)
775
- - `--expected-result`, `--expected_result` (optional; issue expected behavior; clear with `--unset expected-result`)
776
- - `--actual-result`, `--actual_result` (optional; issue observed behavior; clear with `--unset actual-result`)
777
- - `--affected-version`, `--affected_version` (optional; impacted version identifier; clear with `--unset affected-version`)
778
- - `--fixed-version`, `--fixed_version` (optional; fixed version identifier; clear with `--unset fixed-version`)
779
- - `--component` (optional; owning component; clear with `--unset component`)
780
- - `--regression` (optional; boolean `true|false|1|0`; clear with `--unset regression`)
781
- - `--customer-impact`, `--customer_impact` (optional; customer impact summary; clear with `--unset customer-impact`)
782
- - `--definition-of-ready`, `--definition_of_ready` (optional; explicit empty allowed; clear with `--unset definition-of-ready`)
783
- - `--order`, `--rank` (optional; integer rank/order; clear with `--unset order`)
784
- - `--goal` (optional; clear with `--unset goal`)
785
- - `--objective` (optional; clear with `--unset objective`)
786
- - `--value` (optional; clear with `--unset value`)
787
- - `--impact` (optional; clear with `--unset impact`)
788
- - `--outcome` (optional; clear with `--unset outcome`)
789
- - `--why-now`, `--why_now` (optional; clear with `--unset why-now`)
790
-
791
- Mutating `create` flags (repeatable; strict mode may require each at least once depending on type policy, while progressive mode allows staged omission; clear with explicit `--clear-*` flags):
792
-
793
- - `--dep` value format: `id=<id>,kind=<blocks|parent|child|parent_child|child_of|related|related_to|discovered_from|blocked_by|incident_from|epic|supersedes|task>,author=<a>,created_at=<iso|now>,source_kind=<value?>` (also accepts markdown `key: value` lines and stdin token `-`)
794
- - `--comment` value format: `author=<a>,created_at=<iso|now>,text=<t>` (also accepts markdown `key: value` lines and stdin token `-`)
795
- - `--note` value format: `author=<a>,created_at=<iso|now>,text=<t>` (also accepts markdown `key: value` lines and stdin token `-`)
796
- - `--learning` value format: `author=<a>,created_at=<iso|now>,text=<t>` (also accepts markdown `key: value` lines and stdin token `-`)
797
- - Log-seed repeatables (`--comment`/`--note`/`--learning`) accept only `author`, `created_at`, and `text` keys. Parsed extra keys are rejected with usage guidance to avoid silent truncation when unquoted comma segments resemble key/value pairs; quoted text (`text="hello,scope:project"`), markdown key/value input, and stdin token `-` remain supported.
798
- - `--file` value format: `path=<p>,scope=<project|global>,note=<n?>` (also accepts markdown `key: value` lines and stdin token `-`)
799
- - `--test` value format: `command=<c>,path=<p?>,scope=<project|global>,timeout_seconds=<n?>,env_set=<KEY=VALUE;...?>,env_clear=<KEY;...?>,shared_host_safe=<bool?>,note=<n?>` (also accepts markdown `key: value` lines and stdin token `-`; `command` is required and `path` is optional metadata)
800
- - `--doc` value format: `path=<p>,scope=<project|global>,note=<n?>` (also accepts markdown `key: value` lines and stdin token `-`)
801
- - `--reminder` value format: `at=<iso|date|relative>,text=<text>` (also accepts markdown `key: value` lines and stdin token `-`; use `--clear-reminders` to clear)
802
- - `--type-option` / `--type_option` value format: `key=value`, `key:value`, or `key=<name>,value=<value>` (also accepts markdown `key: value` lines and stdin token `-`; use `--clear-type-options` to clear)
803
-
804
- Per-type option policy overrides (`settings.item_types.definitions[]` and extension `registerItemTypes(...)`):
805
-
806
- - `command_option_policies[].command`: `create` or `update`
807
- - `command_option_policies[].option`: canonical option key (for example `message`, `severity`, `typeOption`)
808
- - `required: true|false`: mark option mandatory/optional for the targeted command and type
809
- - `enabled: true|false`: reject/allow the option at runtime for the targeted command and type
810
- - `visible: true|false`: show/hide the option in policy-aware help guidance
811
-
812
- Help and error guidance:
813
-
814
- - `pm create --help` / `pm update --help` accept `--type <value>` to render policy-aware required/disabled/hidden option summaries plus type-option schema details (required marker, allowed values, aliases, description).
815
- - Missing `--type` usage errors include rationale, active allowed values, and custom-type examples.
816
- - Type-governed create validation aggregates all missing required create options and required `--type-option` keys into one deterministic usage error payload (stable flag ordering) instead of iterative one-at-a-time failures.
817
- - Aggregated create/type-option validation guidance includes a deterministic type-specific "next valid example" command for one-shot remediation.
818
- - Commander usage errors are normalized into a single structured guidance payload (duplicate default commander stderr messaging is not emitted).
819
- - Runtime `PmCliError` paths should surface structured guidance while preserving canonical exit-code mapping, with machine-readable JSON error envelopes when `--json` is active.
820
-
821
- Mutating `update` (v0.1 baseline):
822
-
823
- - `--title`, `-t`
824
- - `--description`, `-d`
825
- - `--body`, `-b` (explicit empty string allowed; use `pm append --body` for additive narrative updates)
826
- - `--status`, `-s`
827
- - `--priority`, `-p`
828
- - `--type`
829
- - `--tags`
830
- - `--deadline`
831
- - `--estimate`, `--estimated-minutes`, `--estimated_minutes`
832
- - `--acceptance-criteria`, `--acceptance_criteria`, `--ac`
833
- - `--assignee`
834
- - `--parent` (missing-parent behavior controlled by `settings.validation.parent_reference`)
835
- - `--reviewer`
836
- - `--risk` (`low|med|medium|high|critical`; `med` persists as `medium`)
837
- - `--confidence` (`0..100|low|med|medium|high`; `med` persists as `medium`)
838
- - `--sprint` (format policy controlled by `settings.validation.sprint_release_format`)
839
- - `--release` (format policy controlled by `settings.validation.sprint_release_format`)
840
- - `--blocked-by`, `--blocked_by`
841
- - `--blocked-reason`, `--blocked_reason`
842
- - `--unblock-note`, `--unblock_note`
843
- - `--reporter`
844
- - `--severity` (`low|med|medium|high|critical`; `med` persists as `medium`)
845
- - `--environment`
846
- - `--repro-steps`, `--repro_steps`
847
- - `--resolution`
848
- - `--expected-result`, `--expected_result`
849
- - `--actual-result`, `--actual_result`
850
- - `--affected-version`, `--affected_version`
851
- - `--fixed-version`, `--fixed_version`
852
- - `--component`
853
- - `--regression` (`true|false|1|0`)
854
- - `--customer-impact`, `--customer_impact`
855
- - `--definition-of-ready`, `--definition_of_ready`
856
- - `--order`, `--rank`
857
- - `--goal`
858
- - `--objective`
859
- - `--value`
860
- - `--impact`
861
- - `--outcome`
862
- - `--why-now`, `--why_now`
863
- - `--author`
864
- - `--message`
865
- - `--allow-audit-update`, `--allow_audit_update` (ownership-safe non-owner metadata update mode; intentionally disallows lifecycle/ownership/linkage field mutations in this mode)
866
- - `--allow-audit-dep-update`, `--allow_audit_dep_update` (ownership-safe non-owner append-only dependency update mode; requires at least one `--dep` and is mutually exclusive with `--allow-audit-update`)
867
- - `--dep` (repeatable add format `id=<id>,kind=<...>,author=<a?>,created_at=<iso|now>,source_kind=<value?>`; use `--clear-deps` to clear all dependencies)
868
- - `--dep-remove`, `--dep_remove` (repeatable selector remove by `id` or `id=<id>,kind=<kind?>,source_kind=<value?>`)
869
- - `--comment` (repeatable log seed; supports plain-text shorthand for comment text, or structured `author=<a>,created_at=<iso|now>,text=<t>`; use `--clear-comments` to clear comments)
870
- - `--note` (repeatable log seed format `author=<a>,created_at=<iso|now>,text=<t>`; use `--clear-notes` to clear notes)
871
- - `--learning` (repeatable log seed format `author=<a>,created_at=<iso|now>,text=<t>`; use `--clear-learnings` to clear learnings)
872
- - `--file` (repeatable linked-file format `path=<p>,scope=<project|global>,note=<n?>`; use `--clear-files` to clear files)
873
- - `--test` (repeatable linked-test format `command=<c>,path=<p?>,scope=<project|global>,timeout_seconds=<n?>,...`; use `--clear-tests` to clear tests)
874
- - `--doc` (repeatable linked-doc format `path=<p>,scope=<project|global>,note=<n?>`; use `--clear-docs` to clear docs)
875
- - `--reminder` (repeatable `at=<iso|date|relative>,text=<text>`; use `--clear-reminders` to clear)
876
- - `--event` (repeatable event metadata format; use `--clear-events` to clear)
877
- - `--type-option`, `--type_option` (repeatable type option metadata; use `--clear-type-options` to clear)
878
-
879
- `pm update` status semantics:
880
-
881
- - `--status` supports all non-terminal values plus `canceled`.
882
- - `--status closed` is not supported; callers must use `pm close <ID> <TEXT>` so `close_reason` is always captured.
883
- - `--close-reason`, `--close_reason` support explicit close-reason set; clear with `--unset close-reason`.
884
- - Reopen transition safety: moving from `closed` to a non-terminal status via `--status` auto-clears stale `close_reason` unless `--close-reason` is explicitly provided on that update call.
885
-
886
- `pm update-many` (bulk mutation with native checkpoint lifecycle):
887
-
888
- - Targeting filters: `--filter-status`, `--filter-type`, `--filter-tag`, `--filter-priority`, `--filter-parent`, `--filter-deadline-before`, `--filter-deadline-after`, `--filter-assignee`, `--filter-assignee-filter`, `--filter-sprint`, `--filter-release`
889
- - Paging scope controls: `--limit`, `--offset`
890
- - Apply payload: `pm update` mutation parity, including scalar/unset metadata flags plus linked-array repeatables (`--dep`, `--comment`, `--note`, `--learning`, `--file`, `--test`, `--doc`, `--reminder`, `--event`), explicit `--clear-*` controls, and atomic replacement flags (`--replace-deps`, `--replace-tests`)
891
- - Workflow controls:
892
- - `--dry-run` (preview planned per-item changes without mutation)
893
- - `--rollback <checkpoint-id>` (restore a prior checkpoint snapshot)
894
- - `--no-checkpoint` (disable apply-mode checkpoint capture)
895
- - Safety rule: `--rollback` is exclusive with mutation payload flags.
896
-
897
- List/search filters:
898
-
899
- - `--type`
900
- - `--tag`
901
- - `--priority`
902
- - `--parent` (`list*` commands; exact match on parent item ID for hierarchical scoping)
903
- - `--limit`
904
- - `--offset` (`list*` commands; apply offset before limit for deterministic pagination)
905
- - `--stream` (`list*` commands; JSON-only newline-delimited item streaming)
906
- - `--deadline-before`
907
- - `--deadline-after`
908
- - `--assignee` (exact match on `assignee` field)
909
- - `--assignee-filter assigned|unassigned` (assignee presence filter)
910
- - `--sprint` (exact match on `sprint` field)
911
- - `--release` (exact match on `release` field)
912
- - `--include-body` (list* only; when enabled, each returned item includes `body`; default list rows remain front-matter-only)
913
- - `--compact` / `--fields <csv>` (`list*` projection controls; mutually exclusive)
914
- - `--sort <priority|deadline|updated_at|created_at|title|parent>` + `--order <asc|desc>` (`list*` deterministic sort controls; `--order` requires `--sort`)
915
- - `--compact` / `--full` / `--fields <csv>` (`search` only; mutually exclusive projection controls, default compact)
916
-
917
- Command-specific query and contract flags:
918
-
919
- - `pm comments-audit`: `--latest <n>` (`0` allowed for summary-only rows), `--full-history` (mutually exclusive with `--latest`)
920
- - `pm calendar`: `--full-period` (day/week/month full anchored window; invalid for agenda)
921
- - `pm activity`: `--id`, `--op`, `--author`, `--from`, `--to`, `--stream [rows|ndjson|jsonl]` (`--stream` requires `--json`)
922
- - `pm contracts`: `--flags-only`, `--availability-only` (mutually exclusive), command-scoped default behavior when `--command` is provided
923
- - `pm contracts`: command flag/alias payloads include both canonical flag rows and commander alias metadata (`command_flags` + `commander_aliases`) for machine consumers
924
- - `pm completion`: `--eager-tags` (legacy eager embedding; default generated scripts use lazy runtime tag lookup via `pm completion-tags`)
925
-
926
- Mutation safety:
927
-
928
- - `--author`
929
- - `--message`
930
- - `--force`
931
-
932
- ### 11.5 Command input/output contracts
933
-
934
- All commands return deterministic top-level objects (TOON by default, JSON with `--json`).
935
-
936
- Canonical command/action schema metadata is centralized in `src/sdk/cli-contracts.ts` and reused across:
937
-
938
- - commander normalization in `src/cli/main.ts`
939
- - shell completion generation in `src/cli/commands/completion.ts`
940
- - Pi wrapper `inputSchema` + action mapping in `.pi/extensions/pm-cli/index.ts`
941
-
942
- Contract compatibility policy keeps command names/flags/aliases stable while allowing stricter machine contracts:
943
-
944
- - Existing CLI command paths and aliases remain valid.
945
- - Pi tool input validation uses strict action-scoped schema branches (schema v4) with per-action required fields and `additionalProperties: false`.
946
- - `pm contracts` provides deterministic runtime contract introspection (`--action`, `--command`, `--schema-only`, `--runtime-only`, `--active-only`) for agent callers, including action availability metadata (`action_availability`) with invocability/provider diagnostics and additive extension command/action schema inclusion (`extension_commands`).
947
- - Intentional compatibility exception: `pm contracts --command <name>` now narrows command/action/availability output to that selected command by default to reduce machine payload noise (omit `--command` for full corpus output).
948
- - `pm contracts --flags-only` and `pm contracts --availability-only` provide mutually exclusive lightweight projections for machine consumers.
949
- - Shell completion generation is sourced from the same normalized contract registry so parser/runtime/contracts/completion flag parity remains deterministic (including alias candidates).
950
- - Command output remains deterministic; `--json` exposes command-contract machine payloads and JSON error envelopes.
951
-
952
- | Command | Key inputs | Output object |
953
- | --- | --- | --- |
954
- | `pm init [PREFIX]` | optional prefix, `--path` | `{ ok, path, settings, created_dirs, warnings }` |
955
- | `pm extension [target] --install\|--uninstall\|--explore\|--manage\|--doctor\|--adopt\|--adopt-all\|--activate\|--deactivate` | exactly one lifecycle action, optional `target` (required for install/uninstall/activate/deactivate/adopt; disallowed for `adopt-all`; `doctor` supports `pm extension doctor` target syntax), scope flags (`--project` default, `--local` alias, `--global`), install/adopt source selectors (`target`, `--gh`, `--github`, optional `--ref`), bundled aliases (`beads`, `todos`), doctor detail mode (`--detail summary\|deep`), doctor trace mode (`--trace` with deep detail), manage runtime parity probe (`--runtime-probe`), optional managed-state remediation (`--fix-managed-state`), doctor strict warning exits (`--strict-exit`, alias `--fail-on-warn`); top-level action and subcommand forms share the same flag forwarding (`pm extension --doctor ...` == `pm extension doctor ...`) | `{ ok, action, scope, roots, warnings, details }` where `details` is action-specific: `explore/manage` include per-extension state/status fields (`active` compatibility alias, `enabled`, `runtime_active`, `activation_status`) plus `update_check_status`/`update_check_reason`; `manage` includes optional `runtime_probe` and `managed_state_fix` metadata; `manage`/`doctor` include triage rollups (`warning_codes`, `update_health_coverage`, `update_health_partial`, `update_check_status_totals`, remediation) with top-level warning parity; doctor summary includes blocking-failure indicators (`blocking_failure_count`, `has_blocking_failures`), trace status (`trace_enabled`), and capability metadata (`capability_contract_version`/`capability_guidance`/`capability_contract`); `adopt` reports adoption result/provenance (`adopted`, `already_managed?`, `source`) without reinstalling extension files; `adopt-all` reports bulk adoption totals and per-extension adoption rows |
956
- | `pm list` | optional filter flags (including `--parent`, `--include-body`, `--compact`, `--fields`, `--sort`, `--order`, `--offset`, and JSON-only `--stream`); excludes terminal statuses (`closed`, `canceled`) by default | `{ items, count, filters, projection, sorting, now }` (or streamed newline-delimited rows when `--json --stream`) |
957
- | `pm list-all` | optional filter flags (including `--parent`, `--include-body`, `--compact`, `--fields`, `--sort`, `--order`, `--offset`, and JSON-only `--stream`); includes all statuses including terminal | `{ items, count, filters, projection, sorting, now }` (or streamed newline-delimited rows when `--json --stream`) |
958
- | `pm list-draft` | optional type/tag/priority/parent/deadline/assignee/sprint/release/include-body/compact/fields/sort/order/offset filters plus JSON-only stream mode | `{ items, count, filters, projection, sorting, now }` (or streamed newline-delimited rows when `--json --stream`) |
959
- | `pm list-open` | optional type/tag/priority/parent/deadline/assignee/sprint/release/include-body/compact/fields/sort/order/offset filters plus JSON-only stream mode | `{ items, count, filters, projection, sorting, now }` (or streamed newline-delimited rows when `--json --stream`) |
960
- | `pm list-in-progress` | same as above | `{ items, count, filters, now }` |
961
- | `pm list-blocked` | same as above | `{ items, count, filters, now }` |
962
- | `pm list-closed` | same as above | `{ items, count, filters, now }` |
963
- | `pm list-canceled` | same as above | `{ items, count, filters, now }` |
964
- | `pm aggregate` | grouped-count governance query (`--group-by parent,type` default; supported dimensions: `parent,type,priority,status,assignee,tags,sprint,release`; `--count` accepted for explicit parity) with list-like filters and optional `--include-unparented` | `{ groups, count, totals, filters, now, warnings? }` |
965
- | `pm dedupe-audit` | duplicate-audit query with `--mode title_exact|title_fuzzy|parent_scope`, optional `--threshold`, optional `--limit`, and list-like filters | `{ clusters, count, mode, filters, now, warnings? }` |
966
- | `pm get <ID>` | normalized id | `{ item, body, linked: { files, tests, docs }, claim_state }` where `claim_state` includes current assignee plus latest claim/release history context |
967
- | `pm search <keywords...>` | keyword query tokens + optional mode/include-linked/compact/full/fields/limit filters | `{ query, mode, items, count, filters, projection, now }` |
968
- | `pm reindex` | optional `--mode` (`keyword|semantic|hybrid` baseline) and additive `--progress` stderr visibility | `{ ok, mode, total_items, artifacts, warnings, generated_at }` |
969
- | `pm calendar` / `pm cal` | `--view agenda|day|week|month`, `--date`, `--from`/`--to` (agenda), `--past`, `--full-period` (day/week/month only), list-like filters (`type`, `tag`, `priority`, `status`, `assignee`, `sprint`, `release`, `limit`), source controls (`--include`), and recurrence bounds (`--recurrence-lookahead-days`, `--recurrence-lookback-days`, `--occurrence-limit`) | `{ view, output_default, now, anchor, range, filters, summary, events, days }` where `range` includes period metadata (`period_start`, `period_end`, `full_period`), `summary` includes deterministic aggregate breakdown fields (`by_kind`, `by_type`, `by_status`, `recurring_events`), and markdown output includes rich event detail tokens by default |
970
- | `pm context` / `pm ctx` | `--date`, `--from`/`--to`, `--past`, list-like filters (`type`, `tag`, `priority`, `assignee`, `sprint`, `release`, `limit`), `--format` | `{ output_default, now, window, filters, summary, high_level, low_level, blocked_fallback, agenda }` (defaults to TOON unless `--format` or `--json` override) |
971
- | `pm beads import [--file <path\|->] [--preserve-source-ids]` | optional Beads JSONL source path (`.beads/issues.jsonl` auto-discovered first, then `issues.jsonl`; implicit `sync_base.jsonl` fallback is refused as unsafe; `--file -` requires piped stdin and fails fast on interactive TTY stdin) | `{ ok, source, imported, skipped, ids, warnings }` |
972
- | `pm todos import --folder <path?>` | optional todos markdown source folder (defaults to `.pi/todos`); preserves canonical optional `ItemMetadata` fields when present and applies deterministic defaults for missing PM fields | `{ ok, folder, imported, skipped, ids, warnings }` |
973
- | `pm todos export --folder <path?>` | optional todos markdown destination folder (defaults to `.pi/todos`) | `{ ok, folder, exported, ids, warnings }` |
974
- | `pm create ...` | required `--title` + `--description` + `--type`; strict mode is default (`--create-mode strict`) and enforces type-governed required options; progressive mode (`--create-mode progressive`) supports staged omission of type-level required create fields/repeatables; optional `--template` reusable defaults | `{ item, changed_fields, warnings }` |
975
- | `pm templates save <NAME> ...` | template name + create-compatible option payload (subset of create flags, including repeatable entries) | `{ name, path, template, saved_at }` |
976
- | `pm templates list` | optional output controls (`--json`/TOON) | `{ templates, count }` |
977
- | `pm templates show <NAME>` | template name | `{ name, template }` |
978
- | `pm update <ID> ...` | id + patch-like flags (`--status closed` is rejected; use `pm close <ID> <TEXT>`; `--close-reason`/`--close_reason` explicitly set `close_reason`; `--unset close-reason` clears it; reopening from `closed` to a non-terminal status auto-clears stale `close_reason` unless explicit `--close-reason` is provided; body replacement is supported via `--body`/`-b`; dependencies are mutable via `--dep` / `--dep-remove` / `--clear-deps` / `--replace-deps` (atomic replacement mode); repeatable transactional linked/log flags `--comment`/`--note`/`--learning`/`--file`/`--test`/`--doc` are supported on update with explicit `--clear-*` semantics; `--allow-audit-update` enables ownership-safe non-owner metadata updates only; `--allow-audit-dep-update` enables append-only non-owner dependency additions via `--dep`) | `{ item, changed_fields, warnings, audit_update? }` |
979
- | `pm update-many` | bulk update orchestration with selection filters (`--filter-*` family), update payload parity with scalar + linked-array update flags (`--dep/--comment/--note/--learning/--file/--test/--doc/--reminder/--event`, `--clear-*`, `--replace-deps`, `--replace-tests`), and workflow controls (`--dry-run`, `--rollback <checkpoint-id>`, `--no-checkpoint`) | apply: `{ mode, matched_count, dry_run, ids, updated_count, skipped_count, failed_count, checkpoint?, rows }`; dry-run: `{ mode, matched_count, dry_run, ids, filters, planned_update_options, item_plans }` where `item_plans[].changes` includes linked-array mutation intent summaries; rollback: `{ mode, matched_count, dry_run, ids, rollback_checkpoint_id, restored_count, failed_count, rows }` |
980
- | `pm delete <ID>` | id + optional `--author`/`--message`/`--force` | `{ item, changed_fields, warnings }` |
981
- | `pm close <ID> <TEXT>` | id + close reason text + optional `--author/--message/--force/--validate-close [warn|strict]` | `{ item, changed_fields, warnings }` |
982
- | `pm append <ID> --body` | id + appended markdown (`--body -` reads piped stdin) | `{ item, appended, changed_fields }` |
983
- | `pm claim <ID>` | id, optional `--author`/`--message`/`--force` (`--force` required for terminal/lock override paths; non-terminal assignee takeover does not require force) | `{ item, claimed_by, previous_assignee, forced }` |
984
- | `pm release <ID>` | id, optional `--author`/`--message`/`--allow-audit-release`/`--force` | `{ item, released_by, previous_assignee, audit_release, forced }` |
985
- | `pm start-task <ID>` | lifecycle alias command (`claim` + `update --status in_progress`) with optional `--author`, `--message`, `--force` | `{ id, action: "start_task", claim, update }` |
986
- | `pm pause-task <ID>` | lifecycle alias command (`update --status open` + `release`) with optional `--author`, `--message`, `--force` | `{ id, action: "pause_task", update, release }` |
987
- | `pm close-task <ID> <TEXT>` | lifecycle alias command (`close` + `release`) with optional `--author`, `--message`, `--validate-close`, `--force` | `{ id, action: "close_task", close, release }` |
988
- | `pm comments <ID> [TEXT] --add/--stdin/--file/--limit` | id + optional positional comment text shorthand + comment text/limit (`--add` accepts plain text, `text=<value>`, markdown `text: <value>`, or stdin token `-`; `--stdin` reads multiline markdown from piped stdin; `--file <path>` reads multiline markdown from a file; positional `TEXT` is shorthand for `--add <TEXT>`; ambiguous CSV-like key fragments such as `text=hello,scope:project` remain plain text unless `text` is explicit); exactly one comment source must be provided per mutation invocation (`[TEXT]`, `--add`, `--stdin`, or `--file`); optional mutation metadata flags `--author`/`--message`/`--force`; additive ownership-safe audit path `--allow-audit-comment` for non-owner append-only comments | `{ id, comments, count }` |
989
- | `pm comments-audit` | optional governance filters (`--status`, `--type`, `--assignee`, `--assignee-filter`, `--parent`, `--tag`, `--sprint`, `--release`, `--priority`, `--limit-items`) plus latest/full-history export mode controls (`--latest`, `--full-history`; mutually exclusive, `--latest 0` allowed for summary-only rows) | `{ items, count, summary, filters, export, now, warnings? }` where `summary` includes additive totals/coverage/by-type metrics, `filters.full_history` and `export.mode` indicate latest vs full-history behavior, and `export.row_count` is deterministic (`0` in summary-only latest mode); in full-history mode, `rows[]` includes flat per-comment export entries for NDJSON-friendly downstream processing |
990
- | `pm notes <ID> [TEXT] --add/--limit` | id + optional positional note text shorthand + note text/limit (`--add` accepts plain text, `text=<value>`, markdown `text: <value>`, or stdin token `-`; positional `TEXT` is shorthand for `--add <TEXT>`; ambiguous CSV-like key fragments such as `text=hello,scope:project` remain plain text unless `text` is explicit); optional mutation metadata flags `--author`/`--message`/`--force`; additive ownership-safe audit path `--allow-audit-comment` for non-owner append-only notes | `{ id, notes, count }` |
991
- | `pm learnings <ID> [TEXT] --add/--limit` | id + optional positional learning text shorthand + learning text/limit (`--add` accepts plain text, `text=<value>`, markdown `text: <value>`, or stdin token `-`; positional `TEXT` is shorthand for `--add <TEXT>`; ambiguous CSV-like key fragments such as `text=hello,scope:project` remain plain text unless `text` is explicit); optional mutation metadata flags `--author`/`--message`/`--force`; additive ownership-safe audit path `--allow-audit-comment` for non-owner append-only learnings | `{ id, learnings, count }` |
992
- | `pm files <ID> --add/--add-glob/--remove/--migrate/--append-stable/--validate-paths/--audit/--list`; `pm files discover <ID> [--apply]` | id + file refs (`--add/--remove` accept CSV key/value, markdown `key: value`, or stdin token `-`); optional glob expansion via repeatable `--add-glob` (plain glob or `pattern=<glob>,scope=<scope>,note=<text>`); optional additive linked-path hygiene (`--migrate from=<old>,to=<new>`, path existence validation, cross-item audit, non-mutating list); optional `--append-stable` avoids full-array resorting and appends new links while preserving current order; `discover` scans item text for existing project/global file paths, skips already linked files, and writes missing links only with `--apply` | standard files: `{ id, files, changed, count, migrations_applied, validation, audit }`; discover: `{ id, files, changed, apply, count, candidate_count, addable_count, added_count, skipped_existing_count, candidates, added, skipped_existing }` |
993
- | `pm test <ID> --add/--remove/--run` | id + test refs/options (`--add/--remove` accept CSV key/value, markdown `key: value`, or stdin token `-`; new linked test entries must include `command=...` and may include `path=...` as optional metadata; optional linked-test runtime directives support `env_set`, `env_clear`, `shared_host_safe`, optional per-test context override `pm_context_mode=schema\|tracker\|auto`, and assertion metadata fields `assert_stdout_contains` / `assert_stdout_regex` / `assert_stderr_contains` / `assert_stderr_regex` / `assert_stdout_min_lines` / `assert_json_field_equals` / `assert_json_field_gte`; run-time supports additive `--background`, `--env-set`, `--env-clear`, `--shared-host-safe`, `--pm-context schema\|tracker\|auto`, `--check-context`, `--auto-pm-context`, `--fail-on-context-mismatch`, `--fail-on-skipped`, `--fail-on-empty-test-run`, and `--require-assertions-for-pm`; path-only add/create entries are rejected; reject recursive `test-all` linked commands at add-time, including global-flag and package-spec launcher forms such as `pm --json test-all`, `npx @unbrained/pm-cli@latest --json test-all`, `pnpm dlx @unbrained/pm-cli@latest --json test-all`, and `npm exec -- @unbrained/pm-cli@latest --json test-all`; defensively skip legacy recursive entries at run-time; reject sandbox-unsafe test-runner commands including unsandboxed direct package-manager run-script forms such as `npm run test`/`pnpm run test` and chained direct runner segments evaluated independently; linked command execution seeds sandbox project/global settings and extensions from source roots for extension/type parity, routes context per test (per-test override > run-level, `auto` routes PM tracker-read commands to tracker mode), fails PM tracker-read command mismatches by default in schema mode, emits `execution_context` metadata (including tracker-read classification plus `requested_pm_context_mode` and `auto_pm_context_applied`) in each run result, supports preflight mismatch summary warnings (`context_preflight`) when requested, supports high-confidence empty-selection detection when requested, and runs linked commands via shell-compatible spawn orchestration with deterministic timeout/maxBuffer diagnostics and structured `failure_category` classification) | foreground: `{ id, tests, run_results, failure_categories, fail_on_skipped_triggered?, warnings?, changed, count }`; background start: `{ started, duplicate_of?, run }` |
994
- | `pm test-all --status --timeout` | optional status filter plus additive run controls `--background`, `--progress`, `--env-set`/`--env-clear`/`--shared-host-safe`/`--pm-context`/`--check-context`/`--auto-pm-context`/`--fail-on-context-mismatch`/`--fail-on-skipped`/`--fail-on-empty-test-run`/`--require-assertions-for-pm` (schema-mode runs fail PM tracker-read command mismatches by default); duplicate linked command/path entries are deduped per invocation (keyed by scope+normalized command or scope+path plus runtime directives + context metadata + assertion metadata) and reported as skipped; when duplicate keys carry different `timeout_seconds`, execution uses deterministic maximum timeout for that key | foreground: `{ totals, failed, passed, skipped, fail_on_skipped_triggered?, warnings?, results }`; background start: `{ started, duplicate_of?, run }` (`totals.failure_categories` included) |
995
- | `pm test-runs [list|status|logs|stop|resume]` | list/status/log/stop/resume lifecycle control for managed background test runs; bare `pm test-runs` defaults to list output (`logs` supports `--stream stdout|stderr|both` and `--tail`; `stop` supports `--force`); run attribution resolves `requested_by` via explicit author -> `PM_AUTHOR` -> settings author default -> env user identifiers -> OS username -> `unknown` | list: `{ runs, count, filters }`; status: `{ run, health }`; logs: `{ run, stream, tail, stdout, stderr }`; stop: `{ run, signal_sent }`; resume: `{ resumed_from, run }` |
996
- | `pm stats` | none | `{ totals, by_type, by_status, generated_at }` |
997
- | `pm health` | none (runs settings/directories/extensions/storage plus integrity, history-drift, and vectorization diagnostics); supports `--strict-directories` to treat optional built-in item-type directories as required warning/failure conditions, strict warning exits (`--strict-exit`, alias `--fail-on-warn`), and vector refresh controls (`--check-only`, `--no-refresh`, `--refresh-vectors`) | `{ ok, checks, warnings, generated_at }` with extension diagnostics including condensed `details.triage`, capability guidance metadata, and directory check details (`required`, `optional`, `missing_required`, `missing_optional`) |
998
- | `pm validate` | optional scoped checks (`--check-metadata`, `--check-resolution`, `--check-lifecycle`, `--check-stale-blockers`, `--check-files`, `--check-command-references`, `--check-history-drift`; default all checks); metadata checks accept `--metadata-profile core|strict|custom`; lifecycle checks surface active closure-like metadata and active items whose parents are terminal, with optional stale blocker heuristics when `--check-stale-blockers` is enabled and settings-backed lifecycle pattern lists (`settings.validation.lifecycle_*_patterns`) controlling substring matching; file checks accept `--scan-mode default|tracked-all|tracked-all-strict` plus `--include-pm-internals` opt-in and report filtered + raw candidate metrics (`candidate_total`, `candidate_scanned`, `candidate_total_raw`, `candidate_scanned_raw`) plus structured exclusion summaries (`excluded_by_reason`); resolution checks include default remediation command templates for missing resolution fields; strict warning exits via `--strict-exit` (alias `--fail-on-warn`) | `{ ok, checks, warnings, generated_at }` where metadata details include profile/source/fallback visibility fields, lifecycle details include deterministic per-category row summaries plus effective lifecycle pattern arrays and per-field pattern-source metadata (`default|settings`), and tracked-all-strict visibility includes explicit file-check detail flags (`strict_mode_forces_pm_internals`, `strict_mode_forces_pm_internals_notice`) plus warning token `validate_files_tracked_all_strict_forces_pm_internals` when strict mode force-enables PM internals |
999
- | `pm config <project\|global> <get\|set\|list\|export> [key]` | scope + action; `get/set` require key, `list/export` reject key; policy/format/criterion flags apply where relevant (criterion-list keys include `definition-of-done`, `metadata-required-fields`, and lifecycle pattern keys `lifecycle-*-patterns`) | `get/set`: existing key-specific result shape; `list`: `{ scope, keys, count, settings_path, changed, warnings? }`; `export`: `{ scope, values, settings_path, changed, warnings? }` |
1000
- | `pm gc` | optional `--dry-run` (preview no-side-effect cleanup) and repeatable/comma-delimited `--scope index\|embeddings\|runtime` | `{ ok, dry_run, scope, removed, retained, warnings, guidance, generated_at }` |
1001
- | `pm contracts [--action <value>] [--command <value>] [--schema-only] [--runtime-only|--active-only] [--flags-only|--availability-only]` | optional action/command filters, schema-only mode, runtime invocability filtering, and lightweight projection modes; when `--command` is provided, command/action/availability output is narrowed to that selected command by default; when `--action` is provided without `--command`, `--flags-only` command flag output is action-scoped to matching command surfaces | `{ schema_version, schema_id, selected, actions?, action_availability?, commands?, schema?, extension_commands?, command_flags?, commander_aliases?, command_path?, cli_exposed? }` |
1002
- | `pm docs <ID> --add/--add-glob/--remove/--migrate/--validate-paths/--audit` | id + doc refs (`--add/--remove` accept CSV key/value, markdown `key: value`, or stdin token `-`); optional glob expansion via repeatable `--add-glob` (plain glob or `pattern=<glob>,scope=<scope>,note=<text>`); optional additive linked-path hygiene (`--migrate from=<old>,to=<new>`, path existence validation, cross-item audit) | `{ id, docs, changed, count, migrations_applied, validation, audit }` |
1003
- | `pm deps <ID> --format tree|graph` | id + optional output selector (`tree` default, `graph` for node/edge projection), traversal controls (`--max-depth`), repeat-collapse mode (`--collapse none|repeated`), and count-only summary mode (`--summary`) | `{ id, format, node_count, edge_count, missing_count, tree? graph? }` |
1004
- | `pm history <ID> --limit/--diff/--verify` | id + optional limit + additive diagnostics (`--diff` changed-field patch summaries, `--verify` hash-chain/current-hash verification) | `{ id, history, count, limit, diff, verify }` |
1005
- | `pm activity --limit` | optional limit plus filters (`--id`, `--op`, `--author`, `--from`, `--to`) and JSON-only stream mode (`--stream [rows|ndjson|jsonl]`) | standard: `{ activity, count, limit, filters }`; stream: line-delimited JSON entries with deterministic `meta` and `entry` records |
1006
- | `pm restore <ID> <TIMESTAMP\|VERSION>` | id + restore target + optional `--author/--message/--force` | `{ item, restored_from, changed_fields, warnings }` |
1007
- | `pm completion <shell>` | `bash`, `zsh`, or `fish`; supports `--eager-tags` to embed static tag completions; default script mode uses lazy runtime tag lookup via `pm completion-tags`; non-JSON output is the raw script suitable for eval or pipe; JSON output is `{ shell, script, setup_hint }` | `{ shell, script, setup_hint }` |
1008
- | `pm completion-tags` | internal helper command returning current tag values for completion script lazy lookup | `{ tags, count }` |
1009
-
1010
- List command row projection:
1011
-
1012
- - Default `list*` rows contain `ItemMetadata` fields only.
1013
- - `--compact` projects deterministic compact fields (`id`, `title`, `status`, `type`, `priority`, `parent`, `updated_at`).
1014
- - `--fields <csv>` projects caller-selected list fields.
1015
- - With `--include-body`, each row additionally includes `body` and `filters.include_body` is `true` (`null` when omitted in JSON; omitted in sparse TOON).
1016
- - Without `--include-body`, omission of `body` is intentional for lightweight list payloads; use `pm get <ID>` when full body content is required.
1017
- - `--sort` + `--order` emits deterministic `sorting` metadata describing the active sort field and direction.
1018
-
1019
- Roadmap output contracts remain defined in this PRD for extension areas and advanced search tuning that are still out of v0.1 release scope.
1020
-
1021
- ## 12) Canonical Output Objects (TOON-first)
1022
-
1023
- All commands return a deterministic top-level object with stable key order.
1024
-
1025
- Examples:
1026
-
1027
- - `list*`:
1028
- - `{ items, count, filters, now }`
1029
- - default rows: `ItemMetadata`
1030
- - with `--include-body`: `ItemMetadata + body`
1031
- - `search`:
1032
- - `{ query, mode, items, count, filters, now }`
1033
- - `get`:
1034
- - `{ item, body, linked: { files, tests, docs } }`
1035
- - `create/update/delete`:
1036
- - `{ item, changed_fields, warnings }` (`update` may also include `audit_update` when `--allow-audit-update` is active)
1037
- - `append`:
1038
- - `{ item, appended, changed_fields }`
1039
- - `test-all`:
1040
- - `{ totals, failed, passed, skipped, results }`
1041
- - roadmap examples (advanced semantic/hybrid tuning expansion) remain post-v0.1.
1042
-
1043
- Determinism requirements:
1044
-
1045
- - Stable key order in every object.
1046
- - Stable array order for `items` (default sort: non-terminal before terminal, then priority asc, then updated_at desc, then id asc).
1047
- - `pm list` excludes terminal statuses (`closed`, `canceled`) by default; `pm list-all` includes all statuses.
1048
- - JSON output preserves command-contract fields (including explicit `null` placeholders where applicable by command contract); `pm search` compact projection is default unless `--full` or `--fields` is provided.
1049
- - TOON output is a sparse projection that omits `null`/`undefined`/empty arrays/empty objects while preserving non-empty values.
1050
- - `--quiet` prints nothing to stdout but still uses exit codes.
1051
- - Stdin token paths requiring piped input fail fast on interactive TTY stdin with actionable guidance instead of waiting indefinitely for EOF.
1052
- - Manual interactive EOF guidance remains explicit and cross-platform: `Ctrl+D` (Unix/macOS) and `Ctrl+Z` then `Enter` (Windows).
1053
- - Output writes handle broken pipes (`EPIPE`) as expected shell behavior: stdout `EPIPE` preserves success exits for early-closing consumers and stderr `EPIPE` remains non-zero, with unhandled stack traces suppressed.
1054
-
1055
- ## 13) Search Architecture
1056
-
1057
- ### 13.0 Command contract (implemented baseline)
1058
-
1059
- `pm search <keywords...>` is implemented across keyword, semantic, and hybrid modes with deterministic ordering. The command accepts quoted or unquoted multi-word queries, searches core item corpus fields, supports vector-query execution when configured (or when local Ollama auto-defaults are resolved), and returns stable JSON/TOON payloads with compact projection default.
1060
-
1061
- Initial flags:
1062
-
1063
- - `--mode <keyword|semantic|hybrid>` (all modes implemented baseline; advanced semantic/hybrid tuning planned)
1064
- - `--include-linked` (keyword mode and hybrid lexical component: include readable linked docs/files/tests content in corpus scoring)
1065
- - `--limit <n>`
1066
- - projection controls: `--compact` (default), `--full`, `--fields <csv>` (mutually exclusive)
1067
- - `--limit 0` is valid and returns a deterministic empty result set (after mode/config validation) without executing embedding/vector query requests
1068
- - shared list-like filters where applicable (`--type`, `--tag`, `--priority`, `--deadline-before`, `--deadline-after`)
1069
- - shared `--type` and `--priority` filters follow canonical validation (`--type` resolved by runtime item-type registry aliases, `--priority` integer `0..4`)
1070
-
1071
- ### 13.1 Modes
1072
-
1073
- - `keyword` (always available)
1074
- - `semantic` (when embedding provider + vector store configured)
1075
- - `hybrid` (default if semantic capability is available through explicit settings or local Ollama auto-default resolution)
1076
- - for implicit default mode, if auto-defaulted semantic execution fails at runtime, search degrades to keyword mode for compatibility
1077
-
1078
- ### 13.2 Keyword corpus fields
1079
-
1080
- - `title`
1081
- - `description`
1082
- - `tags`
1083
- - `status`
1084
- - `body`
1085
- - `comments[].text`
1086
- - `notes[].text`
1087
- - `learnings[].text`
1088
- - dependency IDs/kinds
1089
-
1090
- Keyword/hybrid lexical scoring baseline also applies a deterministic exact-title token boost:
1091
-
1092
- - each query token found as a full token in `title` contributes an additional lexical bonus
1093
- - bonus is additive with existing weighted occurrence scoring and keeps deterministic tie-break ordering unchanged
1094
-
1095
- `--include-linked` lexical baseline (keyword + hybrid lexical component):
1096
-
1097
- - linked docs/files/tests content (project/global scope resolution, best-effort reads)
1098
- - linked-content reads are root-bounded by scope:
1099
- - `scope=project`: resolved path and symlink-resolved realpath must remain within project root
1100
- - `scope=global`: resolved path and symlink-resolved realpath must remain within global root
1101
- - out-of-scope paths or realpath escapes are ignored deterministically
1102
-
1103
- ### 13.3 Reindex baseline + semantic execution baseline
1104
-
1105
- - `pm reindex` baseline behavior rebuilds deterministic keyword cache artifacts:
1106
- - `index/manifest.json` (indexed item metadata summary)
1107
- - `search/embeddings.jsonl` (line-delimited keyword corpus records)
1108
- - `pm reindex --mode semantic|hybrid` baseline generates deterministic provider embeddings for canonical item corpus records and upserts vector records to the active vector store.
1109
- - `pm reindex --mode semantic|hybrid` also rewrites `search/vectorization-status.json` with deterministic `id -> updated_at` records for the indexed corpus so health-time vector freshness checks stay in sync.
1110
- - Semantic embedding generation in `pm reindex --mode semantic|hybrid` and mutation-triggered refresh paths executes in deterministic batches sized by `search.embedding_batch_size`, and each batch retries failed embedding requests up to `search.scanner_max_batch_retries` before surfacing deterministic warnings/errors.
1111
- - Successful item-mutation command paths invalidate stale keyword cache artifacts (`index/manifest.json` and `search/embeddings.jsonl`) as best-effort non-fatal cleanup before the next explicit `reindex`.
1112
- - Successful item-mutation command paths also perform best-effort semantic embedding refresh for affected item IDs when embedding-provider and vector-store configuration are available; when an affected ID no longer exists (for example after delete), refresh attempts prune the stale vector entry from the active store. Refresh failures degrade to deterministic warnings.
1113
- - Settings support:
1114
- - `score_threshold`
1115
- - `hybrid_semantic_weight`
1116
- - `max_results`
1117
- - `embedding_model`
1118
- - `embedding_batch_size`
1119
- - `scanner_max_batch_retries`
1120
- - `tuning` (optional object: `title_exact_bonus`, `title_weight`, `description_weight`, `tags_weight`, `status_weight`, `body_weight`, `comments_weight`, `notes_weight`, `learnings_weight`, `dependencies_weight`, `linked_content_weight`)
1121
- - `search.score_threshold` runtime semantics:
1122
- - keyword mode compares against raw lexical score
1123
- - semantic mode compares against vector similarity score
1124
- - hybrid mode compares against normalized blended score (`0..1`) after lexical+semantic combination
1125
- - default `0` preserves all positive-score hits
1126
- - `search.hybrid_semantic_weight` runtime semantics:
1127
- - numeric range `0..1` (out-of-range or non-numeric values fall back to default)
1128
- - hybrid combined score uses: `(semantic_normalized * hybrid_semantic_weight) + (keyword_normalized * (1 - hybrid_semantic_weight))`
1129
- - default `0.7` keeps semantic ranking primary while preserving deterministic lexical influence
1130
- - `search.tuning` runtime semantics:
1131
- - optional object controlling deterministic multi-factor lexical weighting in keyword mode and the hybrid lexical component
1132
- - non-numeric/negative tuning values fall back to deterministic defaults per field
1133
- - default weights when unset: `title_exact_bonus=10`, `title_weight=8`, `description_weight=5`, `tags_weight=6`, `status_weight=2`, `body_weight=1`, `comments_weight=1`, `notes_weight=1`, `learnings_weight=1`, `dependencies_weight=3`, `linked_content_weight=1`
1134
-
1135
- ### 13.4 Providers and vector stores (semantic/hybrid execution baseline)
1136
-
1137
- Embedding providers:
1138
-
1139
- - OpenAI-compatible (`base_url`, `api_key`, `model`)
1140
- - Ollama (`base_url`, `model`)
1141
-
1142
- Implemented baseline:
1143
-
1144
- - Deterministic provider-configuration resolution exists in core search runtime plumbing.
1145
- - OpenAI/Ollama provider blocks are normalized from settings and surfaced through a provider abstraction layer for command-time validation, request-target resolution (including OpenAI-compatible `base_url` normalization for root, `/v1`, and explicit `/embeddings` forms), request payload/response normalization (including deterministic OpenAI data-entry index ordering), deterministic request-execution helper behavior, deterministic per-request normalized-input deduplication with output fan-out back to original input cardinality/order, and deterministic embedding cardinality validation (normalized input count must match returned vector count after dedupe expansion).
1146
- - When semantic settings are otherwise unset and local Ollama is installed, runtime auto-resolves built-in semantic defaults (`providers.ollama`, local `vector_store.lancedb.path`, and `search.embedding_model`) for `search`/`reindex`; auto model selection prefers `PM_OLLAMA_MODEL`, then `ollama list` embedding-like model names, then deterministic fallback `qwen3-embedding:0.6b`.
1147
- - Auto-default behavior is compatibility-guarded: explicit semantic settings always take precedence, and auto-defaults can be disabled via `PM_DISABLE_OLLAMA_AUTO_DEFAULTS=1`.
1148
- - `pm search --mode semantic|hybrid` and `pm reindex --mode semantic|hybrid` use this abstraction for deterministic semantic/hybrid execution (embedding generation/request handling) after configuration validation.
1149
-
1150
- Vector stores:
1151
-
1152
- - Qdrant (`url`, `api_key?`)
1153
- - LanceDB (`path`)
1154
-
1155
- Implemented baseline:
1156
-
1157
- - Deterministic vector-store configuration resolution for Qdrant and LanceDB is available in core search runtime plumbing.
1158
- - Qdrant/LanceDB settings blocks are normalized from `settings.json` and surfaced through a vector-store abstraction layer for command-time validation.
1159
- - Request-target planning, request payload/response normalization, deterministic Qdrant request-execution helper behavior, deterministic LanceDB local query/upsert execution helper behavior, and deterministic query-hit ordering normalization (score desc, id asc tie-break) are available through this abstraction layer.
1160
- - `pm search --mode semantic|hybrid` and `pm reindex --mode semantic|hybrid` use this abstraction for deterministic vector query/upsert execution after configuration validation.
1161
-
1162
- ### 13.5 Health integrity, drift, and vectorization diagnostics
1163
-
1164
- - `pm health` includes deterministic `directories` diagnostics:
1165
- - separates core required directories from optional built-in type directories (`events`, `reminders`, `milestones`, `meetings`)
1166
- - reports `missing_required` and `missing_optional` details independently
1167
- - `--strict-directories` treats missing optional directories as warning/failure contributors
1168
- - `--strict-exit` (alias `--fail-on-warn`) returns non-zero exit (`1`) when health warnings are present (`ok=false`)
1169
- - `pm health` includes deterministic `integrity` diagnostics:
1170
- - scans item/history files for merge-conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`)
1171
- - emits deterministic warning codes for conflict markers, invalid item parses, and invalid JSONL history lines
1172
- - `pm health` includes deterministic `history_drift` diagnostics:
1173
- - checks current item corpus against history stream availability/parsability
1174
- - compares current canonical item hash to latest history `after_hash`
1175
- - emits deterministic warning codes for missing streams, unreadable streams, and hash mismatches
1176
- - `pm health` includes deterministic `vectorization` diagnostics:
1177
- - compares current item `updated_at` values to `search/vectorization-status.json`
1178
- - computes stale/missing vectorization entries deterministically (sorted by item ID)
1179
- - triggers targeted semantic refresh for stale IDs when semantic runtime is available (no forced full reindex)
1180
- - preserves compatibility under auto-resolved Ollama defaults by keeping auto-default refresh failures non-fatal in health status while still exposing refresh details in check output
1181
-
1182
- ## 14) Extension Architecture
1183
-
1184
- ### 14.1 Locations
1185
-
1186
- - Global: `~/.pm-cli/extensions` (or `PM_GLOBAL_PATH/extensions`)
1187
- - Project: `.agents/pm/extensions` (or `PM_PATH/extensions`)
1188
-
1189
- Lifecycle manager command:
1190
-
1191
- - `pm extension` is the canonical extension lifecycle command surface for install/uninstall/explore/manage/doctor/adopt/adopt-all/activate/deactivate.
1192
- - Scope selection: `--project` (default), `--local` (alias of project), `--global`.
1193
- - Install sources: local directory, GitHub HTTPS URL, `github.com/<owner>/<repo>[/path]`, or `--gh/--github <owner>/<repo>[/path]` with optional `--ref`.
1194
- - GitHub subpath resolution probes deterministic default extension roots (`.agents/pm/extensions`, `.custom/pm-extensions`, `.custom/pm-extension`) when shorthand inputs do not include full paths.
1195
- - Scope-local managed state is persisted in `<extension-root>/.managed-extensions.json` and includes source metadata plus update-check status.
1196
- - `pm extension --manage` performs GitHub remote update checks for managed GitHub entries, persists latest check metadata, and returns deterministic per-extension `update_check_status`/`update_check_reason` fields plus `details.triage` status totals/remediation hints.
1197
- - `pm extension --manage --runtime-probe` is opt-in and adds doctor-like runtime activation probing (`runtime_active`/`activation_status`) while preserving default manage behavior when omitted.
1198
- - `pm extension --manage --fix-managed-state` can adopt unmanaged extensions before update checks to reduce managed-state remediation friction.
1199
- - `pm extension --adopt <name>` records existing unmanaged installs into managed state metadata (local or GitHub provenance via `--gh/--github` and optional `--ref`) without reinstalling files.
1200
- - `pm extension --adopt-all` bulk-records all unmanaged installs in selected scope into managed state metadata without reinstalling files.
1201
- - `pm extension --doctor` (or `pm extension doctor`) returns consolidated diagnostics with summary/deep modes (`--detail summary|deep`), normalized warning codes, canonical load roots, active-vs-loaded project consistency diagnostics, explicit state fields (`active` compatibility alias plus `enabled`/`runtime_active`/`activation_status`), strict warning exits (`--strict-exit`, alias `--fail-on-warn`), update-health coverage signals (`update_health_coverage`, `update_health_partial`), blocking-failure indicators (`blocking_failure_count`, `has_blocking_failures`), capability guidance/contract metadata, and remediation hints.
1202
- - `pm extension --doctor --detail deep --trace` adds actionable registration traces (method, command, registration index, expected schema, sanitized received payload, hint) for activation failures.
1203
- - `pm extension --doctor --fix-managed-state` can adopt unmanaged extensions before diagnostics are evaluated.
1204
- - `pm health` extension diagnostics include managed-state summaries/warnings for both project and global scope plus condensed `details.triage` counts/remediation for load, activation, and migration issues, including parity warning code `extension_update_health_partial_coverage` when action-required unmanaged extensions reduce update-check coverage and capability guidance/contract metadata for unknown manifest capabilities.
1205
-
1206
- ### 14.2 Load order and precedence
1207
-
1208
- 1. Core built-ins
1209
- 2. Global extensions
1210
- 3. Project extensions
1211
-
1212
- Precedence:
1213
-
1214
- - Later load can override earlier by explicit command/renderer/hook keys.
1215
- - Project overrides global by default.
1216
- - Priority field in manifest may alter local ordering within same layer.
1217
-
1218
- ### 14.3 Extension manifest (minimum)
1219
-
1220
- ```json
1221
- {
1222
- "name": "pm-ext-example",
1223
- "version": "0.1.0",
1224
- "entry": "./dist/index.js",
1225
- "priority": 100,
1226
- "capabilities": [
1227
- "commands",
1228
- "parser",
1229
- "preflight",
1230
- "services",
1231
- "schema",
1232
- "renderers",
1233
- "importers",
1234
- "search",
1235
- "hooks"
1236
- ]
1237
- }
1238
- ```
1239
-
1240
- Capability declarations are enforced during extension activation. API registrations and
1241
- hook registrations must match declared capabilities (`commands`, `renderers`, `hooks`,
1242
- `schema`, `importers`, `search`, `parser`, `preflight`, `services`) or activation fails with deterministic
1243
- `extension_activate_failed:<layer>:<name>` diagnostics.
1244
- Unknown capability names are ignored for registration gating and produce deterministic
1245
- discovery diagnostics `extension_capability_unknown:<layer>:<name>:<capability>:allowed=<csv>:suggested=<capability|none>` with legacy alias suggestions when applicable (for example `migration`/`validation` -> `schema`).
1246
- Health/doctor payloads additionally publish machine-readable contract metadata (`capability_contract.version`, `capability_contract.capabilities`, `capability_contract.legacy_aliases`) for automation parity.
1247
-
1248
- ### 14.4 Extension API contracts (v1 draft)
1249
-
1250
- Current implementation (single-wave override model):
1251
-
1252
- - `activate(api)` hook registration surface is available.
1253
- - `api.hooks.beforeCommand/afterCommand/onWrite/onRead/onIndex` dispatch is deterministic with failure containment and per-hook context snapshot isolation.
1254
- - Hook registration APIs (`api.hooks.beforeCommand/afterCommand/onWrite/onRead/onIndex`) require function handlers; invalid payloads throw during extension activation and surface deterministic `extension_activate_failed:<layer>:<name>` warnings.
1255
- - `api.registerCommand(...)` validation failures now carry actionable trace metadata that doctor deep trace mode can expose (method, registration index, command, expected schema, received payload, hint) to speed activation triage.
1256
- - `api.registerCommand(name, override)` supports deterministic synchronous overrides for existing command results before output rendering; override execution receives cloned command `args`/`options`/`global` snapshots, `pm_root`, and a cloned prior result payload so extensions can apply contextual overrides without mutating caller fallback state.
1257
- - `api.registerCommand({ name, run })` supports deterministic extension command handlers for declared command paths, including dynamically surfaced non-core extension command paths (for example `beads import` and `acme sync`) and extension-first replacement of core command handlers at dispatch time.
1258
- - `api.registerParser(command, override)` supports command-scoped parser override contracts (sync/async) that can rewrite `args`, `options`, and `global` context before handler dispatch.
1259
- - `api.registerPreflight(override)` supports command preflight interception (sync/async) to control item-format gate enforcement, preflight migration sync, extension migration execution, and mandatory-migration gate enforcement.
1260
- - `api.registerService(service, override)` supports deterministic service-level override hooks (`output_format`, `error_format`, `help_format`, `lock_acquire`, `lock_release`, `history_append`, `item_store_write`, `item_store_delete`) with last-wins precedence per service key.
1261
- - Extension command-handler execution receives cloned `args`/`options`/`global` snapshots so handler-side mutation cannot leak into caller runtime command state.
1262
- - Registered extension command names are canonicalized with trim + lowercase + internal-whitespace collapse before storage and dispatch matching, ensuring equivalent command paths resolve deterministically.
1263
- - Required extension-command dispatch semantics are deterministic: no matched handler returns command-not-found for extension-only paths, while a matched handler throw returns generic failure with warning code `extension_command_handler_failed:<layer>:<name>:<command>`.
1264
- - `api.registerRenderer(format, renderer)` supports deterministic `toon`/`json` output overrides; renderer execution receives isolated command context snapshots (`command`, `args`, `options`, `global`, `pm_root`) plus an isolated result snapshot so failed renderer-side mutation cannot alter core fallback output.
1265
- - Extension API registration baseline now includes deterministic registration-time validation and metadata capture for `api.registerFlags`, `api.registerItemFields`, `api.registerItemTypes`, `api.registerMigration`, `api.registerImporter`, `api.registerExporter`, `api.registerSearchProvider`, and `api.registerVectorStoreAdapter`.
1266
- - `api.registerImporter(name, importer)` and `api.registerExporter(name, exporter)` now provide runtime command wiring in addition to metadata capture: each registration deterministically exposes extension command-handler paths `<name> import` and `<name> export` (canonicalized with trim + lowercase + internal-whitespace collapse) and executes through the same isolated command-handler context snapshots used by `api.registerCommand({ name, run })`.
1267
- - Dynamically surfaced extension command paths now render deterministic help metadata derived from registered `api.registerFlags(...)` definitions while preserving loose option parsing behavior for runtime command dispatch.
1268
- - Extension API and hook registration calls enforce manifest capability declarations (`commands`, `renderers`, `hooks`, `schema`, `importers`, `search`, `parser`, `preflight`, `services`) and fail activation deterministically when an extension registers outside its declared capabilities.
1269
- - Extension activation diagnostics include deterministic registration counts and metadata summaries for the above registries (flags, item fields, item types, migrations, importers, exporters, search providers, and vector store adapters), `pm health` exposes deterministic migration status summaries from registered migration definitions (`status="failed"` -> failed, `status="applied"` -> applied, any other/missing status -> pending), and core write command paths enforce deterministic mandatory-migration gating (`mandatory=true` + status not `"applied"` -> unresolved blocker, with `--force` bypass on force-capable write commands). Mandatory extension migrations are executed in pre-action lifecycle before write-gate enforcement.
1270
- - Registration runtime wiring is live for:
1271
- - item-field defaults + validation on create/update (`registerItemFields`)
1272
- - search provider selection via `settings.search.provider` (`registerSearchProvider`)
1273
- - vector adapter query/upsert selection via `settings.vector_store.adapter` (`registerVectorStoreAdapter`)
1274
- - migration runtime execution and mandatory gate evaluation (`registerMigration`)
1275
-
1276
- Full v1 draft surface:
1277
-
1278
- ```ts
1279
- export interface PmExtension {
1280
- manifest: ExtensionManifest;
1281
- activate(api: ExtensionApi): Promise<void> | void;
1282
- }
1283
-
1284
- export interface ExtensionApi {
1285
- registerCommand(def: CommandDefinition): void;
1286
- registerParser(command: string, override: ParserOverride): void;
1287
- registerPreflight(override: PreflightOverride): void;
1288
- registerService(service: ExtensionServiceName, override: ServiceOverride): void;
1289
- registerFlags(targetCommand: string, flags: FlagDefinition[]): void;
1290
- registerItemFields(fields: SchemaFieldDefinition[]): void;
1291
- registerItemTypes(types: ItemTypeDefinition[]): void;
1292
- registerMigration(def: SchemaMigrationDefinition): void;
1293
- registerRenderer(format: "toon" | "json", renderer: Renderer): void;
1294
- registerImporter(name: string, importer: Importer): void;
1295
- registerExporter(name: string, exporter: Exporter): void;
1296
- registerSearchProvider(provider: SearchProvider): void;
1297
- registerVectorStoreAdapter(adapter: VectorStoreAdapter): void;
1298
- hooks: {
1299
- beforeCommand(hook: BeforeCommandHook): void;
1300
- afterCommand(hook: AfterCommandHook): void;
1301
- onWrite(hook: OnWriteHook): void;
1302
- onRead(hook: OnReadHook): void;
1303
- onIndex(hook: OnIndexHook): void;
1304
- };
1305
- }
1306
- ```
1307
-
1308
- ### 14.5 Failure isolation and safety
1309
-
1310
- - Extension load failure must not corrupt core data.
1311
- - Failed extension is marked unhealthy and reported via `pm health`.
1312
- - `pm health` extension checks must run safe runtime load and activation probes (including installed managed extensions) and emit deterministic warning codes for import/activation failures (for example `extension_load_failed:<layer>:<name>` and `extension_activate_failed:<layer>:<name>`).
1313
- - Extension manifest `entry` paths must resolve within the extension directory after canonical path resolution (including symlink targets); traversal/escape paths are rejected with deterministic diagnostics (for example `extension_entry_outside_extension:<layer>:<name>`).
1314
- - Core commands remain functional unless extension is explicitly required by invoked command.
1315
-
1316
- ### 14.6 Schema extension migrations
1317
-
1318
- - Extensions adding front-matter fields must provide forward migrations.
1319
- - Migration definitions are versioned and idempotent.
1320
- - `pm health` reports deterministic migration status summaries:
1321
- - `applied`: registered migrations whose definition status is `"applied"` (case-insensitive).
1322
- - `pending`: registered migrations whose definition status is neither `"failed"` nor `"applied"` (or is missing).
1323
- - `failed`: registered migrations whose definition status is `"failed"` (case-insensitive), with optional reason from `reason`, `error`, or `message` metadata.
1324
- - Core write command paths are blocked when unresolved mandatory migrations are present from active extension registrations.
1325
- - Mandatory migrations are definitions with `mandatory: true`.
1326
- - Mandatory migration resolution is deterministic: `status` equal to `"applied"` (case-insensitive) is treated as resolved; any other/missing status is unresolved.
1327
- - Force-capable write commands may bypass the guard with explicit `--force`; write commands without `--force` remain blocked until blockers resolve.
1328
-
1329
- ## 15) First-Party Installable Packages Required in v1
1330
-
1331
- ### A) Beads import
1332
-
1333
- Command:
1334
-
1335
- - `pm beads import [--file <path>|-] [--preserve-source-ids]`
1336
-
1337
- Current baseline status (release-hardening):
1338
-
1339
- - Command is packaged in the first-party package root (`packages/pm-beads`) using `activate(api)` and `api.registerCommand({ name, run })` for `beads import`.
1340
- - Command path is available only after package install/activation in selected scope (`pm install packages/pm-beads`, package aliases, or explicit package source install).
1341
-
1342
- Behavior:
1343
-
1344
- - Parse Beads JSONL records.
1345
- - Map Beads fields to PM schema.
1346
- - Preserve IDs and timestamps where possible, including Beads-only compatibility metadata such as `source_type`, `source_owner`, `source_kind`, `design`, `external_ref`, and `closed_at`.
1347
- - Append history with `op: "import"`.
1348
- - When `--file` is not provided, auto-discover `.beads/issues.jsonl` first and then `issues.jsonl`; implicit fallback to `sync_base.jsonl` is refused because it may be partial.
1349
- - `--preserve-source-ids` preserves explicit Beads item IDs verbatim instead of rewriting them to the tracker prefix.
1350
- - Invalid JSONL lines or duplicate IDs are skipped with deterministic warnings.
1351
-
1352
- ### B) todos.ts import/export
1353
-
1354
- Commands:
1355
-
1356
- - `pm todos import [--folder <path>]`
1357
- - `pm todos export [--folder <path>]`
1358
-
1359
- Current baseline status (release-hardening):
1360
-
1361
- - Commands are packaged in the first-party package root (`packages/pm-todos`) using `activate(api)` and `api.registerCommand({ name, run })` for `todos import` and `todos export`.
1362
- - Command paths are available only after package install/activation in selected scope (`pm install packages/pm-todos`, package aliases, or explicit package source install).
1363
-
1364
- Behavior:
1365
-
1366
- - Read/write todos markdown format (JSON front-matter + body).
1367
- - Field mapping:
1368
- - `title -> title`
1369
- - `body -> body`
1370
- - imported IDs, including hierarchical suffixes such as `pm-legacy.1.2`, are preserved verbatim when provided in todos front matter
1371
- - canonical PM metadata fields round-trip when present, including planning/workflow metadata (`definition_of_ready`, `order`, `goal`, `objective`, `value`, `impact`, `outcome`, `why_now`, `reviewer`, `risk`, `confidence`, `sprint`, `release`, `blocked_by`, `blocked_reason`, `unblock_note`) and issue metadata (`reporter`, `severity`, `environment`, `repro_steps`, `resolution`, `expected_result`, `actual_result`, `affected_version`, `fixed_version`, `component`, `regression`, `customer_impact`)
1372
- - `confidence`, `risk`, and `severity` text aliases normalize deterministically (`med -> medium`)
1373
- - Missing PM fields get deterministic defaults:
1374
- - `description = ""`
1375
- - `priority = 2`
1376
- - `type = "Task"`
1377
- - `updated_at = created_at (or now if missing)`
1378
-
1379
- ### C) External tool wrappers and package integrations
1380
-
1381
- Current package-first baseline:
1382
-
1383
- - The bare CLI and SDK expose provider-neutral contracts through `pm contracts` and the SDK exports in `src/sdk/cli-contracts.ts`.
1384
- - External agent/tool wrappers should consume those contracts instead of importing internal CLI modules or embedding provider-specific action schemas in core.
1385
- - Optional workflow bridges belong in installable pm packages that declare `pm.extensions` resources and can be installed, diagnosed, and upgraded with `pm install`, `pm package`, and `pm upgrade`.
1386
- - The main repository can include first-party package sources, but direct provider-specific wrappers are not part of the bare core.
1387
-
1388
- Schema-capability registrations are also validated deterministically at activation-time:
1389
-
1390
- - `registerFlags`: each entry must provide at least one of `long`/`short`; optional metadata fields must match expected scalar types.
1391
- - `registerItemFields`: each entry requires non-empty `name` and `type`.
1392
- - `registerItemTypes`: each type requires non-empty `name`; nested `options[]` and `command_option_policies[]` entries enforce required key fields and boolean toggles.
1393
- - `registerMigration`: typed migration metadata (`id`, `description`, `status`, `mandatory`, `run`) is validated when provided.
1394
-
1395
- ## 16) Security and Data Integrity
1396
-
1397
- - All writes are lock-protected + atomic.
1398
- - Never partially write item or history line.
1399
- - Validate and normalize path inputs to prevent traversal.
1400
- - `pm search --include-linked` must enforce scope-root containment on linked content reads using both resolved-path and symlink-resolved-realpath checks, and ignore linked paths that escape allowed roots.
1401
- - Extension manifest `entry` paths must not escape their owning extension directory.
1402
- - Dynamic extension command loose-option parsing must ignore unsafe prototype keys (`__proto__`, `constructor`, `prototype`) and use null-prototype option maps before passing option snapshots to extension command handlers.
1403
- - Never execute linked test commands without explicit `--run`.
1404
- - Reject new linked test entries that omit `command` metadata (`pm test --add` and `pm create --test`); `path` is optional metadata and cannot be the only runnable signal.
1405
- - Reject linked test command entries that invoke `pm test-all` (including global-flag and package-spec launcher variants such as `pm --json test-all`, `npx @unbrained/pm-cli@latest --json test-all`, `pnpm dlx @unbrained/pm-cli@latest --json test-all`, and `npm exec -- @unbrained/pm-cli@latest --json test-all`) to prevent recursive orchestration loops.
1406
- - `pm test <ID> --run` defensively skips legacy linked command entries that invoke `pm test-all` (including global-flag and package-spec launcher variants such as `npx`, `pnpm dlx`, and `npm exec` launcher forms) and records deterministic skipped results.
1407
- - Reject linked test-runner command entries (for example `pnpm test`, `pnpm test:coverage`, `npm test`, `npm run test`, `pnpm run test`, `yarn run test`, `bun run test`, `vitest`) unless they use `node scripts/run-tests.mjs ...` or explicitly set both `PM_PATH` and `PM_GLOBAL_PATH`; chained direct test-runner segments are validated independently and rejected when not explicitly sandboxed.
1408
- - Linked command execution in `pm test --run` and `pm test-all` must keep temporary sandbox isolation while seeding both project/global `settings.json` and `extensions/` directories from source roots so extension-defined type behavior matches direct workspace commands.
1409
- - Linked PM-command test runs support additive context mode `--pm-context schema|tracker|auto` plus per-test metadata override `pm_context_mode=schema|tracker|auto`; tracker mode seeds tracker corpus into sandbox, auto mode routes PM tracker-read commands to tracker context, per-test overrides take precedence over run-level mode, and every run result emits deterministic `execution_context` metadata (resolved roots, item counts, mismatch signal, PM tracker-read classification).
1410
- - In default `--pm-context schema` mode, PM tracker-read linked commands fail on context mismatch by default; `--fail-on-context-mismatch` remains available to enforce mismatch failures for non-tracker-read PM command shapes.
1411
- - `pm test --run` and `pm test-all` support additive strict governance guards: `--fail-on-context-mismatch`, `--fail-on-skipped`, `--fail-on-empty-test-run`, and `--require-assertions-for-pm`.
1412
- - Linked test assertion metadata (`assert_stdout_contains`, `assert_stdout_regex`, `assert_stderr_contains`, `assert_stderr_regex`, `assert_stdout_min_lines`, `assert_json_field_equals`, `assert_json_field_gte`) is optional and must be evaluated as deterministic assertion failures even when process exit code is `0`.
1413
- - `pm test-all` executes each unique linked command/path key at most once per run; duplicate entries are reported as skipped to keep totals deterministic while avoiding redundant execution. Duplicate-key timeout conflicts resolve deterministically to the maximum `timeout_seconds` value for that key.
1414
- - Linked test execution should emit stderr heartbeat lines in interactive terminals so long-running commands remain observable instead of appearing hung.
1415
- - Linked test timeout handling should attempt graceful termination first and then apply deterministic force-kill fallback for stubborn child process trees.
1416
- - Optional providers use explicit settings; secrets come from env or settings with documented precedence.
1417
- - Restore must verify replay hashes and fail loudly on mismatch.
1418
-
1419
- ## 17) Configuration
1420
-
1421
- `settings.json` baseline keys:
1422
-
1423
- - `version`
1424
- - `id_prefix`
1425
- - `author_default`
1426
- - `locks.ttl_seconds`
1427
- - `output.default_format`
1428
- - `history.missing_stream`
1429
- - `validation.sprint_release_format`
1430
- - `workflow.definition_of_done[]`
1431
- - `item_types.definitions[]` (custom type aliases/folders, required create fields/repeatables, `options[]`, and optional `command_option_policies[]`)
1432
- - `extensions.enabled[]`
1433
- - `extensions.disabled[]`
1434
- - `search.score_threshold`
1435
- - `search.hybrid_semantic_weight`
1436
- - `search.max_results`
1437
- - `search.embedding_model`
1438
- - `search.embedding_batch_size`
1439
- - `search.scanner_max_batch_retries`
1440
- - `search.tuning` (optional object)
1441
- - `providers.openai`
1442
- - `providers.ollama`
1443
- - `vector_store.qdrant`
1444
- - `vector_store.lancedb`
1445
-
1446
- `search.score_threshold` defaults to `0` and applies mode-specific minimum-score filtering as defined in section `13.3`.
1447
- `search.hybrid_semantic_weight` defaults to `0.7` and controls semantic-vs-lexical blend weight in hybrid mode as defined in section `13.3`.
1448
- `search.tuning` is optional; when unset or partially invalid, lexical scoring defaults remain deterministic (`title_exact_bonus=10`, `title_weight=8`, `description_weight=5`, `tags_weight=6`, `status_weight=2`, `body_weight=1`, `comments_weight=1`, `notes_weight=1`, `learnings_weight=1`, `dependencies_weight=3`, `linked_content_weight=1`).
1449
-
1450
- Default `settings.json` object written by `pm init`:
1451
-
1452
- ```json
1453
- {
1454
- "version": 1,
1455
- "id_prefix": "pm-",
1456
- "author_default": "",
1457
- "locks": {
1458
- "ttl_seconds": 1800
1459
- },
1460
- "output": {
1461
- "default_format": "toon"
1462
- },
1463
- "history": {
1464
- "missing_stream": "auto_create"
1465
- },
1466
- "validation": {
1467
- "sprint_release_format": "warn"
1468
- },
1469
- "workflow": {
1470
- "definition_of_done": []
1471
- },
1472
- "item_types": {
1473
- "definitions": []
1474
- },
1475
- "extensions": {
1476
- "enabled": [],
1477
- "disabled": []
1478
- },
1479
- "search": {
1480
- "score_threshold": 0,
1481
- "hybrid_semantic_weight": 0.7,
1482
- "max_results": 50,
1483
- "embedding_model": "",
1484
- "embedding_batch_size": 32,
1485
- "scanner_max_batch_retries": 3
1486
- },
1487
- "providers": {
1488
- "openai": {
1489
- "base_url": "",
1490
- "api_key": "",
1491
- "model": ""
1492
- },
1493
- "ollama": {
1494
- "base_url": "",
1495
- "model": ""
1496
- }
1497
- },
1498
- "vector_store": {
1499
- "qdrant": {
1500
- "url": "",
1501
- "api_key": ""
1502
- },
1503
- "lancedb": {
1504
- "path": ""
1505
- }
1506
- }
1507
- }
1508
- ```
1509
-
1510
- Definition-of-Done config baseline:
1511
-
1512
- - `pm config project set definition-of-done --criterion <text>` replaces the project-level criteria list in `.agents/pm/settings.json`.
1513
- - `pm config global set definition-of-done --criterion <text>` replaces the global criteria list in `~/.pm-cli/settings.json` (or `PM_GLOBAL_PATH/settings.json`).
1514
- - `pm config <project|global> get definition-of-done` returns the currently effective list for the selected scope with deterministic TOON/JSON output.
1515
- - Empty criteria are rejected; duplicate criteria are deduplicated with lexicographic ordering.
1516
-
1517
- History missing-stream policy config baseline:
1518
-
1519
- - `pm config <project|global> set history-missing-stream-policy --policy auto_create|strict_error` updates `settings.history.missing_stream`.
1520
- - `pm config <project|global> get history-missing-stream-policy` returns the active policy.
1521
-
1522
- Sprint/release format policy config baseline:
1523
-
1524
- - `pm config <project|global> set sprint-release-format-policy --policy warn|strict_error` updates `settings.validation.sprint_release_format`.
1525
- - `pm config <project|global> get sprint-release-format-policy` returns the active policy.
1526
-
1527
- Notes:
1528
-
1529
- - Key order in file output MUST remain exactly as shown above.
1530
-
1531
- Env precedence:
1532
-
1533
- 1. CLI flags
1534
- 2. Environment variables
1535
- 3. `settings.json`
1536
- 4. hard defaults
1537
-
1538
- ## 18) Testing Strategy and CI
1539
-
1540
- Release-ready test policy:
1541
-
1542
- - Test runner: Vitest for both unit and integration suites.
1543
- - Coverage gates: 100% for lines, branches, functions, and statements.
1544
- - CI guard: fail build when any coverage metric drops below 100%.
1545
-
1546
- Sandbox safety requirements (hard):
1547
-
1548
- - Tests MUST NOT read/write the repository's real `.agents/pm`.
1549
- - Every test suite uses temporary sandbox storage via `PM_PATH`.
1550
- - PM-driven test execution MUST use a sandbox wrapper command (`node scripts/run-tests.mjs test|coverage`) that creates a temporary directory, sets both `PM_PATH` and `PM_GLOBAL_PATH`, runs the requested test command, and cleans up the sandbox afterward.
1551
- - `pm test <ID> --add` MUST enforce this by requiring `command` metadata (with optional `path` metadata) and rejecting sandbox-unsafe test-runner command entries at add-time unless they use `node scripts/run-tests.mjs ...` or explicitly set both `PM_PATH` and `PM_GLOBAL_PATH`; this includes unsandboxed direct package-manager run-script variants (for example `npm run test` and `pnpm run test`) and chained direct test-runner segments that are not explicitly sandboxed.
1552
- - `pm test <ID> --run` MUST defensively skip legacy linked command entries that invoke `pm test-all` (including global-flag and package-spec launcher variants such as `pm --json test-all`, `npx @unbrained/pm-cli@latest --json test-all`, `pnpm dlx @unbrained/pm-cli@latest --json test-all`, and `npm exec -- @unbrained/pm-cli@latest --json test-all`) and surface deterministic skipped diagnostics.
1553
- - `pm test <ID> --run` and `pm test-all` MUST preserve sandbox isolation while seeding project/global `settings.json` and `extensions/` into sandbox roots so extension-defined schemas and type filters remain parity-consistent with direct workspace runs.
1554
- - Integration tests spawn built CLI subprocesses (`node dist/cli.js ...`) with explicit
1555
- `PM_PATH`, `PM_GLOBAL_PATH`, and `PM_AUTHOR`.
1556
- - Temporary sandbox directories must be cleaned up after each test/suite.
1557
-
1558
- Required unit coverage areas:
1559
-
1560
- - Parser/serializer round-trip and key ordering determinism.
1561
- - ID normalization/generation behavior.
1562
- - Deadline parsing and explicit clear/unset validation behavior.
1563
- - History patch + hash generation.
1564
- - Lock conflict/stale-lock behavior.
1565
-
1566
- Required integration coverage areas:
1567
-
1568
- - `init` idempotency.
1569
- - `create` full-flag.
1570
- - `list*` filtering contracts and deterministic ordering.
1571
- - `get`, `update`, `append`, `claim`, `release`, `delete`.
1572
- - `comments`, `files`, `docs`, `test`, and `test-all`.
1573
- - `history` and `activity` deterministic retrieval commands.
1574
-
1575
- CI requirements:
1576
-
1577
- - `pnpm build`
1578
- - `pnpm typecheck`
1579
- - `pnpm test`
1580
- - `pnpm test:coverage` (must satisfy 100% thresholds)
1581
- - `node scripts/run-tests.mjs coverage` for pm-linked regression execution in automation-safe mode
1582
- - Optional artifact upload for coverage reports
1583
-
1584
- Community/release documentation requirements:
1585
-
1586
- - `LICENSE` (MIT) at repository root.
1587
- - `CHANGELOG.md` at repository root using Keep a Changelog format with an `[Unreleased]` section and explicit SemVer note.
1588
- - `CONTRIBUTING.md` at repository root (or `.github/`) with setup, sandbox-safe testing, and contribution workflow.
1589
- - `SECURITY.md` policy with reporting expectations.
1590
- - `CODE_OF_CONDUCT.md` baseline contributor conduct policy.
1591
-
1592
- ## 19) Dependency Policy (Minimal, Justified)
1593
-
1594
- Core should prefer Node standard library and a minimal set:
1595
-
1596
- - `commander` (CLI arg parsing, help generation)
1597
- - `@toon-format/toon` (TOON encode/decode)
1598
- - `fast-json-patch` (RFC6902 diff/apply)
1599
- - `zod` (runtime schema validation for settings/extensions/import payloads)
1600
- - `undici` (HTTP for embedding providers, if needed by core)
1601
-
1602
- Optional adapters can introduce optional peer dependencies (Qdrant/LanceDB clients) loaded lazily through extension boundaries.
1603
-
1604
- ## 20) Risks and Mitigations
1605
-
1606
- Highest-risk areas:
1607
-
1608
- 1. History/restore correctness
1609
- - Mitigation: hash verification + replay tests + golden fixtures.
1610
- 2. Extension override complexity
1611
- - Mitigation: explicit precedence rules + deterministic registration order + health checks.
1612
- 3. Semantic indexing drift
1613
- - Mitigation: mutation-triggered re-embed + periodic `reindex` + index manifest checksums.
1614
-
1615
- ## 21) Milestone Implementation Plan (Release Hardening)
1616
-
1617
- ### Milestone 0 - Foundations
1618
-
1619
- Checklist:
1620
-
1621
- - [x] Project scaffolding, CLI entrypoint, config loader
1622
- - [x] Deterministic serializer utilities
1623
- - [x] Error model + exit code mapping
1624
-
1625
- Definition of Done:
1626
-
1627
- - `pm --help` and `pm init --help` render
1628
- - config/env precedence tested
1629
-
1630
- ### Milestone 1 - Core Item CRUD + Locking
1631
-
1632
- Checklist:
1633
-
1634
- - [x] Item schema model + validation
1635
- - [x] Parser/serializer for TOON item files plus legacy markdown migration reader
1636
- - [x] ID generation + normalization
1637
- - [x] Lock acquire/release with TTL and conflict handling
1638
- - [x] Core commands: init/create/get/update/append/claim/release/close/delete complete
1639
-
1640
- Definition of Done:
1641
-
1642
- - Full CRUD lifecycle works with atomic writes and conflict exit codes
1643
- - deterministic output in TOON/JSON
1644
-
1645
- ### Milestone 2 - History + Restore
1646
-
1647
- Checklist:
1648
-
1649
- - [x] RFC6902 patch generation per mutation
1650
- - [x] Append-only history writer
1651
- - [x] `history` and `activity` commands
1652
- - [x] `restore` by timestamp/version with replay + hash validation
1653
-
1654
- Definition of Done:
1655
-
1656
- - Replay reproduces exact prior item state in tests
1657
- - restore appends `restore` history event
1658
-
1659
- ### Milestone 3 - Query + Operations
1660
-
1661
- Checklist:
1662
-
1663
- - [x] list/list-* filters and deterministic sort
1664
- - [x] comments/files/docs/test commands
1665
- - [x] test-all orchestration + dependency-failed exit handling
1666
- - [x] stats/health/gc command baseline
1667
-
1668
- Definition of Done:
1669
-
1670
- - Command matrix complete and deterministic
1671
- - docs-linked operations tested
1672
-
1673
- ### Milestone 4 - Search
1674
-
1675
- Checklist:
1676
-
1677
- - [x] keyword indexing + search command (keyword command surface + deterministic reindex artifact rebuild implemented; deterministic exact-title token boost and configurable multi-factor lexical tuning via `search.tuning` implemented; `--limit 0` short-circuit implemented; advanced relevance tuning is post-v0.1 roadmap)
1678
- - [x] embedding provider abstraction (deterministic provider configuration resolution, request-target planning including OpenAI-compatible `base_url` normalization for root/`/v1`/`/embeddings`, provider-specific request payload/response normalization with deterministic OpenAI data-entry index ordering, deterministic request-execution helper behavior, deterministic embedding cardinality validation, deterministic per-request normalized-input dedupe with output fan-out, configurable batch sizing and per-batch retry, command-path embedding execution, and mutation-triggered embedding refresh are implemented; additional advanced provider optimizations are post-v0.1 roadmap)
1679
- - [x] vector store adapters (Qdrant/LanceDB deterministic configuration resolution, request-target planning, request payload/response normalization, deterministic request-execution helpers, deterministic LanceDB local query/upsert/delete execution helper behavior, deterministic local snapshot persistence + reload across process boundaries, query-hit ordering normalization, and command-path vector query/upsert integration implemented; broader adapter optimization is post-v0.1 roadmap)
1680
- - [x] hybrid ranking + include-linked option (`--include-linked` lexical baseline implemented for keyword mode and hybrid lexical blending; deterministic hybrid lexical+semantic blend with configurable `search.hybrid_semantic_weight` implemented; deterministic exact-title token lexical boost implemented; configurable multi-factor lexical tuning via `search.tuning` implemented; broader advanced semantic/hybrid tuning is post-v0.1 roadmap)
1681
- - [x] reindex command (keyword baseline complete; semantic/hybrid embedding+vector upsert implemented; mutation command paths invalidate stale keyword artifacts, trigger best-effort semantic embedding refresh for affected item IDs, and prune vectors for missing/deleted IDs when semantic configuration is available)
1682
-
1683
- Definition of Done:
1684
-
1685
- - Search works in keyword-only and semantic/hybrid mode
1686
- - item mutations trigger search-index freshness via deterministic cache invalidation plus best-effort semantic embedding refresh for affected item IDs when semantic configuration is available, including pruning vectors for missing/deleted affected IDs, with explicit reindex workflows retained for full rebuilds
1687
-
1688
- ### Milestone 5 - Extension System + Built-ins
1689
-
1690
- Checklist:
1691
-
1692
- - [x] extension manifest loader + sandboxed execution boundary (deterministic manifest discovery, precedence, failure-isolated runtime loading, realpath/symlink-resolved entry containment enforcement, command-handler context snapshot isolation for `args`/`options`/`global`, per-hook context snapshot isolation, and dynamic extension command loose-option parsing hardening (null-prototype option maps + prototype-pollution key rejection) are implemented; broader command sandbox API surface is post-v0.1 roadmap)
1693
- - [x] hook lifecycle (extension `activate(api)` baseline with deterministic hook registration is implemented; registration now validates hook handlers as functions at activation time, per-hook context snapshot isolation prevents mutation leakage across hook callbacks and caller state, and `beforeCommand`/`afterCommand` command-lifecycle execution plus baseline read/write/index call-site wiring for core item-store reads/writes, create/restore item and history writes, settings read/write operations, history/activity history-directory scans and history-stream reads, health history-directory scans plus history-stream path dispatch, search item/linked reads, reindex flows, stats/health/gc command file-system paths (including `pm gc` onIndex dispatch with mode `gc` and deterministic cache-target totals), lock file read/write/unlink operations, init directory bootstrap ensure-write dispatch, and first-party Beads/Todos package source/item/history file operations are implemented)
1694
- - [x] renderer and command extension points (deterministic core-command override + renderer override registration/dispatch is implemented with failure containment, extension command handlers for declared command paths including dynamically surfaced non-core paths are implemented, dynamic command help now surfaces `registerFlags` metadata deterministically, deep snapshot isolation for override/renderer result contexts is implemented, and override/renderer execution now includes cloned command `args`/`options`/`global` snapshots plus `pm_root` metadata for contextual deterministic extension output behavior)
1695
- - [x] first-party Beads import package (`packages/pm-beads`) with install-only command surfacing through `pm install`, Beads JSONL field mapping, deterministic defaults, `op: "import"` history entries, and parity polish implemented)
1696
- - [x] first-party Todos import/export package (`packages/pm-todos`) with install-only command surfacing through `pm install`, todos markdown round-trip, canonical optional metadata preservation including planning/workflow and issue fields, hierarchical ID preservation, and `med` alias normalization implemented)
1697
- - [x] Pi tool wrapper extension source module (Pi agent extension module at `.pi/extensions/pm-cli/index.ts` with full v0.1 action dispatch parity, including `completion` + `shell` mapping, camelCase parameter surface for all canonical scalar metadata, explicit empty-string passthrough, numeric-flag stringification, claim/release parity, packaged CLI fallback, and distribution packaging polish implemented)
1698
-
1699
- Definition of Done:
1700
-
1701
- - Project/global precedence verified
1702
- - failing extension reported in `pm health` without core corruption
1703
-
1704
- ### Milestone 6 - Hardening + Release Readiness
1705
-
1706
- Checklist:
1707
-
1708
- - [x] CI matrix finalized (ubuntu/macos/windows Node 20, ubuntu Node 22, ubuntu Node 24)
1709
- - [x] fixture corpus for restore/import/search
1710
- - [x] command help and pm-data-driven runtime checks validated in tests
1711
- - [x] repository layout refactor (`src/cli`, `src/core`, `src/types`)
1712
- - [x] sandboxed integration harness (`withTempPmPath`)
1713
- - [x] sandboxed pm-runner (`scripts/run-tests.mjs`) for `pm test` and `pm test-all` safety
1714
- - [x] installer scripts (`scripts/install.sh`, `scripts/install.ps1`) with post-install `pm --version` availability verification
1715
- - [x] npm packaging allowlist + prepublish build guard
1716
- - [x] community docs baseline (`LICENSE`, `CHANGELOG.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`)
1717
- - [x] shell completion command (`pm completion bash|zsh|fish`)
1718
- - [x] automated npm release workflow (`.github/workflows/release.yml`) triggered on `v*.*.*` tags
1719
-
1720
- Definition of Done:
1721
-
1722
- - All required commands and tests passing
1723
- - pm data, runtime behavior, and user-facing docs kept coherent
1724
-
1725
- ## 22) Open Assumptions and Clarifications Captured
1726
-
1727
- - Imported Beads dependency types outside canonical set are mapped best-effort:
1728
- - `parent-child` -> `parent`/`child` directional mapping based on source context
1729
- - unknown values retained in import metadata notes if lossy mapping is required
1730
- - Hierarchical IDs from imports are preserved verbatim; new IDs generated by core default to flat `prefix-token`.
1731
- - TOON formatting follows deterministic encoding with stable object keys; internal serializer may use a thin compatibility layer to ensure strict consistency across Node versions.
1732
- - For `create`, `before_hash` is computed from the legacy-compatible canonical empty hash document: `{ "front_matter": {}, "body": "" }` (history patch payloads use `metadata` paths).
1733
- - If create item write succeeds but history append fails, implementation MUST rollback the new item file before returning failure.
1734
- - ID normalization helper behavior (`#` prefix, missing configured prefix, case-insensitive input) is required in core utilities even before all commands expose it.