@unbrained/pm-cli 2026.9.23 → 2026.9.26
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.
- package/.agents/skills/README.md +1 -0
- package/.agents/skills/pm-developer/SKILL.md +3 -0
- package/.agents/skills/pm-test-audit/LICENSE.txt +21 -0
- package/.agents/skills/pm-test-audit/SKILL.md +103 -0
- package/.agents/skills/pm-test-audit/references/AUDIT.md +54 -0
- package/.agents/skills/pm-test-audit/references/CAMPAIGN.md +47 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +6 -20
- package/CHANGELOG.md +49 -1
- package/dist/cli/error-guidance.d.ts +9 -0
- package/dist/cli/error-guidance.js +1 -1
- package/dist/cli/help-content.js +1 -1
- package/dist/cli/register-list-query.js +2 -2
- package/dist/cli/schema/registration.js +2 -2
- package/dist/cli-bundle/bundle-manifest.json +149 -149
- package/dist/cli-bundle/chunks/{chunk-NBGSRFZE.js → chunk-4ODHIX7N.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-VO7DHENB.js → chunk-FOTXRNY6.js} +39 -39
- package/dist/cli-bundle/chunks/chunk-GRWPZR7F.js +29 -0
- package/dist/cli-bundle/chunks/{chunk-E6ZES2X7.js → chunk-HTHTX6E6.js} +6 -6
- package/dist/cli-bundle/chunks/chunk-IYN4MJ2C.js +238 -0
- package/dist/cli-bundle/chunks/{chunk-6WBR6GMW.js → chunk-MJWKR7YA.js} +63 -62
- package/dist/cli-bundle/chunks/{chunk-ZSVINTXT.js → chunk-QQD3VRMZ.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-SXRMM72R.js → chunk-SZQCHJI4.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-ZWSWR2GX.js → chunk-TC74K7SS.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-PFWCAI4X.js → chunk-TYJXMT3Q.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-KOUT5SAA.js → chunk-U55RYT3I.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-WBB5B7G5.js → chunk-VMO4P5MX.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-IFPQ4YRD.js → register-list-query-426QTRET.js} +4 -4
- package/dist/cli-bundle/chunks/{register-mutation-C3O7OSXW.js → register-mutation-FLI3HHMP.js} +4 -4
- package/dist/cli-bundle/chunks/{register-operations-LGO7DBZ7.js → register-operations-TP7ZG4MK.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-4EW2PPZD.js → register-setup-574SIVQZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-25BBHWMH.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-YE6F5HGF.js → chunk-ABA3KRPY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-H3BI4EDO.js +75 -0
- package/dist/cli-bundle/focused-chunks/chunk-HNHMK5JI.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-S5JYIFFS.js → chunk-HQPTDJLF.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-LZOEUNHR.js → chunk-MK5AKKHK.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-6CWR2QFM.js → chunk-NRHTXBTI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-L554BKFH.js → chunk-NVVB2HJC.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-PIHQNL32.js → chunk-O7MIBEMK.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-BKXYQQDT.js → chunk-OY3VHCTX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-N2JYYEHM.js → chunk-PMPFLA6X.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-HJA6OHWT.js → chunk-Q355NPLH.js} +28 -28
- package/dist/cli-bundle/focused-chunks/{chunk-BQSKFRL2.js → chunk-TLF6N56J.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-QREDJ77O.js → chunk-UIFVIE5F.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-E5AOEXF5.js → chunk-XKH2FC5D.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-FIQYVDLL.js → chunk-XVBWZTU2.js} +11 -11
- package/dist/cli-bundle/main.js +22 -22
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +30 -45
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +58 -42
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +9 -10
- package/dist/core/policy/workflow-policy-store.js +1 -1
- package/dist/core/search/embedding-batches.d.ts +47 -15
- package/dist/core/search/embedding-batches.js +1 -1
- package/dist/core/search/eval-thresholds.d.ts +14 -0
- package/dist/core/search/eval-thresholds.js +2 -0
- package/dist/core/search/eval.d.ts +4 -1
- package/dist/core/search/eval.js +1 -1
- package/dist/core/search/http-client.d.ts +7 -0
- package/dist/core/search/http-client.js +1 -1
- package/dist/core/shared/errors.d.ts +13 -0
- package/dist/core/shared/errors.js +1 -1
- package/dist/sdk/completion/bash.js +1 -1
- package/dist/sdk/eval.d.ts +19 -17
- package/dist/sdk/eval.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/governance/health.d.ts +1 -1
- package/dist/sdk/governance/health.js +1 -1
- package/dist/sdk/governance/reindex.d.ts +4 -1
- package/dist/sdk/governance/reindex.js +2 -2
- package/dist/sdk/init-agent-guidance.d.ts +2 -1
- package/dist/sdk/init-agent-guidance.js +2 -2
- package/dist/sdk/merge/install.js +2 -2
- package/dist/sdk/merge/worktree-config.d.ts +9 -0
- package/dist/sdk/merge/worktree-config.js +2 -0
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query.d.ts +3 -0
- package/dist/sdk/query.js +1 -1
- package/dist/sdk/runtime/schema-preview.d.ts +2 -0
- package/dist/sdk/runtime/schema-preview.js +2 -0
- package/dist/sdk/runtime/schema.js +1 -1
- package/dist/sdk/runtime-primitives.d.ts +2 -1
- package/dist/sdk/runtime-primitives.js +1 -1
- package/docs/AGENT_RECOVERY_AND_GUIDANCE.md +49 -0
- package/docs/CHATGPT_CODEX_PLUGIN_IMPLEMENTATION.md +2 -0
- package/docs/CLAUDE_CODE_PLUGIN.md +18 -7
- package/docs/CODEX_PLUGIN.md +5 -12
- package/docs/DEFECT_RECURRENCE.md +8 -1
- package/docs/DEPENDENCY_KIND_CONTRACT.md +27 -0
- package/docs/MERGE_SAFETY.md +28 -5
- package/docs/README.md +2 -0
- package/docs/RELEASE_RELIABILITY.md +70 -0
- package/docs/RELEASING.md +9 -1
- package/docs/SEARCH_QUALITY.md +116 -0
- package/docs/TESTING.md +17 -0
- package/docs/VALIDATION_PORTABILITY.md +20 -11
- package/marketplace.json +2 -2
- package/package.json +6 -5
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/README.md +6 -5
- package/plugins/pm-claude/hooks/hooks.json +1 -1
- package/plugins/pm-claude/hooks/session-start.mjs +9 -6
- package/plugins/pm-claude/package.json +9 -0
- package/plugins/pm-claude/scripts/plugin-runtime.mjs +84 -0
- package/plugins/pm-claude/scripts/pm-mcp-server.mjs +39 -96
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.mcp.json +0 -3
- package/plugins/pm-codex/README.md +1 -1
- package/plugins/pm-codex/package.json +9 -0
- package/plugins/pm-codex/scripts/plugin-runtime.mjs +84 -0
- package/plugins/pm-codex/scripts/pm-mcp-server.mjs +38 -99
- package/dist/cli-bundle/chunks/chunk-7IABMUB6.js +0 -238
- package/dist/cli-bundle/chunks/chunk-OBTHQ6UX.js +0 -29
- package/dist/cli-bundle/focused-chunks/chunk-3JAEZ7WI.js +0 -59
- package/dist/cli-bundle/focused-chunks/chunk-BULUJEOL.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-KMNH4G6Y.js +0 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Semantic Batching and Retrieval Quality
|
|
2
|
+
|
|
3
|
+
Tracked by [pm-vjgqj5](../.agents/pm/issues/pm-vjgqj5.toon) and
|
|
4
|
+
[pm-p1g43q](../.agents/pm/features/pm-p1g43q.toon).
|
|
5
|
+
|
|
6
|
+
## Agent Quick Context
|
|
7
|
+
|
|
8
|
+
Use `pm reindex --mode semantic --progress` to refresh stale embeddings and
|
|
9
|
+
`pm eval --queries ./queries.json --k 10` to measure retrieval. A successful
|
|
10
|
+
index refresh proves that vectors were written, not that relevant items rank
|
|
11
|
+
well. Evaluate quality separately.
|
|
12
|
+
|
|
13
|
+
## Independent Input and Request Limits
|
|
14
|
+
|
|
15
|
+
The built-in scheduler preserves `search.embedding_batch_size` as the maximum
|
|
16
|
+
number of input positions per planned batch. Per-input text truncation still
|
|
17
|
+
uses `search.embedding_corpus_max_characters` or the provider default.
|
|
18
|
+
|
|
19
|
+
Requests additionally have a 256 KiB encoded JSON byte ceiling. This counts
|
|
20
|
+
UTF-8, JSON escaping, array separators, and model metadata; it is independent of
|
|
21
|
+
the per-input character limit. OpenAI's single-string encoding and duplicate
|
|
22
|
+
input elimination can make the actual payload smaller than this conservative
|
|
23
|
+
planning bound. An individual input that cannot fit is refused before any
|
|
24
|
+
request is sent. Empty inputs are also refused rather than silently shifting
|
|
25
|
+
vector positions.
|
|
26
|
+
|
|
27
|
+
SDK hosts may supply `maxRequestBytes` to
|
|
28
|
+
`executeEmbeddingBatchesWithRetry`, exported by
|
|
29
|
+
`@unbrained/pm-cli/sdk/query`. The request ceiling is a transport
|
|
30
|
+
bound, not a model tokenizer or context-window claim. Model input limits remain
|
|
31
|
+
provider-owned. [Ollama's embedding API](https://docs.ollama.com/api/embed)
|
|
32
|
+
accepts arrays of inputs and describes its own context truncation behavior.
|
|
33
|
+
|
|
34
|
+
HTTP 413 and timeout failures split a multi-input batch in half. Both halves
|
|
35
|
+
complete in source order. Other failures use the configured bounded retry
|
|
36
|
+
policy. A singleton failure terminates the operation; failed execution never
|
|
37
|
+
returns a partial vector array. Sequential execution avoids creating a burst
|
|
38
|
+
of concurrent requests against a local embedding model.
|
|
39
|
+
|
|
40
|
+
HTTP status takes precedence over response text: a non-413 error containing
|
|
41
|
+
the word "timeout" does not trigger splitting. For a planned batch of `n`
|
|
42
|
+
positions, binary subdivision creates at most `2n - 1` request nodes, each
|
|
43
|
+
with at most `scanner_max_batch_retries + 1` attempts. SDK hosts accepting
|
|
44
|
+
untrusted workloads should constrain corpus size, retry settings and the
|
|
45
|
+
caller-supplied byte ceiling to their provider budget; this primitive does not
|
|
46
|
+
impose a universal account-level quota.
|
|
47
|
+
|
|
48
|
+
`pm reindex` exposes `semantic.batching` for built-in providers:
|
|
49
|
+
|
|
50
|
+
- `planned_batches` counts batches before adaptation;
|
|
51
|
+
- `requests`, `successful_batches`, `split_batches`, and `retries` describe
|
|
52
|
+
actual execution;
|
|
53
|
+
- `batch_size_histogram` counts successful requests by input positions;
|
|
54
|
+
- `elapsed_ms` and `inputs_per_second` include retry backoff;
|
|
55
|
+
- `provider`, `model`, `max_request_bytes`, and `limit_source` explain the run.
|
|
56
|
+
|
|
57
|
+
These receipts contain no input text, credentials, or endpoint addresses.
|
|
58
|
+
Empty work retains the existing empty result; extension providers do not
|
|
59
|
+
receive a fabricated built-in batching receipt.
|
|
60
|
+
|
|
61
|
+
## Per-query Quality Floors
|
|
62
|
+
|
|
63
|
+
A golden query may declare a `minimum` object containing any of `ndcg`, `mrr`,
|
|
64
|
+
`precision`, and `recall`. Each value must be a finite number in `[0, 1]`.
|
|
65
|
+
Unknown metric names fail validation so a spelling error cannot remove a gate.
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
[
|
|
69
|
+
{
|
|
70
|
+
"query": "snapshot restore durability",
|
|
71
|
+
"relevant_ids": ["pm-snapshot", "pm-restore"],
|
|
72
|
+
"mode": "keyword",
|
|
73
|
+
"minimum": { "recall": 0.5, "mrr": 0.5 }
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Replace the example IDs with judgments from your workspace. `pm eval` compares
|
|
79
|
+
unrounded scores to these floors and reports `violations` on the affected query.
|
|
80
|
+
It exits nonzero if any floor fails, even when no `--fail-under` is supplied or
|
|
81
|
+
the aggregate nDCG passes. Existing query sets without floors retain their
|
|
82
|
+
aggregate-only behavior. The public query SDK exports `parseEvalMetricFloors`
|
|
83
|
+
and `evaluateMetricFloors` for package-owned evaluations.
|
|
84
|
+
|
|
85
|
+
The repository's `quality:retrieval-eval` gate also pins floors by query text
|
|
86
|
+
and retrieval mode. Missing, unexpected or duplicated identities and mismatched
|
|
87
|
+
declared row counts fail. Explicit
|
|
88
|
+
`--update` refreshes never lower a prior floor or discard a missing required
|
|
89
|
+
query. A baseline update is a reviewed policy change; it is not permission to
|
|
90
|
+
change relevance judgments until a score turns green.
|
|
91
|
+
|
|
92
|
+
## Verification Boundaries
|
|
93
|
+
|
|
94
|
+
Real loopback HTTP tests exercise batching, Unicode payloads, 413 recovery,
|
|
95
|
+
vector ordering, and pre-dispatch refusal. Evaluator tests exercise the case
|
|
96
|
+
where the macro average passes while one query fails. These are transport and
|
|
97
|
+
quality-contract proofs, not measured million-item model throughput, semantic
|
|
98
|
+
ranking improvements, parallel scheduling, or durable partial-run resume.
|
|
99
|
+
Those broader acceptance criteria remain tracked by the linked items.
|
|
100
|
+
|
|
101
|
+
## Portable Delivery Evidence
|
|
102
|
+
|
|
103
|
+
Workspace-owned custom types used by delivery records must be declared in the
|
|
104
|
+
tracked project schema. An ignored local extension installation does not make
|
|
105
|
+
its types available to a fresh CI checkout. This repository declares `Changeset`
|
|
106
|
+
in `schema/types.json` through `pm schema add-type`, and verifies changelog output
|
|
107
|
+
in a clean checkout with only the published changelog package installed.
|
|
108
|
+
|
|
109
|
+
Schema `--dry-run` applies to rename/remap migrations and workflow-policy
|
|
110
|
+
actions. Other schema operations refuse the flag before writing, through the
|
|
111
|
+
shared SDK guard used by CLI and SDK/MCP dispatch. For example,
|
|
112
|
+
`pm schema rename-type Spike --to Experiment --dry-run` previews a migration;
|
|
113
|
+
`pm schema add-type Spike --dry-run` is refused. Adding a type is an explicit
|
|
114
|
+
write operation. Tracked by [pm-s74dca](../.agents/pm/issues/pm-s74dca.toon).
|
|
115
|
+
SDK hosts can reuse `assertSchemaPreviewSupported(subcommand, dryRun)` from
|
|
116
|
+
`@unbrained/pm-cli/sdk` when dispatching built-in schema operations.
|
package/docs/TESTING.md
CHANGED
|
@@ -88,6 +88,23 @@ so PR-local CodeFactor maintainability annotations fail locally before commit or
|
|
|
88
88
|
push. Existing legacy high-complexity test fixtures are tracked separately and
|
|
89
89
|
must not be used as precedent for new changed production/script code.
|
|
90
90
|
|
|
91
|
+
The same required static gate validates an explicit seven-day Dependabot
|
|
92
|
+
cooldown for every updater and requires both npm and GitHub Actions entries.
|
|
93
|
+
Missing values, semver overrides outside 7–90 days, invalid YAML, removed
|
|
94
|
+
updaters, and include/exclude bypasses fail the gate. The maximum follows
|
|
95
|
+
[GitHub's cooldown limits](https://docs.github.com/en/code-security/tutorials/secure-your-dependencies/optimizing-pr-creation-version-updates).
|
|
96
|
+
Security updates retain GitHub's separate security-update behavior.
|
|
97
|
+
The negative controls are tracked by [pm-gkut](../.agents/pm/issues/pm-gkut.toon)
|
|
98
|
+
and [pm-dmo5](../.agents/pm/issues/pm-dmo5.toon).
|
|
99
|
+
|
|
100
|
+
Release synchronization and commit staging share one distribution manifest
|
|
101
|
+
inventory. The [pm-t4prek regression](../tests/unit/scripts/release/version-manifests.spec.ts)
|
|
102
|
+
bumps versions in a temporary Git repository and reads the resulting commit to
|
|
103
|
+
verify workspace versions and exact plugin runtime pins. The pipeline checks
|
|
104
|
+
version consistency immediately before staging, including required string
|
|
105
|
+
versions and runtime pins in both plugin package manifests; tests of a prepared working
|
|
106
|
+
tree alone cannot establish that the release commit contains those changes.
|
|
107
|
+
|
|
91
108
|
`lint:duplicates` runs three zero-clone profiles: the broad repository profile,
|
|
92
109
|
the production cross-format profile, and a supplemental long-table profile at
|
|
93
110
|
28 lines / 80 tokens. The last profile detects the historical GH-508 normalizer
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Completion portability and bounded replication checks
|
|
2
2
|
|
|
3
|
-
Tracked by [pm-t6jl1f](../.agents/pm/issues/pm-t6jl1f.toon)
|
|
4
|
-
[pm-26y269](../.agents/pm/tasks/pm-26y269.toon)
|
|
3
|
+
Tracked by [pm-t6jl1f](../.agents/pm/issues/pm-t6jl1f.toon),
|
|
4
|
+
[pm-26y269](../.agents/pm/tasks/pm-26y269.toon), and
|
|
5
|
+
[pm-eswzn9](../.agents/pm/tasks/pm-eswzn9.toon).
|
|
5
6
|
|
|
6
7
|
## Bash completion data boundary
|
|
7
8
|
|
|
@@ -13,10 +14,15 @@ elements to `COMPREPLY`. Candidates containing shell metacharacters or Unicode
|
|
|
13
14
|
use portable single-quote syntax, including embedded quote escaping, so accepting
|
|
14
15
|
a candidate and pressing Enter also preserves its literal argument value.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
The matcher decodes the current Readline word without evaluation. It recognizes
|
|
18
|
+
open single quotes, open double quotes, and backslash-escaped prefixes. Each
|
|
19
|
+
completion is encoded for its insertion context: apostrophes are split safely
|
|
20
|
+
inside single quotes; dollar signs, backticks, backslashes, and double quotes
|
|
21
|
+
are escaped inside double quotes; exclamation marks use a single-quoted segment
|
|
22
|
+
so history expansion cannot change the accepted bytes. Unquoted prefixes keep
|
|
23
|
+
the existing literal single-quote encoding for unsafe candidates. Mixed quoted
|
|
24
|
+
segments whose replacement span is ambiguous, and prefixes containing typed
|
|
25
|
+
backticks, still return no suggestions.
|
|
20
26
|
|
|
21
27
|
The matcher never evaluates choices as shell programs. Dollar signs, backticks,
|
|
22
28
|
quotes, backslashes, and wildcard characters remain literal. Unicode survives
|
|
@@ -37,9 +43,10 @@ and cache policy retain their existing contracts.
|
|
|
37
43
|
`compgen -W` cannot safely serve as the data transport here: it performs another
|
|
38
44
|
expansion pass, and Bash 3.2 does not round-trip the ANSI-C quoting emitted by
|
|
39
45
|
`printf %q` for Unicode. Unquoted command-substitution arrays can additionally
|
|
40
|
-
expand literal wildcard candidates into filenames. The regression
|
|
41
|
-
generated function and
|
|
42
|
-
|
|
46
|
+
expand literal wildcard candidates into filenames. The regression checks the
|
|
47
|
+
generated function and uses an interactive pseudo terminal to press Tab and
|
|
48
|
+
Enter in each supported insertion context. It checks the accepted argument
|
|
49
|
+
bytes and verifies that a command-substitution sentinel was not created.
|
|
43
50
|
|
|
44
51
|
Run the focused native regression after building:
|
|
45
52
|
|
|
@@ -51,8 +58,10 @@ To test another installed Bash version, set `PM_COMPLETION_TEST_BASH` to its
|
|
|
51
58
|
executable path. This is a test harness input, not a CLI configuration setting.
|
|
52
59
|
The existing macOS runtime-smoke job pins `/bin/bash` so PATH cannot silently
|
|
53
60
|
substitute a newer Homebrew Bash. It runs this regression before merge; Linux
|
|
54
|
-
coverage runs the same matrix.
|
|
55
|
-
|
|
61
|
+
coverage runs the same matrix. The macOS test drives Bash through a Python 3
|
|
62
|
+
pseudo terminal because BSD `script` requires terminal stdin; Linux uses
|
|
63
|
+
`script`. Set `PM_COMPLETION_TEST_PTY=python` to exercise the macOS driver on
|
|
64
|
+
Linux. Native Fish and Zsh acceptance remains in the required smoke gate.
|
|
56
65
|
|
|
57
66
|
## Replication diff collection
|
|
58
67
|
|
package/marketplace.json
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Official marketplace for pm CLI — native git-based project management for Claude Code and AI coding agents.",
|
|
9
|
-
"version": "2026.9.
|
|
9
|
+
"version": "2026.9.26"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "pm-claude",
|
|
14
14
|
"source": "./plugins/pm-claude",
|
|
15
15
|
"description": "Native pm CLI integration for Claude Code — 28 MCP tools, 5 workflow skills, 14 slash commands, 4 subagents, hybrid TUI task tracking, session context injection, and coordination subagents for git-based project management without leaving Claude Code.",
|
|
16
|
-
"version": "2026.9.
|
|
16
|
+
"version": "2026.9.26",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "unbrained",
|
|
19
19
|
"url": "https://github.com/unbraind/pm-cli"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-cli",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.26",
|
|
4
4
|
"description": "Git-native project management CLI for humans and agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.10.0",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"lint:complexity:baseline": "eslint . --suppress-rule complexity --suppress-rule sonarjs/cognitive-complexity",
|
|
124
124
|
"lint:duplicates": "jscpd --config .jscpd.json && jscpd --config .jscpd.source.json && jscpd --config .jscpd.tables.json",
|
|
125
125
|
"lint:codefactor": "pnpm quality:static",
|
|
126
|
-
"quality:static": "pnpm quality:docstrings && pnpm quality:exports && pnpm lint:eslint && pnpm lint:duplicates && pnpm build && node scripts/contracts-snapshot.mjs --check && node scripts/generate-agent-capability-surfaces.mjs --check && node scripts/generate-error-code-catalog.mjs --check && node scripts/release/repository-assurance.mjs repository-static-quality --trigger ci --json && node scripts/release/audit-package-boundary.mjs && node scripts/release/package-sdk-contract-parity.mjs && node scripts/release/surface-replication-gate.mjs && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs && node scripts/release/refusal-closure-gate.mjs && node scripts/release/agent-task-token-gate.mjs && pnpm quality:mcp-deprecations && node dist/cli.js assurance run tracker-context-quality --trigger ci --dry-run --json --output-budget unbounded && node scripts/release/gate-registry.mjs && node scripts/sdk-surface-snapshot.mjs --check && node scripts/bench/sdk-entrypoint-costs.mjs --check && node scripts/bench/cli-transport-floor.mjs --check && node dist/cli.js assurance run graph-composition --trigger ci --dry-run --json --output-budget unbounded && node dist/cli.js assurance run record-integrity --trigger ci --dry-run --json --output-budget unbounded && pnpm quality:mutation -- --prebuilt",
|
|
126
|
+
"quality:static": "pnpm quality:docstrings && pnpm quality:exports && pnpm lint:eslint && pnpm lint:duplicates && node scripts/check-workflow-permissions.mjs && node scripts/check-dependency-cooldown.mjs && node scripts/gen-plugin-mcp-wrappers.mjs --check && pnpm build && node scripts/contracts-snapshot.mjs --check && node scripts/generate-agent-capability-surfaces.mjs --check && node scripts/generate-error-code-catalog.mjs --check && node scripts/release/repository-assurance.mjs repository-static-quality --trigger ci --json && node scripts/release/audit-package-boundary.mjs && node scripts/release/package-sdk-contract-parity.mjs && node scripts/release/surface-replication-gate.mjs && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs && node scripts/release/refusal-closure-gate.mjs && node scripts/release/agent-task-token-gate.mjs && pnpm quality:mcp-deprecations && node dist/cli.js assurance run tracker-context-quality --trigger ci --dry-run --json --output-budget unbounded && node scripts/release/gate-registry.mjs && node scripts/sdk-surface-snapshot.mjs --check && node scripts/bench/sdk-entrypoint-costs.mjs --check && node scripts/bench/cli-transport-floor.mjs --check && node dist/cli.js assurance run graph-composition --trigger ci --dry-run --json --output-budget unbounded && node dist/cli.js assurance run record-integrity --trigger ci --dry-run --json --output-budget unbounded && pnpm quality:mutation -- --prebuilt",
|
|
127
127
|
"quality:exports": "knip --no-config-hints && node scripts/run-tests.mjs test -- tests/unit/scripts/release/dead-exports.spec.ts",
|
|
128
128
|
"quality:command-grammar": "pnpm build && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs",
|
|
129
129
|
"quality:recovery-closure": "pnpm build && node scripts/release/refusal-closure-gate.mjs",
|
|
@@ -167,6 +167,7 @@
|
|
|
167
167
|
"smoke:external-packages": "node scripts/smoke-external-packages.mjs",
|
|
168
168
|
"smoke:codex-plugin": "node scripts/smoke-codex-plugin-mcp.mjs",
|
|
169
169
|
"smoke:claude-plugin": "node scripts/smoke-claude-plugin.mjs",
|
|
170
|
+
"smoke:plugin-cache": "node tests/acceptance/plugin-cache-smoke.mjs",
|
|
170
171
|
"version:check": "node scripts/release-version.mjs check && node scripts/sync-versions.mjs check",
|
|
171
172
|
"version:next": "node scripts/release-version.mjs next",
|
|
172
173
|
"version:sync": "node scripts/sync-versions.mjs apply",
|
|
@@ -211,7 +212,7 @@
|
|
|
211
212
|
"node": ">=22.18.0"
|
|
212
213
|
},
|
|
213
214
|
"dependencies": {
|
|
214
|
-
"@sentry/node": "10.75.
|
|
215
|
+
"@sentry/node": "10.75.1",
|
|
215
216
|
"@toon-format/toon": "^4.1.1",
|
|
216
217
|
"@types/node": ">=22",
|
|
217
218
|
"commander": "^15.0.0",
|
|
@@ -238,13 +239,13 @@
|
|
|
238
239
|
"fast-check": "^4.10.2",
|
|
239
240
|
"github-slugger": "2.0.0",
|
|
240
241
|
"greptile": "^3.5.3",
|
|
241
|
-
"jscpd": "^5.3.
|
|
242
|
+
"jscpd": "^5.3.1",
|
|
242
243
|
"knip": "^6.37.0",
|
|
243
244
|
"marked": "18.0.13",
|
|
244
245
|
"parse5": "8.0.1",
|
|
245
246
|
"tsx": "^4.23.15",
|
|
246
247
|
"typescript": "^6.0.3",
|
|
247
|
-
"typescript-eslint": "^8.70.
|
|
248
|
+
"typescript-eslint": "^8.70.1",
|
|
248
249
|
"vitest": "^4.1.11"
|
|
249
250
|
}
|
|
250
251
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unbrained/pm-digital-twin",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.26",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Beyond-PM exemplar: a temporal production-facility digital twin built entirely on public pm SDK and extension primitives.",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pm-claude",
|
|
3
3
|
"description": "Native pm CLI integration for Claude Code — 28 MCP tools, 5 workflow skills, 14 slash commands, 4 subagents (coordinator, delivery-chain, triage, verification), hybrid TUI task tracking (pm as persistent store + Claude Code task panel as live view), session context injection, and full git-based project management without leaving Claude Code.",
|
|
4
|
-
"version": "2026.9.
|
|
4
|
+
"version": "2026.9.26",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "unbrained",
|
|
7
7
|
"url": "https://github.com/unbraind/pm-cli"
|
|
@@ -11,7 +11,7 @@ Native pm CLI integration for Claude Code. Use pm project management tools direc
|
|
|
11
11
|
| **14 slash commands** | Full lifecycle coverage — status, start, close, triage, audit, search, new, list, calendar, developer, planner, release, workflow, init |
|
|
12
12
|
| **4 subagents** | `pm-coordinator` (batch/multi-item), `pm-triage-agent` (duplicate-safe item creation), `pm-verification-agent` (evidence + close readiness), and `pm-delivery-chain` (orchestrated delivery) |
|
|
13
13
|
| **Hybrid TUI tracking** | pm items sync to Claude Code's task panel — pm is the persistent store, the task panel is the live session view |
|
|
14
|
-
| **Session hook** | Injects active pm item summary at session start
|
|
14
|
+
| **Session hook** | Injects active pm item summary at session start using the same pinned pm-cli runtime as the MCP server |
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -192,8 +192,9 @@ All skills and commands implement this pattern for every claimed item:
|
|
|
192
192
|
|
|
193
193
|
## Session Context Injection
|
|
194
194
|
|
|
195
|
-
At session start, the hook runs the
|
|
196
|
-
- Uses
|
|
195
|
+
At session start, the hook runs the plugin's exact-version `pm` CLI without requiring a global install:
|
|
196
|
+
- Uses the version pinned in `plugins/pm-claude/package.json` and persisted in the plugin data directory
|
|
197
|
+
- Reuses the installed runtime offline on later sessions
|
|
197
198
|
- Injects a compact summary of in-progress/open/blocked items
|
|
198
199
|
|
|
199
200
|
Example output:
|
|
@@ -215,8 +216,8 @@ Use pm_context tool or /pm-status for full details.
|
|
|
215
216
|
|
|
216
217
|
## Requirements
|
|
217
218
|
|
|
218
|
-
- Node.js ≥
|
|
219
|
-
- pm CLI resolved automatically
|
|
219
|
+
- Node.js ≥ 22.18.0
|
|
220
|
+
- pm CLI resolved automatically from the plugin's pinned package installation (no global install needed)
|
|
220
221
|
- Project initialized with `pm init` (or use `/pm-init`)
|
|
221
222
|
|
|
222
223
|
## Links
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
* pm-cli Claude Code session-start hook.
|
|
4
4
|
*
|
|
5
5
|
* Injects a brief pm context summary into the session when pm is initialized
|
|
6
|
-
* in the current workspace. Uses the
|
|
6
|
+
* in the current workspace. Uses the plugin's exact-version runtime without
|
|
7
7
|
* requiring a global install. Exits silently if pm is not set up.
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { join } from "node:path";
|
|
11
|
-
import {
|
|
11
|
+
import { execFileSync } from "node:child_process";
|
|
12
|
+
import { resolvePluginRuntime } from "../scripts/plugin-runtime.mjs";
|
|
12
13
|
|
|
13
14
|
const workspace = process.cwd();
|
|
14
15
|
const pmSettingsPath = join(workspace, ".agents", "pm", "settings.json");
|
|
@@ -40,10 +41,12 @@ function formatSummary(ctx) {
|
|
|
40
41
|
);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function
|
|
44
|
+
async function readContext() {
|
|
44
45
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
46
|
+
const runtime = await resolvePluginRuntime();
|
|
47
|
+
const raw = execFileSync(
|
|
48
|
+
process.execPath,
|
|
49
|
+
[runtime.cli, "context", "--limit", "5", "--json"],
|
|
47
50
|
{
|
|
48
51
|
cwd: workspace,
|
|
49
52
|
encoding: "utf-8",
|
|
@@ -58,7 +61,7 @@ function tryNpxContext() {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
try {
|
|
61
|
-
const ctx =
|
|
64
|
+
const ctx = await readContext();
|
|
62
65
|
if (!ctx) process.exit(0);
|
|
63
66
|
|
|
64
67
|
const message = formatSummary(ctx);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the exact pm-cli runtime declared by a copied agent plugin.
|
|
3
|
+
* This file is copied into each plugin by gen-plugin-mcp-wrappers.mjs so a
|
|
4
|
+
* marketplace cache never needs to reach back into the repository checkout.
|
|
5
|
+
*/
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { access, mkdir, mkdtemp, readFile, rename, rm } from "node:fs/promises";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
+
const packageName = "@unbrained/pm-cli";
|
|
14
|
+
const versionPattern = /^[1-9]\d{3}\.[1-9]\d*\.[1-9]\d*(?:-[1-9]\d*)?$/;
|
|
15
|
+
|
|
16
|
+
/** Require both the declared version and the executable shipped by that package. */
|
|
17
|
+
async function installedRuntime(root, version) {
|
|
18
|
+
const packageRoot = path.join(root, "node_modules", "@unbrained", "pm-cli");
|
|
19
|
+
try {
|
|
20
|
+
const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
|
|
21
|
+
if (manifest.version !== version) return null;
|
|
22
|
+
const server = path.join(packageRoot, "dist", "mcp", "server.js");
|
|
23
|
+
const cli = path.join(packageRoot, "dist", "cli.js");
|
|
24
|
+
await access(server);
|
|
25
|
+
await access(cli);
|
|
26
|
+
return { server, cli, version };
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Install into a private staging directory and publish only a complete tree. */
|
|
33
|
+
async function installRuntime(dataRoot, version, installer) {
|
|
34
|
+
await mkdir(dataRoot, { recursive: true });
|
|
35
|
+
const finalRoot = path.join(dataRoot, `v${version}`);
|
|
36
|
+
const existing = await installedRuntime(finalRoot, version);
|
|
37
|
+
if (existing) return existing;
|
|
38
|
+
const stagingRoot = await mkdtemp(path.join(dataRoot, `.install-${version}-`));
|
|
39
|
+
try {
|
|
40
|
+
const args = ["install", "--prefix", stagingRoot, "--no-save", "--ignore-scripts", "--no-audit", "--no-fund", "--package-lock=false", `${packageName}@${version}`];
|
|
41
|
+
const windows = process.platform === "win32";
|
|
42
|
+
// The command text contains only fixed tokens and a validated version.
|
|
43
|
+
// Put the caller-selected path in the child environment so cmd.exe never
|
|
44
|
+
// parses path metacharacters as command syntax.
|
|
45
|
+
const command = windows ? process.env.ComSpec || "cmd.exe" : "npm";
|
|
46
|
+
const commandArgs = windows
|
|
47
|
+
? ["/d", "/v:off", "/s", "/c", `"npm.cmd ${args.map((arg) => arg === stagingRoot ? '"%PM_PLUGIN_STAGING_ROOT%"' : arg).join(" ")}"`]
|
|
48
|
+
: args;
|
|
49
|
+
const result = installer(command, commandArgs, {
|
|
50
|
+
encoding: "utf8", timeout: 120000, stdio: ["ignore", "pipe", "pipe"],
|
|
51
|
+
...(windows ? { windowsVerbatimArguments: true, env: { ...process.env, PM_PLUGIN_STAGING_ROOT: stagingRoot } } : {}),
|
|
52
|
+
});
|
|
53
|
+
if (result.error || result.status !== 0 || !(await installedRuntime(stagingRoot, version))) {
|
|
54
|
+
throw new Error(`Cannot install ${packageName}@${version}: ${result.error?.message || result.stderr?.trim() || "incomplete package"}`);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
await rename(stagingRoot, finalRoot);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (!(await installedRuntime(finalRoot, version))) {
|
|
60
|
+
throw new Error(`Plugin runtime at ${finalRoot} is incomplete; remove that directory and retry.`, { cause: error });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const runtime = await installedRuntime(finalRoot, version);
|
|
64
|
+
if (!runtime) throw new Error(`Plugin runtime at ${finalRoot} is incomplete; remove that directory and retry.`);
|
|
65
|
+
return runtime;
|
|
66
|
+
} finally {
|
|
67
|
+
await rm(stagingRoot, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Resolve a copied plugin's runtime without ever consulting npm's latest tag. */
|
|
72
|
+
export async function resolvePluginRuntime(options = {}) {
|
|
73
|
+
const root = options.pluginRoot ?? pluginRoot;
|
|
74
|
+
const plugin = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
|
|
75
|
+
const version = plugin.dependencies?.[packageName];
|
|
76
|
+
if (typeof version !== "string" || !versionPattern.test(version) || plugin.version !== version) {
|
|
77
|
+
throw new Error(`Plugin ${plugin.name ?? root} needs an exact pm-cli dependency matching its version.`);
|
|
78
|
+
}
|
|
79
|
+
const bundled = await installedRuntime(root, version);
|
|
80
|
+
if (bundled) return bundled;
|
|
81
|
+
const dataRoot = process.env.PLUGIN_DATA || process.env.CLAUDE_PLUGIN_DATA ||
|
|
82
|
+
path.join(os.homedir(), ".cache", "pm-cli", "plugins", path.basename(root));
|
|
83
|
+
return installRuntime(dataRoot, version, options.installer ?? spawnSync);
|
|
84
|
+
}
|