@unbrained/pm-cli 2026.5.10 → 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.
- package/.claude-plugin/marketplace.json +4 -4
- package/.pi/README.md +10 -1
- package/.pi/agents/pm-triage-agent.md +19 -0
- package/.pi/agents/pm-verification-agent.md +21 -0
- package/.pi/chains/pm-native-delivery.chain.md +11 -0
- package/.pi/extensions/pm-cli/index.js +276 -36
- package/.pi/skills/pm-native/SKILL.md +6 -2
- package/CHANGELOG.md +7 -0
- package/README.md +9 -1
- package/dist/cli/argv-utils.d.ts +5 -0
- package/dist/cli/argv-utils.js +34 -0
- package/dist/cli/argv-utils.js.map +1 -0
- package/dist/cli/bootstrap-args.d.ts +15 -0
- package/dist/cli/bootstrap-args.js +211 -0
- package/dist/cli/bootstrap-args.js.map +1 -1
- package/dist/cli/commander-usage.js +109 -3
- package/dist/cli/commander-usage.js.map +1 -1
- package/dist/cli/commands/completion.js +7 -3
- package/dist/cli/commands/completion.js.map +1 -1
- package/dist/cli/commands/contracts.d.ts +19 -0
- package/dist/cli/commands/contracts.js +33 -1
- package/dist/cli/commands/contracts.js.map +1 -1
- package/dist/cli/commands/create.js +112 -51
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/commands/docs.js +9 -2
- package/dist/cli/commands/docs.js.map +1 -1
- package/dist/cli/commands/extension.d.ts +3 -1
- package/dist/cli/commands/extension.js +174 -2
- package/dist/cli/commands/extension.js.map +1 -1
- package/dist/cli/commands/files.js +9 -2
- package/dist/cli/commands/files.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +21 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/metadata-normalizers.d.ts +4 -0
- package/dist/cli/commands/metadata-normalizers.js +37 -0
- package/dist/cli/commands/metadata-normalizers.js.map +1 -0
- package/dist/cli/commands/reindex.js +173 -135
- package/dist/cli/commands/reindex.js.map +1 -1
- package/dist/cli/commands/search.js +16 -6
- package/dist/cli/commands/search.js.map +1 -1
- package/dist/cli/commands/test.js +9 -2
- package/dist/cli/commands/test.js.map +1 -1
- package/dist/cli/commands/update.js +70 -39
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/error-guidance.d.ts +9 -1
- package/dist/cli/error-guidance.js +147 -6
- package/dist/cli/error-guidance.js.map +1 -1
- package/dist/cli/help-json-payload.js +11 -1
- package/dist/cli/help-json-payload.js.map +1 -1
- package/dist/cli/main.js +69 -6
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/register-setup.js +14 -0
- package/dist/cli/register-setup.js.map +1 -1
- package/dist/cli/telemetry-flush.d.ts +2 -0
- package/dist/cli/telemetry-flush.js +4 -0
- package/dist/cli/telemetry-flush.js.map +1 -0
- package/dist/cli.js +1 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/extensions/extension-types.d.ts +72 -0
- package/dist/core/extensions/extension-types.js +24 -0
- package/dist/core/extensions/extension-types.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -0
- package/dist/core/extensions/loader.js +766 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/lock/lock.js +2 -0
- package/dist/core/lock/lock.js.map +1 -1
- package/dist/core/sentry/instrument.d.ts +15 -0
- package/dist/core/sentry/instrument.js +35 -3
- package/dist/core/sentry/instrument.js.map +1 -1
- package/dist/core/shared/constants.js +20 -0
- package/dist/core/shared/constants.js.map +1 -1
- package/dist/core/shared/errors.d.ts +8 -0
- package/dist/core/shared/errors.js.map +1 -1
- package/dist/core/shared/levenshtein.d.ts +1 -0
- package/dist/core/shared/levenshtein.js +37 -0
- package/dist/core/shared/levenshtein.js.map +1 -0
- package/dist/core/store/paths.js +34 -1
- package/dist/core/store/paths.js.map +1 -1
- package/dist/core/store/settings.js +210 -1
- package/dist/core/store/settings.js.map +1 -1
- package/dist/core/telemetry/runtime.d.ts +1 -0
- package/dist/core/telemetry/runtime.js +102 -3
- package/dist/core/telemetry/runtime.js.map +1 -1
- package/dist/mcp/server.js +3 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/pi/native.js +57 -4
- package/dist/pi/native.js.map +1 -1
- package/dist/sdk/cli-contracts.d.ts +21 -1
- package/dist/sdk/cli-contracts.js +250 -0
- package/dist/sdk/cli-contracts.js.map +1 -1
- package/dist/sdk/index.d.ts +12 -1
- package/dist/sdk/index.js +8 -1
- package/dist/sdk/index.js.map +1 -1
- package/dist/types.d.ts +41 -0
- package/dist/types.js.map +1 -1
- package/docs/CLAUDE_CODE_PLUGIN.md +39 -0
- package/docs/EXTENSIONS.md +687 -0
- package/docs/MIGRATION_CLI_SIMPLIFICATION.md +64 -0
- package/docs/PI_PACKAGE.md +95 -10
- package/docs/SDK.md +441 -0
- package/docs/examples/ci/github-actions-pm-extension-gate.yml +53 -0
- package/docs/examples/ci/gitlab-ci-pm-extension-gate.yml +41 -0
- package/docs/examples/ci/jenkins-pm-extension-gate.Jenkinsfile +45 -0
- package/docs/examples/policy-restricted-extension/README.md +74 -0
- package/docs/examples/policy-restricted-extension/index.js +21 -0
- package/docs/examples/policy-restricted-extension/manifest.json +21 -0
- package/docs/examples/policy-restricted-extension/package.json +8 -0
- package/docs/examples/sdk-app-embedding/README.md +39 -0
- package/docs/examples/sdk-app-embedding/package.json +9 -0
- package/docs/examples/sdk-app-embedding/run-embedded-pm.mjs +61 -0
- package/docs/examples/sdk-contract-consumer/README.md +57 -0
- package/docs/examples/sdk-contract-consumer/inspect-contracts.mjs +47 -0
- package/docs/examples/sdk-contract-consumer/package.json +10 -0
- package/docs/examples/starter-extension/README.md +57 -42
- package/docs/examples/starter-extension/manifest.json +15 -0
- package/marketplace.json +3 -3
- package/package.json +1 -1
- package/plugins/pm-cli-claude/.claude-plugin/plugin.json +2 -2
- package/plugins/pm-cli-claude/README.md +55 -14
- package/plugins/pm-cli-claude/agents/pm-delivery-chain.md +88 -0
- package/plugins/pm-cli-claude/agents/pm-triage-agent.md +83 -0
- package/plugins/pm-cli-claude/agents/pm-verification-agent.md +88 -0
- package/plugins/pm-cli-claude/hooks/session-start.mjs +87 -22
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: pm-extension-gate
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
extension-gate:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Setup Node
|
|
17
|
+
uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: 22
|
|
20
|
+
cache: pnpm
|
|
21
|
+
|
|
22
|
+
- name: Setup pnpm
|
|
23
|
+
uses: pnpm/action-setup@v4
|
|
24
|
+
with:
|
|
25
|
+
version: 10
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: pnpm install --frozen-lockfile
|
|
29
|
+
|
|
30
|
+
- name: Build
|
|
31
|
+
run: pnpm build
|
|
32
|
+
|
|
33
|
+
- name: Export contracts (schema + flags)
|
|
34
|
+
run: |
|
|
35
|
+
pm contracts --schema-only --json > contracts-schema.json
|
|
36
|
+
pm contracts --command extension --flags-only --json > contracts-extension-flags.json
|
|
37
|
+
pm contracts --json > contracts-runtime.json
|
|
38
|
+
|
|
39
|
+
- name: Verify extension contract compatibility metadata
|
|
40
|
+
run: |
|
|
41
|
+
node -e 'const fs=require("node:fs");const c=JSON.parse(fs.readFileSync("contracts-runtime.json","utf8"));if(!c.extension_contracts?.compatibility){throw new Error("missing extension compatibility metadata");}'
|
|
42
|
+
|
|
43
|
+
- name: Reload extensions
|
|
44
|
+
run: pm extension --reload --project --json
|
|
45
|
+
|
|
46
|
+
- name: Extension governance diagnostics gate
|
|
47
|
+
run: pm extension --doctor --project --detail summary --strict-exit
|
|
48
|
+
|
|
49
|
+
- name: Unit tests
|
|
50
|
+
run: node scripts/run-tests.mjs test -- tests/unit/contracts-command.spec.ts tests/unit/extension-loader.spec.ts tests/unit/extension-command.spec.ts
|
|
51
|
+
|
|
52
|
+
- name: Coverage
|
|
53
|
+
run: node scripts/run-tests.mjs coverage
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
image: node:22
|
|
2
|
+
|
|
3
|
+
stages:
|
|
4
|
+
- build
|
|
5
|
+
- contracts
|
|
6
|
+
- extension_gate
|
|
7
|
+
- test
|
|
8
|
+
|
|
9
|
+
before_script:
|
|
10
|
+
- corepack enable
|
|
11
|
+
- corepack prepare pnpm@10 --activate
|
|
12
|
+
- pnpm install --frozen-lockfile
|
|
13
|
+
|
|
14
|
+
build:
|
|
15
|
+
stage: build
|
|
16
|
+
script:
|
|
17
|
+
- pnpm build
|
|
18
|
+
|
|
19
|
+
contracts:
|
|
20
|
+
stage: contracts
|
|
21
|
+
script:
|
|
22
|
+
- node dist/cli.js contracts --schema-only --json > contracts-schema.json
|
|
23
|
+
- node dist/cli.js contracts --command extension --flags-only --json > contracts-extension-flags.json
|
|
24
|
+
- node dist/cli.js contracts --json > contracts-runtime.json
|
|
25
|
+
artifacts:
|
|
26
|
+
paths:
|
|
27
|
+
- contracts-schema.json
|
|
28
|
+
- contracts-extension-flags.json
|
|
29
|
+
- contracts-runtime.json
|
|
30
|
+
|
|
31
|
+
extension_gate:
|
|
32
|
+
stage: extension_gate
|
|
33
|
+
script:
|
|
34
|
+
- node dist/cli.js extension --reload --project --json
|
|
35
|
+
- node dist/cli.js extension --doctor --project --detail summary --strict-exit --json
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
stage: test
|
|
39
|
+
script:
|
|
40
|
+
- node scripts/run-tests.mjs test -- tests/unit/contracts-command.spec.ts tests/unit/extension-loader.spec.ts tests/unit/extension-command.spec.ts
|
|
41
|
+
- node scripts/run-tests.mjs coverage
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
pipeline {
|
|
2
|
+
agent any
|
|
3
|
+
|
|
4
|
+
tools {
|
|
5
|
+
nodejs "node-22"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
stages {
|
|
9
|
+
stage("Install") {
|
|
10
|
+
steps {
|
|
11
|
+
sh "corepack enable"
|
|
12
|
+
sh "corepack prepare pnpm@10 --activate"
|
|
13
|
+
sh "pnpm install --frozen-lockfile"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
stage("Build") {
|
|
18
|
+
steps {
|
|
19
|
+
sh "pnpm build"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
stage("Contracts") {
|
|
24
|
+
steps {
|
|
25
|
+
sh "node dist/cli.js contracts --schema-only --json > contracts-schema.json"
|
|
26
|
+
sh "node dist/cli.js contracts --command extension --flags-only --json > contracts-extension-flags.json"
|
|
27
|
+
sh "node dist/cli.js contracts --json > contracts-runtime.json"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
stage("Extension Gate") {
|
|
32
|
+
steps {
|
|
33
|
+
sh "node dist/cli.js extension --reload --project --json"
|
|
34
|
+
sh "node dist/cli.js extension --doctor --project --detail summary --strict-exit --json"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
stage("Tests") {
|
|
39
|
+
steps {
|
|
40
|
+
sh "node scripts/run-tests.mjs test -- tests/unit/contracts-command.spec.ts tests/unit/extension-loader.spec.ts tests/unit/extension-command.spec.ts"
|
|
41
|
+
sh "node scripts/run-tests.mjs coverage"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Policy-Restricted Extension Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates governance policy behavior with real registrations.
|
|
4
|
+
|
|
5
|
+
The extension declares:
|
|
6
|
+
|
|
7
|
+
- `commands` (handler registration)
|
|
8
|
+
- `hooks` (beforeCommand)
|
|
9
|
+
- `services` (output_format override)
|
|
10
|
+
|
|
11
|
+
You can enforce policy so command/hooks remain allowed while service override is blocked.
|
|
12
|
+
|
|
13
|
+
## Run It
|
|
14
|
+
|
|
15
|
+
From repository root:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p .agents/pm/extensions
|
|
19
|
+
cp -R docs/examples/policy-restricted-extension .agents/pm/extensions/policy-restricted-extension
|
|
20
|
+
cd .agents/pm/extensions/policy-restricted-extension
|
|
21
|
+
npm install
|
|
22
|
+
cd -
|
|
23
|
+
pm extension --install --project .agents/pm/extensions/policy-restricted-extension
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Add policy in `.agents/pm/settings.json`:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"extensions": {
|
|
31
|
+
"policy": {
|
|
32
|
+
"mode": "enforce",
|
|
33
|
+
"trust_mode": "enforce",
|
|
34
|
+
"require_provenance": true,
|
|
35
|
+
"trusted_extensions": ["policy-restricted-extension"],
|
|
36
|
+
"default_sandbox_profile": "restricted",
|
|
37
|
+
"allowed_extensions": ["policy-restricted-extension"],
|
|
38
|
+
"blocked_extensions": [],
|
|
39
|
+
"allowed_capabilities": [],
|
|
40
|
+
"blocked_capabilities": [],
|
|
41
|
+
"allowed_surfaces": [],
|
|
42
|
+
"blocked_surfaces": ["services.override"],
|
|
43
|
+
"allowed_commands": [],
|
|
44
|
+
"blocked_commands": [],
|
|
45
|
+
"allowed_actions": [],
|
|
46
|
+
"blocked_actions": [],
|
|
47
|
+
"allowed_services": [],
|
|
48
|
+
"blocked_services": ["output_format"],
|
|
49
|
+
"extension_overrides": [
|
|
50
|
+
{
|
|
51
|
+
"name": "policy-restricted-extension",
|
|
52
|
+
"require_trusted": true,
|
|
53
|
+
"require_provenance": true,
|
|
54
|
+
"sandbox_profile": "strict"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then validate:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pm extension --doctor --project --detail summary
|
|
66
|
+
pm policy demo
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Expected behavior:
|
|
70
|
+
|
|
71
|
+
- `pm policy demo` still works (command handler allowed).
|
|
72
|
+
- `extension --doctor` includes `extension_policy_blocked_registration`.
|
|
73
|
+
- `details.triage.policy_blocked_count` is greater than `0`.
|
|
74
|
+
- trust/provenance contract fields are visible in `pm contracts --json` metadata.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineExtension } from "@unbrained/pm-cli/sdk";
|
|
2
|
+
|
|
3
|
+
export default defineExtension({
|
|
4
|
+
activate(api) {
|
|
5
|
+
api.registerCommand({
|
|
6
|
+
name: "policy demo",
|
|
7
|
+
action: "policy-demo",
|
|
8
|
+
description: "Emit a deterministic payload to validate policy-gated activation.",
|
|
9
|
+
run: async (context) => ({
|
|
10
|
+
ok: true,
|
|
11
|
+
command: context.command,
|
|
12
|
+
source: "policy-restricted-extension",
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
api.hooks.beforeCommand(() => {});
|
|
17
|
+
|
|
18
|
+
// This registration is intentionally useful for policy demos.
|
|
19
|
+
api.registerService("output_format", (payload) => payload);
|
|
20
|
+
},
|
|
21
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "policy-restricted-extension",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"entry": "./index.js",
|
|
5
|
+
"manifest_version": 2,
|
|
6
|
+
"trusted": true,
|
|
7
|
+
"provenance": {
|
|
8
|
+
"source": "docs/examples/policy-restricted-extension",
|
|
9
|
+
"verified": true
|
|
10
|
+
},
|
|
11
|
+
"sandbox_profile": "restricted",
|
|
12
|
+
"permissions": {
|
|
13
|
+
"fs_read": true,
|
|
14
|
+
"fs_write": false,
|
|
15
|
+
"network": false,
|
|
16
|
+
"env_read": true,
|
|
17
|
+
"env_write": false,
|
|
18
|
+
"process_spawn": false
|
|
19
|
+
},
|
|
20
|
+
"capabilities": ["commands", "hooks", "services"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# SDK App Embedding Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates a simple app-style wrapper that:
|
|
4
|
+
|
|
5
|
+
1. validates an action using SDK contracts
|
|
6
|
+
2. checks runtime availability (`pm contracts --json`)
|
|
7
|
+
3. executes a safe command mapping
|
|
8
|
+
4. emits a structured JSON payload for CI/services
|
|
9
|
+
|
|
10
|
+
## Files
|
|
11
|
+
|
|
12
|
+
- `package.json`
|
|
13
|
+
- `run-embedded-pm.mjs`
|
|
14
|
+
|
|
15
|
+
## Run
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cp -R docs/examples/sdk-app-embedding /tmp/pm-sdk-app-embedding
|
|
19
|
+
cd /tmp/pm-sdk-app-embedding
|
|
20
|
+
|
|
21
|
+
# During local development against this repository:
|
|
22
|
+
PM_CLI_REPO_ROOT=/absolute/path/to/pm-cli
|
|
23
|
+
npm install "$PM_CLI_REPO_ROOT"
|
|
24
|
+
|
|
25
|
+
# Run extension reload flow
|
|
26
|
+
node run-embedded-pm.mjs extension-reload
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## What It Returns
|
|
30
|
+
|
|
31
|
+
The script returns JSON with:
|
|
32
|
+
|
|
33
|
+
- `action`
|
|
34
|
+
- resolved command invocation
|
|
35
|
+
- required/optional parameter contracts
|
|
36
|
+
- `policy_state` from action availability
|
|
37
|
+
- command result payload
|
|
38
|
+
|
|
39
|
+
This pattern is useful for backend workers, CI jobs, or orchestrators that must remain contract-safe across SDK/CLI upgrades.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { PM_TOOL_ACTION_PARAMETER_CONTRACTS, isPmToolAction } from "@unbrained/pm-cli/sdk";
|
|
3
|
+
|
|
4
|
+
function runPm(args) {
|
|
5
|
+
const completed = spawnSync("pm", args, {
|
|
6
|
+
encoding: "utf8",
|
|
7
|
+
env: {
|
|
8
|
+
...process.env,
|
|
9
|
+
NO_COLOR: "1",
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
if (completed.status !== 0) {
|
|
13
|
+
const stderr = (completed.stderr ?? "").trim();
|
|
14
|
+
throw new Error(stderr.length > 0 ? stderr : `pm ${args.join(" ")} failed with exit code ${completed.status}`);
|
|
15
|
+
}
|
|
16
|
+
const stdout = (completed.stdout ?? "").trim();
|
|
17
|
+
return stdout.length > 0 ? JSON.parse(stdout) : {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function resolveCommandForAction(action) {
|
|
21
|
+
const commandMap = {
|
|
22
|
+
"extension-reload": ["extension", "--reload", "--project", "--json"],
|
|
23
|
+
"extension-doctor": ["extension", "--doctor", "--project", "--detail", "summary", "--json"],
|
|
24
|
+
contracts: ["contracts", "--json"],
|
|
25
|
+
};
|
|
26
|
+
return commandMap[action] ?? [action, "--json"];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const requestedAction = (process.argv[2] ?? "extension-reload").trim().toLowerCase();
|
|
30
|
+
if (!isPmToolAction(requestedAction)) {
|
|
31
|
+
throw new Error(`Unsupported pm action "${requestedAction}".`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const contracts = runPm(["contracts", "--json"]);
|
|
35
|
+
const actionAvailability = Array.isArray(contracts.action_availability)
|
|
36
|
+
? contracts.action_availability.find((entry) => entry?.action === requestedAction) ?? null
|
|
37
|
+
: null;
|
|
38
|
+
if (actionAvailability?.available === false) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Action "${requestedAction}" is not available in this runtime (${actionAvailability.disabled_reason ?? "unknown_reason"}).`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const actionContract = PM_TOOL_ACTION_PARAMETER_CONTRACTS[requestedAction];
|
|
45
|
+
const command = resolveCommandForAction(requestedAction);
|
|
46
|
+
const commandResult = runPm(command);
|
|
47
|
+
|
|
48
|
+
process.stdout.write(
|
|
49
|
+
`${JSON.stringify(
|
|
50
|
+
{
|
|
51
|
+
action: requestedAction,
|
|
52
|
+
command: `pm ${command.join(" ")}`,
|
|
53
|
+
required_parameters: actionContract.required ?? [],
|
|
54
|
+
optional_parameters: actionContract.optional ?? [],
|
|
55
|
+
policy_state: actionAvailability?.policy_state ?? null,
|
|
56
|
+
result: commandResult,
|
|
57
|
+
},
|
|
58
|
+
null,
|
|
59
|
+
2,
|
|
60
|
+
)}\n`,
|
|
61
|
+
);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# SDK Contracts Consumer Example
|
|
2
|
+
|
|
3
|
+
This example shows how to consume `pm` contracts programmatically in a script and validate action payload requirements before execution.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
- `package.json` -> installs `@unbrained/pm-cli` and script aliases
|
|
8
|
+
- `inspect-contracts.mjs` -> loads contracts + SDK helpers and prints required/optional parameter metadata
|
|
9
|
+
|
|
10
|
+
## Run
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cp -R docs/examples/sdk-contract-consumer /tmp/pm-sdk-contract-consumer
|
|
14
|
+
cd /tmp/pm-sdk-contract-consumer
|
|
15
|
+
# Local checkout (recommended while iterating on unreleased SDK changes):
|
|
16
|
+
PM_CLI_REPO_ROOT=/absolute/path/to/pm-cli
|
|
17
|
+
npm install "$PM_CLI_REPO_ROOT"
|
|
18
|
+
|
|
19
|
+
# Or use a published release once available:
|
|
20
|
+
# npm install @unbrained/pm-cli@latest
|
|
21
|
+
|
|
22
|
+
node inspect-contracts.mjs create
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Expected output shape:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"action": "create",
|
|
30
|
+
"required_parameters": ["title", "description", "type", "status", "priority", "message"],
|
|
31
|
+
"optional_parameters": ["template", "createMode", "schedulePreset"],
|
|
32
|
+
"any_of_required_groups": [],
|
|
33
|
+
"runtime_available": true,
|
|
34
|
+
"policy_state": null,
|
|
35
|
+
"compatibility": {
|
|
36
|
+
"current": "v2",
|
|
37
|
+
"previous": ["v1"],
|
|
38
|
+
"breaking_strategy": "versioned_breaking"
|
|
39
|
+
},
|
|
40
|
+
"manifest_versions": [1, 2]
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
You can inspect any action:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
node inspect-contracts.mjs update
|
|
48
|
+
node inspect-contracts.mjs extension
|
|
49
|
+
node inspect-contracts.mjs extension-reload
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Why This Pattern Works
|
|
53
|
+
|
|
54
|
+
- Uses `isPmToolAction()` for strict action validation.
|
|
55
|
+
- Uses `PM_TOOL_ACTION_PARAMETER_CONTRACTS` for deterministic required/optional metadata.
|
|
56
|
+
- Uses runtime `pm contracts --json` so extension-provided actions and command availability are reflected.
|
|
57
|
+
- Includes policy-state and compatibility metadata so CI/app callers can gate behavior during v1 -> v2 migrations.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { PM_TOOL_ACTION_PARAMETER_CONTRACTS, isPmToolAction } from "@unbrained/pm-cli/sdk";
|
|
3
|
+
|
|
4
|
+
function runPmContracts() {
|
|
5
|
+
const completed = spawnSync("pm", ["contracts", "--json"], {
|
|
6
|
+
encoding: "utf8",
|
|
7
|
+
env: {
|
|
8
|
+
...process.env,
|
|
9
|
+
NO_COLOR: "1",
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
if (completed.status !== 0) {
|
|
13
|
+
const stderr = (completed.stderr ?? "").trim();
|
|
14
|
+
throw new Error(stderr.length > 0 ? stderr : `pm contracts failed with exit code ${completed.status}`);
|
|
15
|
+
}
|
|
16
|
+
return JSON.parse(completed.stdout ?? "{}");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const requestedAction = (process.argv[2] ?? "create").trim().toLowerCase();
|
|
20
|
+
if (!isPmToolAction(requestedAction)) {
|
|
21
|
+
throw new Error(`Unsupported pm action "${requestedAction}".`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const contracts = runPmContracts();
|
|
25
|
+
const availableActions = Array.isArray(contracts.actions) ? contracts.actions : [];
|
|
26
|
+
if (!availableActions.includes(requestedAction)) {
|
|
27
|
+
throw new Error(`Action "${requestedAction}" is not currently invocable in this runtime.`);
|
|
28
|
+
}
|
|
29
|
+
const actionAvailability = Array.isArray(contracts.action_availability)
|
|
30
|
+
? contracts.action_availability.find((entry) => entry?.action === requestedAction) ?? null
|
|
31
|
+
: null;
|
|
32
|
+
const extensionContracts =
|
|
33
|
+
contracts.extension_contracts && typeof contracts.extension_contracts === "object" ? contracts.extension_contracts : null;
|
|
34
|
+
|
|
35
|
+
const actionContract = PM_TOOL_ACTION_PARAMETER_CONTRACTS[requestedAction];
|
|
36
|
+
const payload = {
|
|
37
|
+
action: requestedAction,
|
|
38
|
+
required_parameters: actionContract.required ?? [],
|
|
39
|
+
optional_parameters: actionContract.optional ?? [],
|
|
40
|
+
any_of_required_groups: actionContract.anyOfRequired ?? [],
|
|
41
|
+
runtime_available: actionAvailability?.available === true,
|
|
42
|
+
policy_state: actionAvailability?.policy_state ?? null,
|
|
43
|
+
compatibility: extensionContracts?.compatibility ?? null,
|
|
44
|
+
manifest_versions: extensionContracts?.manifest_versions ?? [],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pm-sdk-contract-consumer-example",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"inspect:create": "node inspect-contracts.mjs create",
|
|
7
|
+
"inspect:update": "node inspect-contracts.mjs update",
|
|
8
|
+
"inspect:reload": "node inspect-contracts.mjs extension-reload"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,62 +1,77 @@
|
|
|
1
|
-
# Starter Extension
|
|
1
|
+
# Starter Extension (Runnable Example)
|
|
2
2
|
|
|
3
|
-
This example
|
|
3
|
+
This example is a full capability reference extension. It intentionally demonstrates every extension capability surface, including parser/preflight/services/search/schema hooks.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use it to learn APIs, then narrow capabilities for production extensions.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- Use `pm extension init ./my-extension` when you need a smaller scaffold.
|
|
9
|
-
- Keep production extensions narrower than this example.
|
|
7
|
+
## Contents
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
- `manifest.json` -> extension metadata/capabilities
|
|
10
|
+
- `package.json` -> local dependency metadata
|
|
11
|
+
- `index.js` -> capability demonstrations
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
- [SDK](../../SDK.md)
|
|
13
|
+
## End-to-End Run
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
| File | Purpose |
|
|
19
|
-
|------|---------|
|
|
20
|
-
| `manifest.json` | declares extension metadata and capabilities |
|
|
21
|
-
| `package.json` | declares local package metadata and SDK dependency |
|
|
22
|
-
| `index.js` | registers examples for each capability category |
|
|
23
|
-
|
|
24
|
-
## Capability Coverage
|
|
25
|
-
|
|
26
|
-
| Capability | Example surface |
|
|
27
|
-
|------------|-----------------|
|
|
28
|
-
| `commands` | `api.registerCommand(...)` |
|
|
29
|
-
| `parser` | `api.registerParser(...)` |
|
|
30
|
-
| `preflight` | `api.registerPreflight(...)` |
|
|
31
|
-
| `services` | `api.registerService(...)` |
|
|
32
|
-
| `renderers` | `api.registerRenderer(...)` |
|
|
33
|
-
| `hooks` | command, read, write, and index hooks |
|
|
34
|
-
| `schema` | item fields, item types, migrations |
|
|
35
|
-
| `importers` | importer and exporter registration |
|
|
36
|
-
| `search` | search provider and vector adapter |
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
39
|
-
|
|
40
|
-
Copy into an extension root:
|
|
15
|
+
From repository root:
|
|
41
16
|
|
|
42
17
|
```bash
|
|
18
|
+
# 1) Copy into project extension root
|
|
43
19
|
mkdir -p .agents/pm/extensions
|
|
44
20
|
cp -R docs/examples/starter-extension .agents/pm/extensions/starter-extension
|
|
21
|
+
|
|
22
|
+
# 2) Install dependencies for the copied extension
|
|
45
23
|
cd .agents/pm/extensions/starter-extension
|
|
46
24
|
npm install
|
|
25
|
+
cd -
|
|
26
|
+
|
|
27
|
+
# 3) Install/activate in project scope
|
|
28
|
+
pm extension --install --project .agents/pm/extensions/starter-extension
|
|
29
|
+
|
|
30
|
+
# 4) Run a starter command
|
|
31
|
+
pm starter ping --name "agent"
|
|
32
|
+
|
|
33
|
+
# 5) Reload extension modules after edits
|
|
34
|
+
pm extension --reload --project
|
|
35
|
+
|
|
36
|
+
# 6) Optional watch-mode semantics
|
|
37
|
+
pm extension --reload --project --watch
|
|
38
|
+
|
|
39
|
+
# 7) Verify runtime health
|
|
40
|
+
pm extension --doctor --project --detail summary
|
|
47
41
|
```
|
|
48
42
|
|
|
49
|
-
|
|
43
|
+
Expected outcomes:
|
|
44
|
+
|
|
45
|
+
- `pm starter ping` returns deterministic output (plain text when starter service overrides output formatting).
|
|
46
|
+
- `extension --doctor` shows `details.summary.status` as `ok` or `warn`.
|
|
47
|
+
- If `warn`, inspect `details.summary.warning_codes` and `details.triage.remediation`.
|
|
48
|
+
- `extension --reload` returns deterministic load/activation diagnostics for cache-busted imports.
|
|
49
|
+
|
|
50
|
+
## Policy-Restricted Variant
|
|
51
|
+
|
|
52
|
+
To test governance controls with this extension:
|
|
53
|
+
|
|
54
|
+
1. Set `settings.extensions.policy.mode` to `warn` or `enforce`.
|
|
55
|
+
2. Block one surface (for example `commands.override`).
|
|
56
|
+
3. Re-run `pm extension --doctor --detail summary`.
|
|
57
|
+
|
|
58
|
+
You should see `extension_policy_*` warnings and policy counters in `details.triage`.
|
|
59
|
+
|
|
60
|
+
## CI-Friendly Verification Commands
|
|
50
61
|
|
|
51
62
|
```bash
|
|
52
|
-
pm
|
|
53
|
-
pm
|
|
54
|
-
|
|
63
|
+
pm contracts --command extension --flags-only --json
|
|
64
|
+
pm extension --doctor --project --detail summary --strict-exit
|
|
65
|
+
node scripts/run-tests.mjs test -- tests/unit/extension-loader.spec.ts tests/unit/extension-command.spec.ts
|
|
55
66
|
```
|
|
56
67
|
|
|
57
68
|
## Notes
|
|
58
69
|
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
- Keep
|
|
62
|
-
|
|
70
|
+
- Keep production manifests minimal: only declare capabilities you need.
|
|
71
|
+
- Prefer command metadata (`action`, `examples`, `failure_hints`) for machine+human diagnostics.
|
|
72
|
+
- Keep parser/preflight/service overrides narrow and deterministic.
|
|
73
|
+
|
|
74
|
+
## Related Examples
|
|
75
|
+
|
|
76
|
+
- `docs/examples/policy-restricted-extension/README.md`
|
|
77
|
+
- `docs/examples/sdk-contract-consumer/README.md`
|
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"description": "SDK-first starter extension demonstrating all pm extension capabilities.",
|
|
5
5
|
"entry": "./index.js",
|
|
6
|
+
"manifest_version": 2,
|
|
7
|
+
"trusted": true,
|
|
8
|
+
"provenance": {
|
|
9
|
+
"source": "docs/examples/starter-extension",
|
|
10
|
+
"verified": true
|
|
11
|
+
},
|
|
12
|
+
"sandbox_profile": "none",
|
|
13
|
+
"permissions": {
|
|
14
|
+
"fs_read": true,
|
|
15
|
+
"fs_write": true,
|
|
16
|
+
"network": false,
|
|
17
|
+
"env_read": true,
|
|
18
|
+
"env_write": false,
|
|
19
|
+
"process_spawn": false
|
|
20
|
+
},
|
|
6
21
|
"capabilities": [
|
|
7
22
|
"commands",
|
|
8
23
|
"parser",
|
package/marketplace.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "pm
|
|
2
|
+
"name": "pm",
|
|
3
3
|
"description": "Official marketplace for pm CLI — native git-based project management for Claude Code and AI coding agents.",
|
|
4
4
|
"owner": {
|
|
5
5
|
"name": "unbrained",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"plugins": [
|
|
9
9
|
{
|
|
10
10
|
"name": "pm-cli",
|
|
11
|
-
"description": "Native pm CLI integration for Claude Code — 18 MCP tools, 5 workflow skills, 14 slash commands, hybrid TUI task tracking, session context injection, and
|
|
12
|
-
"version": "1.
|
|
11
|
+
"description": "Native pm CLI integration for Claude Code — 18 MCP tools, 5 workflow skills, 14 slash commands, 3 subagents, hybrid TUI task tracking, session context injection, and coordination subagents for git-based project management without leaving Claude Code.",
|
|
12
|
+
"version": "1.3.0",
|
|
13
13
|
"source": "./plugins/pm-cli-claude",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "unbrained",
|