@unbrained/pm-cli 2026.5.6 → 2026.5.11

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 (268) hide show
  1. package/.agents/pm/extensions/.managed-extensions.json +2 -2
  2. package/.agents/pm/extensions/beads/runtime.js +4 -4
  3. package/.agents/pm/extensions/beads/runtime.ts +5 -5
  4. package/.agents/pm/extensions/todos/runtime.js +7 -7
  5. package/.agents/pm/extensions/todos/runtime.ts +10 -10
  6. package/.agents/skills/HARNESS_COMPATIBILITY.md +45 -0
  7. package/.agents/skills/README.md +21 -0
  8. package/.agents/skills/pm-developer/SKILL.md +73 -0
  9. package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +48 -0
  10. package/.agents/skills/pm-developer/references/PROMPTS.md +17 -0
  11. package/.agents/skills/pm-extensions/SKILL.md +57 -0
  12. package/.agents/skills/pm-extensions/references/LIFECYCLE.md +40 -0
  13. package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +25 -0
  14. package/.agents/skills/pm-sdk/SKILL.md +50 -0
  15. package/.agents/skills/pm-sdk/references/INTEGRATION_CHECKLIST.md +31 -0
  16. package/.agents/skills/pm-sdk/references/PROMPTS.md +13 -0
  17. package/.agents/skills/pm-user/SKILL.md +59 -0
  18. package/.agents/skills/pm-user/references/PROMPTS.md +17 -0
  19. package/.agents/skills/pm-user/references/WORKFLOWS.md +35 -0
  20. package/.claude-plugin/marketplace.json +38 -0
  21. package/.pi/README.md +35 -0
  22. package/.pi/agents/pm-triage-agent.md +19 -0
  23. package/.pi/agents/pm-verification-agent.md +21 -0
  24. package/.pi/chains/pm-native-delivery.chain.md +11 -0
  25. package/.pi/extensions/pm-cli/index.js +387 -0
  26. package/.pi/prompts/pm-workflow.md +5 -0
  27. package/.pi/skills/pm-native/SKILL.md +44 -0
  28. package/.pi/skills/pm-release/SKILL.md +35 -0
  29. package/AGENTS.md +1 -1
  30. package/CHANGELOG.md +13 -0
  31. package/PRD.md +16 -16
  32. package/README.md +30 -4
  33. package/dist/cli/argv-utils.d.ts +5 -0
  34. package/dist/cli/argv-utils.js +34 -0
  35. package/dist/cli/argv-utils.js.map +1 -0
  36. package/dist/cli/bootstrap-args.d.ts +15 -0
  37. package/dist/cli/bootstrap-args.js +211 -0
  38. package/dist/cli/bootstrap-args.js.map +1 -1
  39. package/dist/cli/commander-usage.js +109 -3
  40. package/dist/cli/commander-usage.js.map +1 -1
  41. package/dist/cli/commands/claim.js +6 -6
  42. package/dist/cli/commands/claim.js.map +1 -1
  43. package/dist/cli/commands/close.js +9 -9
  44. package/dist/cli/commands/close.js.map +1 -1
  45. package/dist/cli/commands/comments.d.ts +2 -0
  46. package/dist/cli/commands/comments.js +57 -8
  47. package/dist/cli/commands/comments.js.map +1 -1
  48. package/dist/cli/commands/completion.js +40 -7
  49. package/dist/cli/commands/completion.js.map +1 -1
  50. package/dist/cli/commands/config.js +6 -3
  51. package/dist/cli/commands/config.js.map +1 -1
  52. package/dist/cli/commands/contracts.d.ts +19 -0
  53. package/dist/cli/commands/contracts.js +36 -1
  54. package/dist/cli/commands/contracts.js.map +1 -1
  55. package/dist/cli/commands/create.d.ts +2 -2
  56. package/dist/cli/commands/create.js +116 -55
  57. package/dist/cli/commands/create.js.map +1 -1
  58. package/dist/cli/commands/docs.js +13 -6
  59. package/dist/cli/commands/docs.js.map +1 -1
  60. package/dist/cli/commands/extension.d.ts +3 -1
  61. package/dist/cli/commands/extension.js +174 -2
  62. package/dist/cli/commands/extension.js.map +1 -1
  63. package/dist/cli/commands/files.js +19 -12
  64. package/dist/cli/commands/files.js.map +1 -1
  65. package/dist/cli/commands/get.js +5 -5
  66. package/dist/cli/commands/get.js.map +1 -1
  67. package/dist/cli/commands/guide.d.ts +55 -0
  68. package/dist/cli/commands/guide.js +260 -0
  69. package/dist/cli/commands/guide.js.map +1 -0
  70. package/dist/cli/commands/health.js +1 -1
  71. package/dist/cli/commands/health.js.map +1 -1
  72. package/dist/cli/commands/history.js +30 -10
  73. package/dist/cli/commands/history.js.map +1 -1
  74. package/dist/cli/commands/index.d.ts +1 -0
  75. package/dist/cli/commands/index.js +1 -0
  76. package/dist/cli/commands/index.js.map +1 -1
  77. package/dist/cli/commands/init.d.ts +2 -0
  78. package/dist/cli/commands/init.js +21 -1
  79. package/dist/cli/commands/init.js.map +1 -1
  80. package/dist/cli/commands/learnings.js +3 -3
  81. package/dist/cli/commands/learnings.js.map +1 -1
  82. package/dist/cli/commands/metadata-normalizers.d.ts +4 -0
  83. package/dist/cli/commands/metadata-normalizers.js +37 -0
  84. package/dist/cli/commands/metadata-normalizers.js.map +1 -0
  85. package/dist/cli/commands/notes.js +3 -3
  86. package/dist/cli/commands/notes.js.map +1 -1
  87. package/dist/cli/commands/reindex.js +180 -156
  88. package/dist/cli/commands/reindex.js.map +1 -1
  89. package/dist/cli/commands/restore.d.ts +2 -2
  90. package/dist/cli/commands/restore.js +44 -24
  91. package/dist/cli/commands/restore.js.map +1 -1
  92. package/dist/cli/commands/search.d.ts +2 -0
  93. package/dist/cli/commands/search.js +45 -26
  94. package/dist/cli/commands/search.js.map +1 -1
  95. package/dist/cli/commands/test-all.d.ts +2 -0
  96. package/dist/cli/commands/test-all.js +2 -0
  97. package/dist/cli/commands/test-all.js.map +1 -1
  98. package/dist/cli/commands/test.d.ts +1 -0
  99. package/dist/cli/commands/test.js +13 -5
  100. package/dist/cli/commands/test.js.map +1 -1
  101. package/dist/cli/commands/update.js +188 -157
  102. package/dist/cli/commands/update.js.map +1 -1
  103. package/dist/cli/commands/validate.js +1 -1
  104. package/dist/cli/commands/validate.js.map +1 -1
  105. package/dist/cli/error-guidance.d.ts +9 -1
  106. package/dist/cli/error-guidance.js +147 -6
  107. package/dist/cli/error-guidance.js.map +1 -1
  108. package/dist/cli/guide-topics.d.ts +25 -0
  109. package/dist/cli/guide-topics.js +283 -0
  110. package/dist/cli/guide-topics.js.map +1 -0
  111. package/dist/cli/help-content.js +25 -1
  112. package/dist/cli/help-content.js.map +1 -1
  113. package/dist/cli/help-json-payload.js +11 -1
  114. package/dist/cli/help-json-payload.js.map +1 -1
  115. package/dist/cli/main.js +69 -6
  116. package/dist/cli/main.js.map +1 -1
  117. package/dist/cli/register-list-query.js +38 -1
  118. package/dist/cli/register-list-query.js.map +1 -1
  119. package/dist/cli/register-mutation.js +17 -4
  120. package/dist/cli/register-mutation.js.map +1 -1
  121. package/dist/cli/register-setup.js +15 -1
  122. package/dist/cli/register-setup.js.map +1 -1
  123. package/dist/cli/telemetry-flush.d.ts +2 -0
  124. package/dist/cli/telemetry-flush.js +4 -0
  125. package/dist/cli/telemetry-flush.js.map +1 -0
  126. package/dist/cli.js +1 -2
  127. package/dist/cli.js.map +1 -1
  128. package/dist/core/extensions/extension-types.d.ts +72 -0
  129. package/dist/core/extensions/extension-types.js +24 -0
  130. package/dist/core/extensions/extension-types.js.map +1 -1
  131. package/dist/core/extensions/loader.d.ts +1 -0
  132. package/dist/core/extensions/loader.js +766 -7
  133. package/dist/core/extensions/loader.js.map +1 -1
  134. package/dist/core/history/history.js +32 -11
  135. package/dist/core/history/history.js.map +1 -1
  136. package/dist/core/item/item-format.d.ts +2 -2
  137. package/dist/core/item/item-format.js +16 -16
  138. package/dist/core/item/item-format.js.map +1 -1
  139. package/dist/core/lock/lock.js +2 -0
  140. package/dist/core/lock/lock.js.map +1 -1
  141. package/dist/core/schema/runtime-field-filters.js +1 -1
  142. package/dist/core/schema/runtime-field-filters.js.map +1 -1
  143. package/dist/core/schema/runtime-field-values.js +2 -2
  144. package/dist/core/schema/runtime-field-values.js.map +1 -1
  145. package/dist/core/schema/runtime-schema.d.ts +1 -1
  146. package/dist/core/schema/runtime-schema.js +3 -3
  147. package/dist/core/schema/runtime-schema.js.map +1 -1
  148. package/dist/core/search/cache.js +7 -21
  149. package/dist/core/search/cache.js.map +1 -1
  150. package/dist/core/search/corpus.d.ts +13 -0
  151. package/dist/core/search/corpus.js +74 -0
  152. package/dist/core/search/corpus.js.map +1 -0
  153. package/dist/core/search/embedding-batches.js +90 -30
  154. package/dist/core/search/embedding-batches.js.map +1 -1
  155. package/dist/core/sentry/instrument.d.ts +18 -1
  156. package/dist/core/sentry/instrument.js +128 -12
  157. package/dist/core/sentry/instrument.js.map +1 -1
  158. package/dist/core/shared/constants.d.ts +1 -1
  159. package/dist/core/shared/constants.js +21 -1
  160. package/dist/core/shared/constants.js.map +1 -1
  161. package/dist/core/shared/errors.d.ts +8 -0
  162. package/dist/core/shared/errors.js.map +1 -1
  163. package/dist/core/shared/levenshtein.d.ts +1 -0
  164. package/dist/core/shared/levenshtein.js +37 -0
  165. package/dist/core/shared/levenshtein.js.map +1 -0
  166. package/dist/core/store/front-matter-cache.d.ts +1 -1
  167. package/dist/core/store/front-matter-cache.js +13 -13
  168. package/dist/core/store/front-matter-cache.js.map +1 -1
  169. package/dist/core/store/item-format-migration.js +5 -2
  170. package/dist/core/store/item-format-migration.js.map +1 -1
  171. package/dist/core/store/item-store.js +16 -15
  172. package/dist/core/store/item-store.js.map +1 -1
  173. package/dist/core/store/paths.js +35 -2
  174. package/dist/core/store/paths.js.map +1 -1
  175. package/dist/core/store/settings.js +216 -2
  176. package/dist/core/store/settings.js.map +1 -1
  177. package/dist/core/telemetry/runtime.d.ts +1 -0
  178. package/dist/core/telemetry/runtime.js +102 -3
  179. package/dist/core/telemetry/runtime.js.map +1 -1
  180. package/dist/core/test/item-test-run-tracking.js +2 -2
  181. package/dist/core/test/item-test-run-tracking.js.map +1 -1
  182. package/dist/mcp/server.d.ts +2 -0
  183. package/dist/mcp/server.js +407 -0
  184. package/dist/mcp/server.js.map +1 -0
  185. package/dist/pi/native.d.ts +5 -0
  186. package/dist/pi/native.js +236 -0
  187. package/dist/pi/native.js.map +1 -0
  188. package/dist/sdk/cli-contracts.d.ts +24 -2
  189. package/dist/sdk/cli-contracts.js +317 -2
  190. package/dist/sdk/cli-contracts.js.map +1 -1
  191. package/dist/sdk/index.d.ts +12 -1
  192. package/dist/sdk/index.js +8 -1
  193. package/dist/sdk/index.js.map +1 -1
  194. package/dist/types.d.ts +51 -2
  195. package/dist/types.js.map +1 -1
  196. package/docs/AGENT_GUIDE.md +15 -0
  197. package/docs/ARCHITECTURE.md +2 -2
  198. package/docs/CLAUDE_CODE_PLUGIN.md +225 -0
  199. package/docs/CODEX_PLUGIN.md +33 -0
  200. package/docs/COMMANDS.md +6 -2
  201. package/docs/CONFIGURATION.md +2 -8
  202. package/docs/EXTENSIONS.md +688 -0
  203. package/docs/MIGRATION_CLI_SIMPLIFICATION.md +64 -0
  204. package/docs/PI_PACKAGE.md +141 -0
  205. package/docs/QUICKSTART.md +1 -0
  206. package/docs/README.md +30 -1
  207. package/docs/RELEASING.md +4 -2
  208. package/docs/SDK.md +444 -2
  209. package/docs/examples/ci/github-actions-pm-extension-gate.yml +53 -0
  210. package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +41 -0
  211. package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +45 -0
  212. package/docs/examples/policy-restricted-extension/README.md +74 -0
  213. package/docs/examples/policy-restricted-extension/index.js +21 -0
  214. package/docs/examples/policy-restricted-extension/manifest.json +21 -0
  215. package/docs/examples/policy-restricted-extension/package.json +8 -0
  216. package/docs/examples/sdk-app-embedding/README.md +39 -0
  217. package/docs/examples/sdk-app-embedding/package.json +9 -0
  218. package/docs/examples/sdk-app-embedding/run-embedded-pm.mjs +61 -0
  219. package/docs/examples/sdk-contract-consumer/README.md +57 -0
  220. package/docs/examples/sdk-contract-consumer/inspect-contracts.mjs +47 -0
  221. package/docs/examples/sdk-contract-consumer/package.json +10 -0
  222. package/docs/examples/starter-extension/README.md +57 -42
  223. package/docs/examples/starter-extension/manifest.json +15 -0
  224. package/marketplace.json +34 -0
  225. package/package.json +38 -4
  226. package/plugins/pm-cli-claude/.claude-plugin/plugin.json +23 -0
  227. package/plugins/pm-cli-claude/.mcp.json +12 -0
  228. package/plugins/pm-cli-claude/README.md +225 -0
  229. package/plugins/pm-cli-claude/agents/pm-coordinator.md +48 -0
  230. package/plugins/pm-cli-claude/agents/pm-delivery-chain.md +88 -0
  231. package/plugins/pm-cli-claude/agents/pm-triage-agent.md +83 -0
  232. package/plugins/pm-cli-claude/agents/pm-verification-agent.md +88 -0
  233. package/plugins/pm-cli-claude/commands/pm-audit.md +39 -0
  234. package/plugins/pm-cli-claude/commands/pm-calendar.md +41 -0
  235. package/plugins/pm-cli-claude/commands/pm-close-task.md +20 -0
  236. package/plugins/pm-cli-claude/commands/pm-developer.md +38 -0
  237. package/plugins/pm-cli-claude/commands/pm-init.md +44 -0
  238. package/plugins/pm-cli-claude/commands/pm-list.md +39 -0
  239. package/plugins/pm-cli-claude/commands/pm-new.md +36 -0
  240. package/plugins/pm-cli-claude/commands/pm-planner.md +51 -0
  241. package/plugins/pm-cli-claude/commands/pm-release.md +41 -0
  242. package/plugins/pm-cli-claude/commands/pm-search.md +21 -0
  243. package/plugins/pm-cli-claude/commands/pm-start-task.md +27 -0
  244. package/plugins/pm-cli-claude/commands/pm-status.md +15 -0
  245. package/plugins/pm-cli-claude/commands/pm-triage.md +35 -0
  246. package/plugins/pm-cli-claude/commands/pm-workflow.md +49 -0
  247. package/plugins/pm-cli-claude/hooks/hooks.json +17 -0
  248. package/plugins/pm-cli-claude/hooks/session-start.mjs +120 -0
  249. package/plugins/pm-cli-claude/scripts/pm-mcp-server.mjs +60 -0
  250. package/plugins/pm-cli-claude/skills/pm-audit/SKILL.md +88 -0
  251. package/plugins/pm-cli-claude/skills/pm-developer/SKILL.md +116 -0
  252. package/plugins/pm-cli-claude/skills/pm-planner/SKILL.md +118 -0
  253. package/plugins/pm-cli-claude/skills/pm-release/SKILL.md +83 -0
  254. package/plugins/pm-cli-claude/skills/pm-workflow/SKILL.md +148 -0
  255. package/plugins/pm-cli-codex/.codex-plugin/plugin.json +45 -0
  256. package/plugins/pm-cli-codex/.mcp.json +14 -0
  257. package/plugins/pm-cli-codex/README.md +30 -0
  258. package/plugins/pm-cli-codex/assets/pm-cli-small.svg +4 -0
  259. package/plugins/pm-cli-codex/commands/pm-audit.md +8 -0
  260. package/plugins/pm-cli-codex/commands/pm-close-task.md +9 -0
  261. package/plugins/pm-cli-codex/commands/pm-start-task.md +9 -0
  262. package/plugins/pm-cli-codex/scripts/pm-mcp-server.mjs +54 -0
  263. package/plugins/pm-cli-codex/skills/pm-auditor/SKILL.md +21 -0
  264. package/plugins/pm-cli-codex/skills/pm-auditor/agents/openai.yaml +6 -0
  265. package/plugins/pm-cli-codex/skills/pm-native/SKILL.md +57 -0
  266. package/plugins/pm-cli-codex/skills/pm-native/agents/openai.yaml +6 -0
  267. package/plugins/pm-cli-codex/skills/pm-release/SKILL.md +19 -0
  268. package/plugins/pm-cli-codex/skills/pm-release/agents/openai.yaml +6 -0
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: pm-native
3
+ description: Native pm integration for Pi. Use when planning, claiming, updating, linking files/tests/docs, validating, or closing pm CLI work through the Pi pm tool instead of shelling out to the pm CLI.
4
+ license: MIT
5
+ compatibility: Pi coding-agent with the @unbrained/pm-cli Pi package installed.
6
+ metadata:
7
+ owner: unbrained
8
+ domain: pm-cli
9
+ scope: pi-native
10
+ ---
11
+
12
+ # pm Native for Pi
13
+
14
+ Use the `pm` tool for project-management operations. Do not run `pm ...` through bash when the native tool is available. In interactive Pi, use `/pm-board`, `/pm-item <id>`, `/pm-history <id>`, `/pm-actions`, and `/pm-workflows` when the user would benefit from seeing tracker state in the TUI.
15
+
16
+ ## Required Loop
17
+
18
+ 1. Orient before creating work:
19
+ - `pm` action `context` with `limit: 10`
20
+ - `pm` action `search` with relevant keywords
21
+ - `pm` action `list-open` and `list-in-progress`
22
+ 2. Claim work with action `claim` or `start-task`.
23
+ 3. Mutate with explicit `author`.
24
+ 4. Link evidence:
25
+ - action `files` with `add`
26
+ - action `docs` with `add`
27
+ - action `test` with sandbox-safe commands
28
+ 5. Verify with action `test`, `validate`, and project test commands when appropriate.
29
+ 6. Close with action `close-task` or `close`, then release if needed.
30
+ 7. For exact feature support, call `pm` action `contracts` or `guide` instead of guessing flags.
31
+
32
+ ## Common Tool Calls
33
+
34
+ - Context: `{ "action": "context", "limit": 10 }`
35
+ - Search: `{ "action": "search", "query": "pi native extension", "limit": 10 }`
36
+ - Claim: `{ "action": "claim", "id": "pm-1234", "author": "pi-agent" }`
37
+ - Link file: `{ "action": "files", "id": "pm-1234", "add": ["path=src/file.ts,scope=project,note=implementation"], "author": "pi-agent" }`
38
+ - Add comment: `{ "action": "comments", "id": "pm-1234", "add": ["Evidence: build and tests passed"], "author": "pi-agent" }`
39
+ - Close: `{ "action": "close-task", "id": "pm-1234", "text": "All acceptance criteria met", "author": "pi-agent", "validateClose": "warn" }`
40
+ - Extension lifecycle: `{ "action": "extension", "install": true, "target": "todos", "scope": "project" }`
41
+ - Templates: `{ "action": "templates-list" }`
42
+ - Managed tests: `{ "action": "test-runs-list" }`
43
+
44
+ Use `pm guide` topics through action `guide` for deeper command docs. The bundled Pi package also provides project subagent definitions (`pm-triage-agent`, `pm-verification-agent`) and a `pm-native-delivery` chain for installations that use pi-subagents.
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: pm-release
3
+ description: Release-readiness workflow for pm projects in Pi. Use when running final validation, coverage, package checks, GitHub checks, and closure evidence through the native pm integration.
4
+ license: MIT
5
+ compatibility: Pi coding-agent with the @unbrained/pm-cli Pi package installed.
6
+ metadata:
7
+ owner: unbrained
8
+ domain: pm-cli
9
+ scope: release
10
+ ---
11
+
12
+ # pm Release Workflow for Pi
13
+
14
+ Use the native `pm` tool for tracker state and linked evidence. Use shell only for non-pm project build/test commands.
15
+
16
+ ## Release Checklist
17
+
18
+ 1. `pm` action `context` with `depth: "standard"`.
19
+ 2. Run linked tests with `pm` action `test` and `run: true` for active work items.
20
+ 3. Run validation with `pm` action `validate`, usually `checkResolution: true`, `checkHistoryDrift: true`, and relevant file checks.
21
+ 4. Run package install/discovery smoke for Pi packages:
22
+ - local: `pi install -l .` or `pi -e .`
23
+ - npm after publish: `pi install npm:@unbrained/pm-cli`
24
+ 5. Use `gh` only to inspect GitHub checks after pushing.
25
+ 6. Add a `comments` evidence entry with exact command summaries.
26
+ 7. Close/release the pm item with `close-task` once acceptance criteria are met.
27
+
28
+ ## Evidence Format
29
+
30
+ Record:
31
+ - Changed package resources (`pi.extensions`, `pi.skills`, `pi.prompts`)
32
+ - Native pm tool smoke result
33
+ - Build/test/coverage result
34
+ - Pi install smoke result
35
+ - GitHub checks status
package/AGENTS.md CHANGED
@@ -7,7 +7,7 @@ This document defines how coding agents must use `pm` for planning, execution, a
7
7
  - Use `pm` as the system of record for project work.
8
8
  - Prefer deterministic, script-friendly command usage (`--json` when strict parsing is needed).
9
9
  - Default to TOON output when human + model readability and low token use are desired (calendar command is the intentional exception and defaults to markdown unless overridden).
10
- - Treat TOON as the canonical item-document format in this repo; `front_matter` is an internal model key, while TOON item metadata is stored as top-level object fields.
10
+ - Treat TOON as the canonical item-document format in this repo; item metadata is modeled as `metadata` internally and stored as top-level object fields in TOON.
11
11
  - Never make destructive item changes outside `pm` mutations.
12
12
  - Every mutation must produce a history entry.
13
13
 
package/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2026.5.11] - 2026-05-11
11
+
12
+ ### Added
13
+ - Claude Code plugin v1.3.0: added `pm-triage-agent`, `pm-verification-agent`, and `pm-delivery-chain` subagents for full end-to-end pm workflow orchestration without leaving Claude Code.
14
+ - Claude Code plugin: registered `pm` as the canonical marketplace ID so the plugin installs via `/plugin install pm-cli@pm` (both `pm` and `pm-cli` marketplace IDs resolve to the same plugin).
15
+ - Claude Code plugin: rewrote session-start hook to use native `dist/pi/native.js` modules when available (repo checkout), falling back to `npx @unbrained/pm-cli` — no global `pm` CLI required.
16
+
17
+ ## [2026.5.10] - 2026-05-10
18
+
19
+ ### Changed
20
+ - Added top-level `ok` fields to `pm test --run --json` and `pm test-all --json` so agents can gate linked-test execution without parsing every result row.
21
+ - Capped and truncated semantic reindex embedding payloads for local Ollama providers, with adaptive timeout splitting, to avoid full-corpus reindex failures on large item bodies.
22
+
10
23
  ## [2026.5.6] - 2026-05-06
11
24
 
12
25
  ### Changed
package/PRD.md CHANGED
@@ -73,7 +73,7 @@ Existing trackers either rely on hosted backends, store state in non-diff-friend
73
73
 
74
74
  From `todos.ts`:
75
75
 
76
- - Item file format = JSON front-matter at file start, blank line, then markdown body.
76
+ - Legacy import format = JSON front-matter at file start, blank line, then markdown body.
77
77
  - ID normalization accepts optional `#` and optional prefix.
78
78
  - Claim/release is represented in-record (`assignee`).
79
79
  - Locking model:
@@ -246,16 +246,16 @@ Constraints:
246
246
 
247
247
  ## 7) Item File Format
248
248
 
249
- Each item is one document at `<type-folder>/<id>.toon` (default) or `<type-folder>/<id>.md` (JSON+Markdown alternative).
249
+ Each item is one TOON document at `<type-folder>/<id>.toon`. Legacy `<type-folder>/<id>.md` files are read only for migration.
250
250
 
251
251
  Format:
252
252
 
253
- 1. TOON root-object metadata keys (default) or JSON object metadata block (markdown format).
254
- 2. Optional `body` field / markdown body.
253
+ 1. TOON root-object metadata keys.
254
+ 2. Optional `body` field.
255
255
 
256
256
  ### 7.1 Canonical item-metadata schema
257
257
 
258
- `front_matter` remains the internal field name in TypeScript (`ItemDocument.front_matter`), but TOON documents store the same metadata as top-level keys.
258
+ `metadata` is the internal TypeScript field name (`ItemDocument.metadata`), and TOON documents store the same metadata as top-level keys.
259
259
 
260
260
  Required fields:
261
261
 
@@ -500,7 +500,7 @@ Canonical patch document shape:
500
500
 
501
501
  ```json
502
502
  {
503
- "front_matter": { "...": "..." },
503
+ "metadata": { "...": "..." },
504
504
  "body": "markdown text"
505
505
  }
506
506
  ```
@@ -517,7 +517,7 @@ Canonical patch document shape:
517
517
 
518
518
  1. Resolve item or matching history stream for ID and load full history.
519
519
  2. Replay patches from initial create through target version/timestamp.
520
- 3. Rebuild exact canonical document (`front_matter` + `body`).
520
+ 3. Rebuild exact canonical document (`metadata` + `body`).
521
521
  4. Write item atomically.
522
522
  5. Append a `restore` history event with patch from pre-restore state to restored state.
523
523
 
@@ -717,7 +717,7 @@ Help and error UX note:
717
717
  - `pm restore <ID> <TIMESTAMP|VERSION>`
718
718
  - `pm config <project|global> set definition-of-done --criterion <text>`
719
719
  - `pm config <project|global> get definition-of-done`
720
- - `pm config <project|global> set item-format --format toon|json_markdown`
720
+ - `pm config <project|global> set item-format --format toon`
721
721
  - `pm config <project|global> get item-format`
722
722
  - `pm config <project|global> set history-missing-stream-policy --policy auto_create|strict_error`
723
723
  - `pm config <project|global> get history-missing-stream-policy`
@@ -970,7 +970,7 @@ Contract compatibility policy keeps command names/flags/aliases stable while all
970
970
  | `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 |
971
971
  | `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) |
972
972
  | `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 }` |
973
- | `pm todos import --folder <path?>` | optional todos markdown source folder (defaults to `.pi/todos`); preserves canonical optional `ItemFrontMatter` metadata when present and applies deterministic defaults for missing PM fields | `{ ok, folder, imported, skipped, ids, warnings }` |
973
+ | `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 }` |
974
974
  | `pm todos export --folder <path?>` | optional todos markdown destination folder (defaults to `.pi/todos`) | `{ ok, folder, exported, ids, warnings }` |
975
975
  | `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 }` |
976
976
  | `pm templates save <NAME> ...` | template name + create-compatible option payload (subset of create flags, including repeatable entries) | `{ name, path, template, saved_at }` |
@@ -986,7 +986,7 @@ Contract compatibility policy keeps command names/flags/aliases stable while all
986
986
  | `pm start-task <ID>` | lifecycle alias command (`claim` + `update --status in_progress`) with optional `--author`, `--message`, `--force` | `{ id, action: "start_task", claim, update }` |
987
987
  | `pm pause-task <ID>` | lifecycle alias command (`update --status open` + `release`) with optional `--author`, `--message`, `--force` | `{ id, action: "pause_task", update, release }` |
988
988
  | `pm close-task <ID> <TEXT>` | lifecycle alias command (`close` + `release`) with optional `--author`, `--message`, `--validate-close`, `--force` | `{ id, action: "close_task", close, release }` |
989
- | `pm comments <ID> [TEXT] --add/--limit` | id + optional positional comment text shorthand + comment 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 comments | `{ id, comments, count }` |
989
+ | `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 }` |
990
990
  | `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 |
991
991
  | `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 }` |
992
992
  | `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 }` |
@@ -1010,7 +1010,7 @@ Contract compatibility policy keeps command names/flags/aliases stable while all
1010
1010
 
1011
1011
  List command row projection:
1012
1012
 
1013
- - Default `list*` rows contain `ItemFrontMatter` fields only.
1013
+ - Default `list*` rows contain `ItemMetadata` fields only.
1014
1014
  - `--compact` projects deterministic compact fields (`id`, `title`, `status`, `type`, `priority`, `parent`, `updated_at`).
1015
1015
  - `--fields <csv>` projects caller-selected list fields.
1016
1016
  - With `--include-body`, each row additionally includes `body` and `filters.include_body` is `true` (`null` when omitted in JSON; omitted in sparse TOON).
@@ -1027,8 +1027,8 @@ Examples:
1027
1027
 
1028
1028
  - `list*`:
1029
1029
  - `{ items, count, filters, now }`
1030
- - default rows: `ItemFrontMatter`
1031
- - with `--include-body`: `ItemFrontMatter + body`
1030
+ - default rows: `ItemMetadata`
1031
+ - with `--include-body`: `ItemMetadata + body`
1032
1032
  - `search`:
1033
1033
  - `{ query, mode, items, count, filters, now }`
1034
1034
  - `get`:
@@ -1369,7 +1369,7 @@ Behavior:
1369
1369
  - `title -> title`
1370
1370
  - `body -> body`
1371
1371
  - imported IDs, including hierarchical suffixes such as `pm-legacy.1.2`, are preserved verbatim when provided in todos front matter
1372
- - canonical PM front-matter 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
+ - 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`)
1373
1373
  - `confidence`, `risk`, and `severity` text aliases normalize deterministically (`med -> medium`)
1374
1374
  - Missing PM fields get deterministic defaults:
1375
1375
  - `description = ""`
@@ -1653,7 +1653,7 @@ Definition of Done:
1653
1653
  Checklist:
1654
1654
 
1655
1655
  - [x] Item schema model + validation
1656
- - [x] Parser/serializer for markdown item files
1656
+ - [x] Parser/serializer for TOON item files plus legacy markdown migration reader
1657
1657
  - [x] ID generation + normalization
1658
1658
  - [x] Lock acquire/release with TTL and conflict handling
1659
1659
  - [x] Core commands: init/create/get/update/append/claim/release/close/delete complete
@@ -1750,6 +1750,6 @@ Definition of Done:
1750
1750
  - unknown values retained in import metadata notes if lossy mapping is required
1751
1751
  - Hierarchical IDs from imports are preserved verbatim; new IDs generated by core default to flat `prefix-token`.
1752
1752
  - TOON formatting follows deterministic encoding with stable object keys; internal serializer may use a thin compatibility layer to ensure strict consistency across Node versions.
1753
- - For `create`, `before_hash` is computed from canonical empty document: `{ "front_matter": {}, "body": "" }`.
1753
+ - For `create`, `before_hash` is computed from the legacy-compatible canonical empty hash document: `{ "front_matter": {}, "body": "" }` (history patch payloads use `metadata` paths).
1754
1754
  - If create item write succeeds but history append fails, implementation MUST rollback the new item file before returning failure.
1755
1755
  - ID normalization helper behavior (`#` prefix, missing configured prefix, case-insensitive input) is required in core utilities even before all commands expose it.
package/README.md CHANGED
@@ -17,11 +17,22 @@
17
17
  | Settings, storage, search, and output | [Configuration](docs/CONFIGURATION.md) |
18
18
  | Safe test execution and linked tests | [Testing](docs/TESTING.md) |
19
19
  | Extension authoring | [Extensions](docs/EXTENSIONS.md) and [SDK](docs/SDK.md) |
20
+ | Pi native package | [Pi Package](docs/PI_PACKAGE.md) |
21
+ | Codex native integration | [Codex Plugin](docs/CODEX_PLUGIN.md) |
20
22
  | Maintainer release process (daily auto-release + local parity) | [Releasing](docs/RELEASING.md) |
21
23
  | Contributor internals | [Architecture](docs/ARCHITECTURE.md) |
22
24
 
23
25
  Full documentation starts at [docs/README.md](docs/README.md).
24
26
 
27
+ Use local in-CLI routing when an agent should stay inside terminal context:
28
+
29
+ ```bash
30
+ pm guide
31
+ pm guide quickstart
32
+ pm guide commands --depth standard
33
+ pm guide skills --depth deep --format markdown
34
+ ```
35
+
25
36
  ## Install
26
37
 
27
38
  `pm-cli` requires Node.js 20 or newer.
@@ -40,6 +51,22 @@ Project-local invocation also works:
40
51
  npx @unbrained/pm-cli --help
41
52
  ```
42
53
 
54
+ For Claude Code, install the native plugin (no `pm` CLI required):
55
+
56
+ ```
57
+ /plugin install pm-cli@pm
58
+ ```
59
+
60
+ This registers 18 MCP tools, 5 workflow skills, 14 slash commands, 3 subagents, hybrid TUI tracking, and a session-start context hook — all without shelling out to the `pm` CLI.
61
+
62
+ For Pi, install the native package integration after publish:
63
+
64
+ ```bash
65
+ pi install npm:@unbrained/pm-cli
66
+ ```
67
+
68
+ This registers a native `pm` tool, custom TUI panels/renderers (`/pm-board`, `/pm-item`, `/pm-history`), Pi skills, prompt templates, and optional pi-subagents setup without requiring Pi to run the `pm` shell command.
69
+
43
70
  ## 60 Second Example
44
71
 
45
72
  ```bash
@@ -78,6 +105,8 @@ pm list-in-progress --limit 20
78
105
 
79
106
  If no relevant item exists, create a parent lineage before child work, claim the child item, link changed files/docs/tests, and leave evidence comments before closing. The full workflow is in the [Agent Guide](docs/AGENT_GUIDE.md).
80
107
 
108
+ For token-aware local routing, use `pm guide workflows` and then drill into related topics (`commands`, `skills`, `release`) only when needed.
109
+
81
110
  ## Release Automation
82
111
 
83
112
  - Daily release preparation runs in `.github/workflows/auto-release.yml`.
@@ -94,6 +123,7 @@ If no relevant item exists, create a parent lineage before child work, claim the
94
123
  - Built-in types include `Epic`, `Feature`, `Task`, `Chore`, `Issue`, `Decision`, `Event`, `Reminder`, `Milestone`, and `Meeting`.
95
124
  - Output defaults to sparse TOON. Use `--json` for strict parsing.
96
125
  - `pm contracts` is the machine-readable command and schema contract surface for agents.
126
+ - `pm guide` is the local progressive-disclosure docs and skills index for agents.
97
127
 
98
128
  ## Tracker References
99
129
 
@@ -105,10 +135,6 @@ This documentation refresh is tracked through `pm`:
105
135
 
106
136
  Docs should link to relevant `pm` items, and `pm` items should link back to changed docs through `pm docs`.
107
137
 
108
- ## Privacy Boundary
109
-
110
- Private production operations material is local-only, gitignored, and intentionally not linked from public documentation or packaged release files. Keep public docs focused on user-facing CLI behavior and safe contribution workflows.
111
-
112
138
  ## License
113
139
 
114
140
  [MIT](LICENSE)
@@ -0,0 +1,5 @@
1
+ export declare function normalizeLongFlag(flag: string): string;
2
+ export declare function normalizeLongOptionFlag(token: string): string | undefined;
3
+ export declare function extractProvidedOptionFlags(argv: string[]): string[];
4
+ export declare function quoteCommandArg(arg: string): string;
5
+ export declare function renderPmCommand(argv: string[]): string;
@@ -0,0 +1,34 @@
1
+ export function normalizeLongFlag(flag) {
2
+ return `--${flag
3
+ .replace(/^--?/, "")
4
+ .replace(/_/g, "-")
5
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
6
+ .toLowerCase()}`;
7
+ }
8
+ export function normalizeLongOptionFlag(token) {
9
+ if (!token.startsWith("--")) {
10
+ return undefined;
11
+ }
12
+ const key = token.includes("=") ? token.slice(0, token.indexOf("=")) : token;
13
+ return normalizeLongFlag(key);
14
+ }
15
+ export function extractProvidedOptionFlags(argv) {
16
+ const provided = new Set();
17
+ for (const token of argv) {
18
+ const normalized = normalizeLongOptionFlag(token);
19
+ if (normalized) {
20
+ provided.add(normalized);
21
+ }
22
+ }
23
+ return [...provided].sort((left, right) => left.localeCompare(right));
24
+ }
25
+ export function quoteCommandArg(arg) {
26
+ if (/^[A-Za-z0-9._:/@=-]+$/.test(arg)) {
27
+ return arg;
28
+ }
29
+ return `"${arg.replace(/(["\\$`])/g, "\\$1")}"`;
30
+ }
31
+ export function renderPmCommand(argv) {
32
+ return `pm ${argv.map((token) => quoteCommandArg(token)).join(" ")}`;
33
+ }
34
+ //# sourceMappingURL=argv-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"argv-utils.js","sourceRoot":"/","sources":["cli/argv-utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,KAAK,IAAI;SACb,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;SACnB,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;SAClB,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,WAAW,EAAE,EAAE,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAa;IACnD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7E,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,IAAc;IACvD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,UAAU,EAAE,CAAC;YACf,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACvE,CAAC","sourcesContent":["export function normalizeLongFlag(flag: string): string {\n return `--${flag\n .replace(/^--?/, \"\")\n .replace(/_/g, \"-\")\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .toLowerCase()}`;\n}\n\nexport function normalizeLongOptionFlag(token: string): string | undefined {\n if (!token.startsWith(\"--\")) {\n return undefined;\n }\n const key = token.includes(\"=\") ? token.slice(0, token.indexOf(\"=\")) : token;\n return normalizeLongFlag(key);\n}\n\nexport function extractProvidedOptionFlags(argv: string[]): string[] {\n const provided = new Set<string>();\n for (const token of argv) {\n const normalized = normalizeLongOptionFlag(token);\n if (normalized) {\n provided.add(normalized);\n }\n }\n return [...provided].sort((left, right) => left.localeCompare(right));\n}\n\nexport function quoteCommandArg(arg: string): string {\n if (/^[A-Za-z0-9._:/@=-]+$/.test(arg)) {\n return arg;\n }\n return `\"${arg.replace(/([\"\\\\$`])/g, \"\\\\$1\")}\"`;\n}\n\nexport function renderPmCommand(argv: string[]): string {\n return `pm ${argv.map((token) => quoteCommandArg(token)).join(\" \")}`;\n}\n"]}
@@ -15,4 +15,19 @@ export declare function parseBootstrapHelpRequest(argv: string[]): BootstrapHelp
15
15
  export declare function parseBootstrapCommandName(argv: string[]): string | undefined;
16
16
  export declare function applyBootstrapPagerPolicy(argv: string[]): void;
17
17
  export declare function normalizeLegacyExtensionActionSyntax(argv: string[]): string[];
18
+ type BootstrapNormalizationReason = "legacy_extension_action" | "flag_alias" | "flag_typo" | "bare_key_value";
19
+ type BootstrapNormalizationConfidence = "high" | "medium";
20
+ export interface BootstrapNormalizationEvent {
21
+ from: string;
22
+ to: string[];
23
+ reason: BootstrapNormalizationReason;
24
+ confidence: BootstrapNormalizationConfidence;
25
+ }
26
+ export interface BootstrapInvocationNormalizationResult {
27
+ argv: string[];
28
+ commandName: string | undefined;
29
+ trace: BootstrapNormalizationEvent[];
30
+ }
31
+ export declare function normalizeBootstrapInvocation(argv: string[]): BootstrapInvocationNormalizationResult;
18
32
  export declare function parseBootstrapTypeValue(argv: string[]): string | undefined;
33
+ export {};
@@ -1,3 +1,5 @@
1
+ import { resolveSubcommandFlagContractsForCommand } from "../sdk/cli-contracts.js";
2
+ import { levenshteinDistanceWithinLimit } from "../core/shared/levenshtein.js";
1
3
  function parseBootstrapPathToken(token, next) {
2
4
  if (token === "--path") {
3
5
  if (typeof next === "string" && next.length > 0) {
@@ -220,6 +222,215 @@ export function normalizeLegacyExtensionActionSyntax(argv) {
220
222
  }
221
223
  return [...argv.slice(0, extensionIndex + 1), forcedActionFlag, ...argv.slice(extensionIndex + 2)];
222
224
  }
225
+ function normalizeFlagKeyToken(raw) {
226
+ const withoutPrefix = raw.replace(/^--?/, "");
227
+ return withoutPrefix
228
+ .replace(/_/g, "-")
229
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
230
+ .toLowerCase();
231
+ }
232
+ function toComparableFlagKey(raw) {
233
+ return normalizeFlagKeyToken(raw).replace(/-/g, "");
234
+ }
235
+ function markUnambiguousFlag(map, key, canonicalFlag) {
236
+ const existing = map.get(key);
237
+ if (existing === undefined) {
238
+ map.set(key, canonicalFlag);
239
+ return;
240
+ }
241
+ if (existing !== canonicalFlag) {
242
+ map.set(key, null);
243
+ }
244
+ }
245
+ function collectLongFlagCandidates(contract) {
246
+ const candidates = [];
247
+ const pushLongFlag = (value) => {
248
+ if (typeof value !== "string") {
249
+ return;
250
+ }
251
+ if (!value.startsWith("--")) {
252
+ return;
253
+ }
254
+ candidates.push(value);
255
+ };
256
+ pushLongFlag(contract.flag);
257
+ for (const alias of contract.aliases ?? []) {
258
+ pushLongFlag(alias);
259
+ }
260
+ return candidates;
261
+ }
262
+ function buildFlagLookup(commandName) {
263
+ const contracts = resolveSubcommandFlagContractsForCommand(commandName);
264
+ const canonicalByNormalized = new Map();
265
+ const canonicalByCompact = new Map();
266
+ const canonicalComparablesMap = new Map();
267
+ for (const contract of contracts) {
268
+ const longCandidates = collectLongFlagCandidates(contract);
269
+ if (longCandidates.length === 0) {
270
+ continue;
271
+ }
272
+ const canonicalFlag = `--${normalizeFlagKeyToken(longCandidates[0])}`;
273
+ for (const candidate of longCandidates) {
274
+ markUnambiguousFlag(canonicalByNormalized, normalizeFlagKeyToken(candidate), canonicalFlag);
275
+ markUnambiguousFlag(canonicalByCompact, toComparableFlagKey(candidate), canonicalFlag);
276
+ }
277
+ const comparable = toComparableFlagKey(canonicalFlag);
278
+ if (!canonicalComparablesMap.has(canonicalFlag)) {
279
+ canonicalComparablesMap.set(canonicalFlag, comparable);
280
+ }
281
+ }
282
+ return {
283
+ canonicalByNormalized,
284
+ canonicalByCompact,
285
+ canonicalComparables: [...canonicalComparablesMap.entries()].map(([canonicalFlag, comparable]) => ({
286
+ canonicalFlag,
287
+ comparable,
288
+ })),
289
+ };
290
+ }
291
+ function resolveCanonicalFlag(rawKey, lookup) {
292
+ const normalizedKey = normalizeFlagKeyToken(rawKey);
293
+ const direct = lookup.canonicalByNormalized.get(normalizedKey);
294
+ if (typeof direct === "string") {
295
+ return {
296
+ flag: direct,
297
+ reason: "flag_alias",
298
+ confidence: "high",
299
+ };
300
+ }
301
+ const comparableKey = normalizedKey.replace(/-/g, "");
302
+ const compactMatch = lookup.canonicalByCompact.get(comparableKey);
303
+ if (typeof compactMatch === "string") {
304
+ return {
305
+ flag: compactMatch,
306
+ reason: "flag_alias",
307
+ confidence: "high",
308
+ };
309
+ }
310
+ const maxDistance = comparableKey.length >= 8 ? 2 : 1;
311
+ let bestDistance = Number.POSITIVE_INFINITY;
312
+ let bestFlag;
313
+ let tied = false;
314
+ for (const candidate of lookup.canonicalComparables) {
315
+ const distance = levenshteinDistanceWithinLimit(comparableKey, candidate.comparable, maxDistance);
316
+ if (distance === null) {
317
+ continue;
318
+ }
319
+ if (distance < bestDistance) {
320
+ bestDistance = distance;
321
+ bestFlag = candidate.canonicalFlag;
322
+ tied = false;
323
+ continue;
324
+ }
325
+ if (distance === bestDistance && bestFlag !== candidate.canonicalFlag) {
326
+ tied = true;
327
+ }
328
+ }
329
+ if (!bestFlag || tied || !Number.isFinite(bestDistance) || bestDistance <= 0) {
330
+ return null;
331
+ }
332
+ return {
333
+ flag: bestFlag,
334
+ reason: "flag_typo",
335
+ confidence: bestDistance >= 2 ? "medium" : "high",
336
+ };
337
+ }
338
+ function parseBareKeyValueToken(token) {
339
+ if (token.includes("://")) {
340
+ return null;
341
+ }
342
+ const match = token.match(/^([A-Za-z][A-Za-z0-9_-]{1,63})([:=])(.*)$/);
343
+ if (!match) {
344
+ return null;
345
+ }
346
+ const key = match[1];
347
+ const value = match[3];
348
+ if (value.length === 0) {
349
+ return null;
350
+ }
351
+ return {
352
+ key,
353
+ value,
354
+ };
355
+ }
356
+ function normalizeLongOptionToken(token, lookup) {
357
+ if (!token.startsWith("--")) {
358
+ return { tokens: [token] };
359
+ }
360
+ const equalsIndex = token.indexOf("=");
361
+ const key = equalsIndex >= 0 ? token.slice(0, equalsIndex) : token;
362
+ const inlineValue = equalsIndex >= 0 ? token.slice(equalsIndex + 1) : undefined;
363
+ const resolution = resolveCanonicalFlag(key, lookup);
364
+ if (!resolution) {
365
+ return { tokens: [token] };
366
+ }
367
+ const normalizedToken = inlineValue === undefined ? resolution.flag : `${resolution.flag}=${inlineValue}`;
368
+ if (normalizedToken === token) {
369
+ return { tokens: [token] };
370
+ }
371
+ return {
372
+ tokens: [normalizedToken],
373
+ event: {
374
+ from: token,
375
+ to: [normalizedToken],
376
+ reason: resolution.reason,
377
+ confidence: resolution.confidence,
378
+ },
379
+ };
380
+ }
381
+ export function normalizeBootstrapInvocation(argv) {
382
+ const trace = [];
383
+ const legacyNormalized = normalizeLegacyExtensionActionSyntax(argv);
384
+ if (legacyNormalized.length !== argv.length || legacyNormalized.some((token, index) => token !== argv[index])) {
385
+ trace.push({
386
+ from: argv.join(" "),
387
+ to: [...legacyNormalized],
388
+ reason: "legacy_extension_action",
389
+ confidence: "high",
390
+ });
391
+ }
392
+ const commandName = parseBootstrapCommandName(legacyNormalized);
393
+ const lookup = buildFlagLookup(commandName);
394
+ const normalizedArgv = [];
395
+ for (let index = 0; index < legacyNormalized.length; index += 1) {
396
+ const token = legacyNormalized[index];
397
+ if (token === "--") {
398
+ normalizedArgv.push(...legacyNormalized.slice(index));
399
+ break;
400
+ }
401
+ const previous = normalizedArgv[normalizedArgv.length - 1];
402
+ if (token.startsWith("--")) {
403
+ const normalizedToken = normalizeLongOptionToken(token, lookup);
404
+ normalizedArgv.push(...normalizedToken.tokens);
405
+ if (normalizedToken.event) {
406
+ trace.push(normalizedToken.event);
407
+ }
408
+ continue;
409
+ }
410
+ const bareKeyValue = parseBareKeyValueToken(token);
411
+ if (bareKeyValue &&
412
+ !(typeof previous === "string" && previous.startsWith("-"))) {
413
+ const resolution = resolveCanonicalFlag(bareKeyValue.key, lookup);
414
+ if (resolution) {
415
+ const replacement = [resolution.flag, bareKeyValue.value];
416
+ normalizedArgv.push(...replacement);
417
+ trace.push({
418
+ from: token,
419
+ to: replacement,
420
+ reason: "bare_key_value",
421
+ confidence: resolution.confidence,
422
+ });
423
+ continue;
424
+ }
425
+ }
426
+ normalizedArgv.push(token);
427
+ }
428
+ return {
429
+ argv: normalizedArgv,
430
+ commandName,
431
+ trace,
432
+ };
433
+ }
223
434
  export function parseBootstrapTypeValue(argv) {
224
435
  for (let index = 0; index < argv.length; index += 1) {
225
436
  const token = argv[index];