@unbrained/pm-cli 2026.5.10 → 2026.5.12
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/AGENTS.md +3 -116
- package/CHANGELOG.md +14 -0
- package/PRD.md +11 -11
- package/README.md +20 -2
- 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 +40 -2
- 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 +12 -3
- package/dist/cli/commands/extension.js +421 -69
- 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/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/index.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 +23 -8
- 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/commands/upgrade.d.ts +63 -0
- package/dist/cli/commands/upgrade.js +260 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- 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/guide-topics.js +18 -16
- package/dist/cli/guide-topics.js.map +1 -1
- package/dist/cli/help-content.js +42 -2
- package/dist/cli/help-content.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 +174 -82
- 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/packages/manifest.d.ts +13 -0
- package/dist/core/packages/manifest.js +139 -0
- package/dist/core/packages/manifest.js.map +1 -0
- 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 +11 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/sdk/cli-contracts.d.ts +38 -17
- package/dist/sdk/cli-contracts.js +387 -35
- package/dist/sdk/cli-contracts.js.map +1 -1
- package/dist/sdk/index.d.ts +13 -1
- package/dist/sdk/index.js +9 -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/ARCHITECTURE.md +1 -1
- package/docs/CLAUDE_CODE_PLUGIN.md +39 -0
- package/docs/COMMANDS.md +14 -1
- package/docs/EXTENSIONS.md +782 -12
- package/docs/MIGRATION_CLI_SIMPLIFICATION.md +64 -0
- package/docs/QUICKSTART.md +10 -2
- package/docs/README.md +4 -6
- package/docs/SDK.md +445 -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 +5 -23
- package/packages/pm-beads/README.md +10 -0
- package/{.agents/pm → packages/pm-beads}/extensions/beads/index.js +24 -9
- package/packages/pm-beads/extensions/beads/index.ts +131 -0
- package/packages/pm-beads/package.json +17 -0
- package/packages/pm-todos/README.md +11 -0
- package/{.agents/pm → packages/pm-todos}/extensions/todos/index.js +24 -9
- package/packages/pm-todos/extensions/todos/index.ts +149 -0
- package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.js +1 -1
- package/{.agents/pm → packages/pm-todos}/extensions/todos/runtime.ts +1 -1
- package/packages/pm-todos/package.json +17 -0
- package/plugins/pm-cli-claude/.claude-plugin/plugin.json +2 -2
- package/plugins/pm-cli-claude/README.md +54 -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 +35 -21
- package/.agents/pm/extensions/.managed-extensions.json +0 -42
- package/.agents/skills/HARNESS_COMPATIBILITY.md +0 -45
- package/.agents/skills/README.md +0 -21
- package/.agents/skills/pm-developer/SKILL.md +0 -73
- package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +0 -48
- package/.agents/skills/pm-developer/references/PROMPTS.md +0 -17
- package/.agents/skills/pm-extensions/SKILL.md +0 -57
- package/.agents/skills/pm-extensions/references/LIFECYCLE.md +0 -40
- package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +0 -25
- package/.agents/skills/pm-sdk/SKILL.md +0 -50
- package/.agents/skills/pm-sdk/references/INTEGRATION_CHECKLIST.md +0 -31
- package/.agents/skills/pm-sdk/references/PROMPTS.md +0 -13
- package/.agents/skills/pm-user/SKILL.md +0 -59
- package/.agents/skills/pm-user/references/PROMPTS.md +0 -17
- package/.agents/skills/pm-user/references/WORKFLOWS.md +0 -35
- package/.pi/README.md +0 -26
- package/.pi/extensions/pm-cli/index.js +0 -147
- package/.pi/prompts/pm-workflow.md +0 -5
- package/.pi/skills/pm-native/SKILL.md +0 -40
- package/.pi/skills/pm-release/SKILL.md +0 -35
- package/dist/pi/native.d.ts +0 -5
- package/dist/pi/native.js +0 -183
- package/dist/pi/native.js.map +0 -1
- package/docs/PI_PACKAGE.md +0 -56
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/manifest.json +0 -0
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.js +0 -0
- /package/{.agents/pm → packages/pm-beads}/extensions/beads/runtime.ts +0 -0
- /package/{.agents/pm → packages/pm-todos}/extensions/todos/manifest.json +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type ExtensionScope, type ManagedExtensionSource } from "./extension.js";
|
|
2
|
+
import type { GlobalOptions } from "../../core/shared/command-types.js";
|
|
3
|
+
export interface UpgradeCommandOptions {
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
cliOnly?: boolean;
|
|
6
|
+
packagesOnly?: boolean;
|
|
7
|
+
project?: boolean;
|
|
8
|
+
local?: boolean;
|
|
9
|
+
global?: boolean;
|
|
10
|
+
repair?: boolean;
|
|
11
|
+
tag?: string;
|
|
12
|
+
packageName?: string;
|
|
13
|
+
commandRunner?: UpgradeCommandRunner;
|
|
14
|
+
}
|
|
15
|
+
export interface UpgradeCommandRunnerResult {
|
|
16
|
+
stdout: string;
|
|
17
|
+
stderr: string;
|
|
18
|
+
}
|
|
19
|
+
export type UpgradeCommandRunner = (command: string, args: string[], options?: {
|
|
20
|
+
cwd?: string;
|
|
21
|
+
}) => Promise<UpgradeCommandRunnerResult>;
|
|
22
|
+
export interface UpgradeCliResult {
|
|
23
|
+
requested: boolean;
|
|
24
|
+
status: "planned" | "updated" | "failed" | "skipped";
|
|
25
|
+
package: string;
|
|
26
|
+
target: string;
|
|
27
|
+
command: string[];
|
|
28
|
+
before_version?: string;
|
|
29
|
+
after_version?: string;
|
|
30
|
+
repair: boolean;
|
|
31
|
+
reason?: string;
|
|
32
|
+
error?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface UpgradePackageResult {
|
|
35
|
+
name: string;
|
|
36
|
+
directory: string;
|
|
37
|
+
scope: ExtensionScope;
|
|
38
|
+
source: ManagedExtensionSource;
|
|
39
|
+
status: "planned" | "updated" | "failed" | "skipped";
|
|
40
|
+
command: string[];
|
|
41
|
+
previous_version: string;
|
|
42
|
+
installed_version?: string;
|
|
43
|
+
reason?: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface UpgradeResult {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
action: "upgrade";
|
|
49
|
+
dry_run: boolean;
|
|
50
|
+
scope: ExtensionScope;
|
|
51
|
+
target?: string;
|
|
52
|
+
cli: UpgradeCliResult;
|
|
53
|
+
packages: UpgradePackageResult[];
|
|
54
|
+
summary: {
|
|
55
|
+
requested_cli: boolean;
|
|
56
|
+
requested_packages: boolean;
|
|
57
|
+
planned: number;
|
|
58
|
+
updated: number;
|
|
59
|
+
skipped: number;
|
|
60
|
+
failed: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export declare function runUpgrade(target: string | undefined, options: UpgradeCommandOptions, global: GlobalOptions): Promise<UpgradeResult>;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { readManagedExtensionState, runExtension, } from "./extension.js";
|
|
7
|
+
import { resolveExtensionRoots } from "../../core/extensions/loader.js";
|
|
8
|
+
import { pathExists } from "../../core/fs/fs-utils.js";
|
|
9
|
+
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
10
|
+
import { PmCliError } from "../../core/shared/errors.js";
|
|
11
|
+
import { resolvePmRoot } from "../../core/store/paths.js";
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
const DEFAULT_CLI_PACKAGE = "@unbrained/pm-cli";
|
|
14
|
+
const DEFAULT_TAG = "latest";
|
|
15
|
+
function resolveScope(options) {
|
|
16
|
+
const projectLike = options.project === true || options.local === true;
|
|
17
|
+
const global = options.global === true;
|
|
18
|
+
if (projectLike && global) {
|
|
19
|
+
throw new PmCliError('Options "--project/--local" and "--global" are mutually exclusive.', EXIT_CODE.USAGE);
|
|
20
|
+
}
|
|
21
|
+
return global ? "global" : "project";
|
|
22
|
+
}
|
|
23
|
+
function normalizeTarget(value) {
|
|
24
|
+
return value.trim().toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
function packageRecordMatchesTarget(record, target) {
|
|
27
|
+
const normalizedTarget = normalizeTarget(target);
|
|
28
|
+
const values = [
|
|
29
|
+
record.name,
|
|
30
|
+
record.directory,
|
|
31
|
+
record.source.input,
|
|
32
|
+
record.source.location,
|
|
33
|
+
record.source.package,
|
|
34
|
+
record.source.repository,
|
|
35
|
+
record.source.owner && record.source.repo ? `${record.source.owner}/${record.source.repo}` : undefined,
|
|
36
|
+
];
|
|
37
|
+
return values.some((value) => typeof value === "string" && normalizeTarget(value) === normalizedTarget);
|
|
38
|
+
}
|
|
39
|
+
function resolveRoots(scope, global) {
|
|
40
|
+
const pmRoot = resolvePmRoot(process.cwd(), global.path);
|
|
41
|
+
const roots = resolveExtensionRoots(pmRoot, process.cwd());
|
|
42
|
+
return {
|
|
43
|
+
selected_root: scope === "global" ? roots.global : roots.project,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function defaultCommandRunner(command, args, options) {
|
|
47
|
+
try {
|
|
48
|
+
const result = await execFileAsync(command, args, {
|
|
49
|
+
cwd: options?.cwd,
|
|
50
|
+
encoding: "utf8",
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
stdout: result.stdout ?? "",
|
|
54
|
+
stderr: result.stderr ?? "",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
const stderr = typeof error === "object" && error !== null && "stderr" in error
|
|
59
|
+
? String(error.stderr ?? "")
|
|
60
|
+
: "";
|
|
61
|
+
const message = stderr.trim().length > 0 ? stderr.trim() : error instanceof Error ? error.message : String(error);
|
|
62
|
+
throw new PmCliError(`Command failed: ${command} ${args.join(" ")}\n${message}`, EXIT_CODE.GENERIC_FAILURE);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function readCurrentVersion() {
|
|
66
|
+
const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..", "package.json");
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
69
|
+
return typeof parsed.version === "string" ? parsed.version : undefined;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function resolveTag(options) {
|
|
76
|
+
return typeof options.tag === "string" && options.tag.trim().length > 0 ? options.tag.trim() : DEFAULT_TAG;
|
|
77
|
+
}
|
|
78
|
+
function resolveCliPackage(options) {
|
|
79
|
+
return typeof options.packageName === "string" && options.packageName.trim().length > 0
|
|
80
|
+
? options.packageName.trim()
|
|
81
|
+
: DEFAULT_CLI_PACKAGE;
|
|
82
|
+
}
|
|
83
|
+
async function upgradeCli(options, dryRun) {
|
|
84
|
+
const runner = options.commandRunner ?? defaultCommandRunner;
|
|
85
|
+
const packageName = resolveCliPackage(options);
|
|
86
|
+
const tag = resolveTag(options);
|
|
87
|
+
const target = `${packageName}@${tag}`;
|
|
88
|
+
const command = ["npm", "install", "-g", target];
|
|
89
|
+
if (options.repair === true) {
|
|
90
|
+
command.push("--force");
|
|
91
|
+
}
|
|
92
|
+
const beforeVersion = await readCurrentVersion();
|
|
93
|
+
const planned = {
|
|
94
|
+
requested: true,
|
|
95
|
+
status: "planned",
|
|
96
|
+
package: packageName,
|
|
97
|
+
target,
|
|
98
|
+
command,
|
|
99
|
+
before_version: beforeVersion,
|
|
100
|
+
repair: options.repair === true,
|
|
101
|
+
};
|
|
102
|
+
if (dryRun) {
|
|
103
|
+
return planned;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
await runner(command[0], command.slice(1));
|
|
107
|
+
const verified = await runner("pm", ["--version"]);
|
|
108
|
+
return {
|
|
109
|
+
...planned,
|
|
110
|
+
status: "updated",
|
|
111
|
+
after_version: verified.stdout.trim() || undefined,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
...planned,
|
|
117
|
+
status: "failed",
|
|
118
|
+
error: error instanceof Error ? error.message : String(error),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function isLocalNpmSpec(spec) {
|
|
123
|
+
return path.isAbsolute(spec) || spec.startsWith(".") || spec.startsWith("..") || spec.startsWith("file:");
|
|
124
|
+
}
|
|
125
|
+
function resolvePackageInstallSource(source, tag) {
|
|
126
|
+
if (source.kind === "npm") {
|
|
127
|
+
const rawSpec = source.input.startsWith("npm:") ? source.input.slice("npm:".length).trim() : source.input.trim();
|
|
128
|
+
if (!isLocalNpmSpec(rawSpec) && source.package && source.package.trim().length > 0) {
|
|
129
|
+
return `npm:${source.package.trim()}@${tag}`;
|
|
130
|
+
}
|
|
131
|
+
return source.input.startsWith("npm:") ? source.input : `npm:${source.input}`;
|
|
132
|
+
}
|
|
133
|
+
return source.input;
|
|
134
|
+
}
|
|
135
|
+
async function resolveRunnablePackageSource(source, tag) {
|
|
136
|
+
const installSource = resolvePackageInstallSource(source, tag);
|
|
137
|
+
if (source.kind !== "local") {
|
|
138
|
+
return installSource;
|
|
139
|
+
}
|
|
140
|
+
if (await pathExists(installSource)) {
|
|
141
|
+
return installSource;
|
|
142
|
+
}
|
|
143
|
+
if (await pathExists(source.location)) {
|
|
144
|
+
return source.location;
|
|
145
|
+
}
|
|
146
|
+
return installSource;
|
|
147
|
+
}
|
|
148
|
+
function packageCommandFor(source, installSource, scope, ref) {
|
|
149
|
+
const command = ["pm", "install", installSource, scope === "global" ? "--global" : "--project"];
|
|
150
|
+
if (source.kind === "github" && ref && ref.trim().length > 0) {
|
|
151
|
+
command.push("--ref", ref.trim());
|
|
152
|
+
}
|
|
153
|
+
return command;
|
|
154
|
+
}
|
|
155
|
+
async function readManagedRecords(scope, global) {
|
|
156
|
+
const roots = resolveRoots(scope, global);
|
|
157
|
+
const managedState = await readManagedExtensionState(roots.selected_root);
|
|
158
|
+
return managedState.state.entries.filter((entry) => entry.scope === scope);
|
|
159
|
+
}
|
|
160
|
+
async function refreshManagedRecord(scope, global, record) {
|
|
161
|
+
const records = await readManagedRecords(scope, global);
|
|
162
|
+
return records.find((candidate) => packageRecordMatchesTarget(candidate, record.name)) ??
|
|
163
|
+
records.find((candidate) => normalizeTarget(candidate.directory) === normalizeTarget(record.directory));
|
|
164
|
+
}
|
|
165
|
+
async function upgradePackageRecord(record, options, global, scope, dryRun) {
|
|
166
|
+
const tag = resolveTag(options);
|
|
167
|
+
const installSource = await resolveRunnablePackageSource(record.source, tag);
|
|
168
|
+
const command = packageCommandFor(record.source, installSource, scope, record.source.ref);
|
|
169
|
+
const planned = {
|
|
170
|
+
name: record.name,
|
|
171
|
+
directory: record.directory,
|
|
172
|
+
scope,
|
|
173
|
+
source: record.source,
|
|
174
|
+
status: "planned",
|
|
175
|
+
command,
|
|
176
|
+
previous_version: record.manifest_version,
|
|
177
|
+
};
|
|
178
|
+
if (dryRun) {
|
|
179
|
+
return planned;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
await runExtension(installSource, {
|
|
183
|
+
install: true,
|
|
184
|
+
project: scope === "project",
|
|
185
|
+
global: scope === "global",
|
|
186
|
+
ref: record.source.kind === "github" ? record.source.ref : undefined,
|
|
187
|
+
}, global);
|
|
188
|
+
const refreshed = await refreshManagedRecord(scope, global, record);
|
|
189
|
+
return {
|
|
190
|
+
...planned,
|
|
191
|
+
status: "updated",
|
|
192
|
+
installed_version: refreshed?.manifest_version,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
return {
|
|
197
|
+
...planned,
|
|
198
|
+
status: "failed",
|
|
199
|
+
error: error instanceof Error ? error.message : String(error),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function summarize(cli, packages, includeCli, includePackages) {
|
|
204
|
+
const statuses = [includeCli ? cli.status : undefined, ...packages.map((entry) => entry.status)].filter((value) => typeof value === "string");
|
|
205
|
+
return {
|
|
206
|
+
requested_cli: includeCli,
|
|
207
|
+
requested_packages: includePackages,
|
|
208
|
+
planned: statuses.filter((status) => status === "planned").length,
|
|
209
|
+
updated: statuses.filter((status) => status === "updated").length,
|
|
210
|
+
skipped: statuses.filter((status) => status === "skipped").length,
|
|
211
|
+
failed: statuses.filter((status) => status === "failed").length,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
export async function runUpgrade(target, options, global) {
|
|
215
|
+
if (options.cliOnly === true && options.packagesOnly === true) {
|
|
216
|
+
throw new PmCliError('Options "--cli-only" and "--packages-only" are mutually exclusive.', EXIT_CODE.USAGE);
|
|
217
|
+
}
|
|
218
|
+
const scope = resolveScope(options);
|
|
219
|
+
const dryRun = options.dryRun === true;
|
|
220
|
+
const normalizedTarget = typeof target === "string" && target.trim().length > 0 ? target.trim() : undefined;
|
|
221
|
+
if (options.cliOnly === true && normalizedTarget) {
|
|
222
|
+
throw new PmCliError('A package target cannot be used with "--cli-only".', EXIT_CODE.USAGE);
|
|
223
|
+
}
|
|
224
|
+
const includeCli = options.packagesOnly === true || normalizedTarget ? false : true;
|
|
225
|
+
const includePackages = options.cliOnly === true ? false : true;
|
|
226
|
+
const cli = includeCli
|
|
227
|
+
? await upgradeCli(options, dryRun)
|
|
228
|
+
: {
|
|
229
|
+
requested: false,
|
|
230
|
+
status: "skipped",
|
|
231
|
+
package: resolveCliPackage(options),
|
|
232
|
+
target: `${resolveCliPackage(options)}@${resolveTag(options)}`,
|
|
233
|
+
command: ["npm", "install", "-g", `${resolveCliPackage(options)}@${resolveTag(options)}`],
|
|
234
|
+
repair: options.repair === true,
|
|
235
|
+
reason: "not_requested",
|
|
236
|
+
};
|
|
237
|
+
let packageRecords = includePackages ? await readManagedRecords(scope, global) : [];
|
|
238
|
+
if (normalizedTarget) {
|
|
239
|
+
packageRecords = packageRecords.filter((entry) => packageRecordMatchesTarget(entry, normalizedTarget));
|
|
240
|
+
if (packageRecords.length === 0) {
|
|
241
|
+
throw new PmCliError(`Managed package "${normalizedTarget}" was not found in ${scope} scope.`, EXIT_CODE.NOT_FOUND);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const packages = [];
|
|
245
|
+
for (const record of packageRecords) {
|
|
246
|
+
packages.push(await upgradePackageRecord(record, options, global, scope, dryRun));
|
|
247
|
+
}
|
|
248
|
+
const summary = summarize(cli, packages, includeCli, includePackages);
|
|
249
|
+
return {
|
|
250
|
+
ok: summary.failed === 0,
|
|
251
|
+
action: "upgrade",
|
|
252
|
+
dry_run: dryRun,
|
|
253
|
+
scope,
|
|
254
|
+
target: normalizedTarget,
|
|
255
|
+
cli,
|
|
256
|
+
packages,
|
|
257
|
+
summary,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"/","sources":["cli/commands/upgrade.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { promisify } from \"node:util\";\nimport {\n readManagedExtensionState,\n runExtension,\n type ExtensionScope,\n type ManagedExtensionRecord,\n type ManagedExtensionSource,\n} from \"./extension.js\";\nimport { resolveExtensionRoots } from \"../../core/extensions/loader.js\";\nimport { pathExists } from \"../../core/fs/fs-utils.js\";\nimport type { GlobalOptions } from \"../../core/shared/command-types.js\";\nimport { EXIT_CODE } from \"../../core/shared/constants.js\";\nimport { PmCliError } from \"../../core/shared/errors.js\";\nimport { resolvePmRoot } from \"../../core/store/paths.js\";\n\nconst execFileAsync = promisify(execFile);\nconst DEFAULT_CLI_PACKAGE = \"@unbrained/pm-cli\";\nconst DEFAULT_TAG = \"latest\";\n\nexport interface UpgradeCommandOptions {\n dryRun?: boolean;\n cliOnly?: boolean;\n packagesOnly?: boolean;\n project?: boolean;\n local?: boolean;\n global?: boolean;\n repair?: boolean;\n tag?: string;\n packageName?: string;\n commandRunner?: UpgradeCommandRunner;\n}\n\nexport interface UpgradeCommandRunnerResult {\n stdout: string;\n stderr: string;\n}\n\nexport type UpgradeCommandRunner = (\n command: string,\n args: string[],\n options?: { cwd?: string },\n) => Promise<UpgradeCommandRunnerResult>;\n\nexport interface UpgradeCliResult {\n requested: boolean;\n status: \"planned\" | \"updated\" | \"failed\" | \"skipped\";\n package: string;\n target: string;\n command: string[];\n before_version?: string;\n after_version?: string;\n repair: boolean;\n reason?: string;\n error?: string;\n}\n\nexport interface UpgradePackageResult {\n name: string;\n directory: string;\n scope: ExtensionScope;\n source: ManagedExtensionSource;\n status: \"planned\" | \"updated\" | \"failed\" | \"skipped\";\n command: string[];\n previous_version: string;\n installed_version?: string;\n reason?: string;\n error?: string;\n}\n\nexport interface UpgradeResult {\n ok: boolean;\n action: \"upgrade\";\n dry_run: boolean;\n scope: ExtensionScope;\n target?: string;\n cli: UpgradeCliResult;\n packages: UpgradePackageResult[];\n summary: {\n requested_cli: boolean;\n requested_packages: boolean;\n planned: number;\n updated: number;\n skipped: number;\n failed: number;\n };\n}\n\nfunction resolveScope(options: UpgradeCommandOptions): ExtensionScope {\n const projectLike = options.project === true || options.local === true;\n const global = options.global === true;\n if (projectLike && global) {\n throw new PmCliError('Options \"--project/--local\" and \"--global\" are mutually exclusive.', EXIT_CODE.USAGE);\n }\n return global ? \"global\" : \"project\";\n}\n\nfunction normalizeTarget(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction packageRecordMatchesTarget(record: ManagedExtensionRecord, target: string): boolean {\n const normalizedTarget = normalizeTarget(target);\n const values = [\n record.name,\n record.directory,\n record.source.input,\n record.source.location,\n record.source.package,\n record.source.repository,\n record.source.owner && record.source.repo ? `${record.source.owner}/${record.source.repo}` : undefined,\n ];\n return values.some((value) => typeof value === \"string\" && normalizeTarget(value) === normalizedTarget);\n}\n\nfunction resolveRoots(scope: ExtensionScope, global: GlobalOptions): {\n selected_root: string;\n} {\n const pmRoot = resolvePmRoot(process.cwd(), global.path);\n const roots = resolveExtensionRoots(pmRoot, process.cwd());\n return {\n selected_root: scope === \"global\" ? roots.global : roots.project,\n };\n}\n\nasync function defaultCommandRunner(\n command: string,\n args: string[],\n options?: { cwd?: string },\n): Promise<UpgradeCommandRunnerResult> {\n try {\n const result = await execFileAsync(command, args, {\n cwd: options?.cwd,\n encoding: \"utf8\",\n });\n return {\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n };\n } catch (error: unknown) {\n const stderr = typeof error === \"object\" && error !== null && \"stderr\" in error\n ? String((error as { stderr?: unknown }).stderr ?? \"\")\n : \"\";\n const message = stderr.trim().length > 0 ? stderr.trim() : error instanceof Error ? error.message : String(error);\n throw new PmCliError(`Command failed: ${command} ${args.join(\" \")}\\n${message}`, EXIT_CODE.GENERIC_FAILURE);\n }\n}\n\nasync function readCurrentVersion(): Promise<string | undefined> {\n const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"../../..\", \"package.json\");\n try {\n const parsed = JSON.parse(await fs.readFile(packageJsonPath, \"utf8\")) as { version?: unknown };\n return typeof parsed.version === \"string\" ? parsed.version : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction resolveTag(options: UpgradeCommandOptions): string {\n return typeof options.tag === \"string\" && options.tag.trim().length > 0 ? options.tag.trim() : DEFAULT_TAG;\n}\n\nfunction resolveCliPackage(options: UpgradeCommandOptions): string {\n return typeof options.packageName === \"string\" && options.packageName.trim().length > 0\n ? options.packageName.trim()\n : DEFAULT_CLI_PACKAGE;\n}\n\nasync function upgradeCli(options: UpgradeCommandOptions, dryRun: boolean): Promise<UpgradeCliResult> {\n const runner = options.commandRunner ?? defaultCommandRunner;\n const packageName = resolveCliPackage(options);\n const tag = resolveTag(options);\n const target = `${packageName}@${tag}`;\n const command = [\"npm\", \"install\", \"-g\", target];\n if (options.repair === true) {\n command.push(\"--force\");\n }\n const beforeVersion = await readCurrentVersion();\n const planned: UpgradeCliResult = {\n requested: true,\n status: \"planned\",\n package: packageName,\n target,\n command,\n before_version: beforeVersion,\n repair: options.repair === true,\n };\n if (dryRun) {\n return planned;\n }\n try {\n await runner(command[0]!, command.slice(1));\n const verified = await runner(\"pm\", [\"--version\"]);\n return {\n ...planned,\n status: \"updated\",\n after_version: verified.stdout.trim() || undefined,\n };\n } catch (error: unknown) {\n return {\n ...planned,\n status: \"failed\",\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nfunction isLocalNpmSpec(spec: string): boolean {\n return path.isAbsolute(spec) || spec.startsWith(\".\") || spec.startsWith(\"..\") || spec.startsWith(\"file:\");\n}\n\nfunction resolvePackageInstallSource(source: ManagedExtensionSource, tag: string): string {\n if (source.kind === \"npm\") {\n const rawSpec = source.input.startsWith(\"npm:\") ? source.input.slice(\"npm:\".length).trim() : source.input.trim();\n if (!isLocalNpmSpec(rawSpec) && source.package && source.package.trim().length > 0) {\n return `npm:${source.package.trim()}@${tag}`;\n }\n return source.input.startsWith(\"npm:\") ? source.input : `npm:${source.input}`;\n }\n return source.input;\n}\n\nasync function resolveRunnablePackageSource(source: ManagedExtensionSource, tag: string): Promise<string> {\n const installSource = resolvePackageInstallSource(source, tag);\n if (source.kind !== \"local\") {\n return installSource;\n }\n if (await pathExists(installSource)) {\n return installSource;\n }\n if (await pathExists(source.location)) {\n return source.location;\n }\n return installSource;\n}\n\nfunction packageCommandFor(source: ManagedExtensionSource, installSource: string, scope: ExtensionScope, ref?: string): string[] {\n const command = [\"pm\", \"install\", installSource, scope === \"global\" ? \"--global\" : \"--project\"];\n if (source.kind === \"github\" && ref && ref.trim().length > 0) {\n command.push(\"--ref\", ref.trim());\n }\n return command;\n}\n\nasync function readManagedRecords(scope: ExtensionScope, global: GlobalOptions): Promise<ManagedExtensionRecord[]> {\n const roots = resolveRoots(scope, global);\n const managedState = await readManagedExtensionState(roots.selected_root);\n return managedState.state.entries.filter((entry) => entry.scope === scope);\n}\n\nasync function refreshManagedRecord(\n scope: ExtensionScope,\n global: GlobalOptions,\n record: ManagedExtensionRecord,\n): Promise<ManagedExtensionRecord | undefined> {\n const records = await readManagedRecords(scope, global);\n return records.find((candidate) => packageRecordMatchesTarget(candidate, record.name)) ??\n records.find((candidate) => normalizeTarget(candidate.directory) === normalizeTarget(record.directory));\n}\n\nasync function upgradePackageRecord(\n record: ManagedExtensionRecord,\n options: UpgradeCommandOptions,\n global: GlobalOptions,\n scope: ExtensionScope,\n dryRun: boolean,\n): Promise<UpgradePackageResult> {\n const tag = resolveTag(options);\n const installSource = await resolveRunnablePackageSource(record.source, tag);\n const command = packageCommandFor(record.source, installSource, scope, record.source.ref);\n const planned: UpgradePackageResult = {\n name: record.name,\n directory: record.directory,\n scope,\n source: record.source,\n status: \"planned\",\n command,\n previous_version: record.manifest_version,\n };\n if (dryRun) {\n return planned;\n }\n try {\n await runExtension(\n installSource,\n {\n install: true,\n project: scope === \"project\",\n global: scope === \"global\",\n ref: record.source.kind === \"github\" ? record.source.ref : undefined,\n },\n global,\n );\n const refreshed = await refreshManagedRecord(scope, global, record);\n return {\n ...planned,\n status: \"updated\",\n installed_version: refreshed?.manifest_version,\n };\n } catch (error: unknown) {\n return {\n ...planned,\n status: \"failed\",\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nfunction summarize(\n cli: UpgradeCliResult,\n packages: UpgradePackageResult[],\n includeCli: boolean,\n includePackages: boolean,\n): UpgradeResult[\"summary\"] {\n const statuses = [includeCli ? cli.status : undefined, ...packages.map((entry) => entry.status)].filter(\n (value): value is UpgradeCliResult[\"status\"] => typeof value === \"string\",\n );\n return {\n requested_cli: includeCli,\n requested_packages: includePackages,\n planned: statuses.filter((status) => status === \"planned\").length,\n updated: statuses.filter((status) => status === \"updated\").length,\n skipped: statuses.filter((status) => status === \"skipped\").length,\n failed: statuses.filter((status) => status === \"failed\").length,\n };\n}\n\nexport async function runUpgrade(\n target: string | undefined,\n options: UpgradeCommandOptions,\n global: GlobalOptions,\n): Promise<UpgradeResult> {\n if (options.cliOnly === true && options.packagesOnly === true) {\n throw new PmCliError('Options \"--cli-only\" and \"--packages-only\" are mutually exclusive.', EXIT_CODE.USAGE);\n }\n\n const scope = resolveScope(options);\n const dryRun = options.dryRun === true;\n const normalizedTarget = typeof target === \"string\" && target.trim().length > 0 ? target.trim() : undefined;\n if (options.cliOnly === true && normalizedTarget) {\n throw new PmCliError('A package target cannot be used with \"--cli-only\".', EXIT_CODE.USAGE);\n }\n const includeCli = options.packagesOnly === true || normalizedTarget ? false : true;\n const includePackages = options.cliOnly === true ? false : true;\n const cli = includeCli\n ? await upgradeCli(options, dryRun)\n : {\n requested: false,\n status: \"skipped\",\n package: resolveCliPackage(options),\n target: `${resolveCliPackage(options)}@${resolveTag(options)}`,\n command: [\"npm\", \"install\", \"-g\", `${resolveCliPackage(options)}@${resolveTag(options)}`],\n repair: options.repair === true,\n reason: \"not_requested\",\n } as UpgradeCliResult;\n\n let packageRecords = includePackages ? await readManagedRecords(scope, global) : [];\n if (normalizedTarget) {\n packageRecords = packageRecords.filter((entry) => packageRecordMatchesTarget(entry, normalizedTarget));\n if (packageRecords.length === 0) {\n throw new PmCliError(`Managed package \"${normalizedTarget}\" was not found in ${scope} scope.`, EXIT_CODE.NOT_FOUND);\n }\n }\n\n const packages: UpgradePackageResult[] = [];\n for (const record of packageRecords) {\n packages.push(await upgradePackageRecord(record, options, global, scope, dryRun));\n }\n\n const summary = summarize(cli, packages, includeCli, includePackages);\n return {\n ok: summary.failed === 0,\n action: \"upgrade\",\n dry_run: dryRun,\n scope,\n target: normalizedTarget,\n cli,\n packages,\n summary,\n };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PmCliErrorContext } from "../core/shared/errors.js";
|
|
1
|
+
import type { PmCliErrorContext, PmCliErrorRecoveryPayload } from "../core/shared/errors.js";
|
|
2
2
|
interface GuidanceMessage {
|
|
3
3
|
code: string;
|
|
4
4
|
type: string;
|
|
@@ -8,6 +8,7 @@ interface GuidanceMessage {
|
|
|
8
8
|
why?: string;
|
|
9
9
|
examples?: string[];
|
|
10
10
|
nextSteps?: string[];
|
|
11
|
+
recovery?: PmCliErrorRecoveryPayload;
|
|
11
12
|
}
|
|
12
13
|
export interface JsonErrorEnvelope {
|
|
13
14
|
type: string;
|
|
@@ -19,6 +20,7 @@ export interface JsonErrorEnvelope {
|
|
|
19
20
|
why?: string;
|
|
20
21
|
examples?: string[];
|
|
21
22
|
next_steps?: string[];
|
|
23
|
+
recovery?: PmCliErrorRecoveryPayload;
|
|
22
24
|
}
|
|
23
25
|
export interface ErrorClassification {
|
|
24
26
|
type: string;
|
|
@@ -29,10 +31,16 @@ export interface ErrorClassification {
|
|
|
29
31
|
why?: string;
|
|
30
32
|
examples?: string[];
|
|
31
33
|
next_steps?: string[];
|
|
34
|
+
recovery?: PmCliErrorRecoveryPayload;
|
|
32
35
|
}
|
|
33
36
|
export interface CommanderGuidanceContext {
|
|
34
37
|
unknownCommandExamples?: string[];
|
|
35
38
|
unknownCommandNextSteps?: string[];
|
|
39
|
+
attemptedCommand?: string;
|
|
40
|
+
normalizedInvocationArgs?: string[];
|
|
41
|
+
providedOptionFlags?: string[];
|
|
42
|
+
unknownOptionSuggestions?: string[];
|
|
43
|
+
suggestedRetryCommand?: string;
|
|
36
44
|
}
|
|
37
45
|
export declare function renderGuidanceMessage(message: GuidanceMessage): string;
|
|
38
46
|
export declare function formatPmCliErrorForDisplay(rawMessage: string, context?: PmCliErrorContext): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderPmCommand } from "./argv-utils.js";
|
|
1
2
|
function errorType(code) {
|
|
2
3
|
return `urn:pm-cli:error:${code}`;
|
|
3
4
|
}
|
|
@@ -13,6 +14,60 @@ function renderList(title, entries) {
|
|
|
13
14
|
}
|
|
14
15
|
return [title, ...entries.map((entry) => ` - ${entry}`)];
|
|
15
16
|
}
|
|
17
|
+
function normalizeRecoveryPayload(payload) {
|
|
18
|
+
if (!payload || typeof payload !== "object") {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const normalized = {};
|
|
22
|
+
if (typeof payload.attempted_command === "string" && payload.attempted_command.trim().length > 0) {
|
|
23
|
+
normalized.attempted_command = payload.attempted_command.trim();
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(payload.normalized_args)) {
|
|
26
|
+
const args = payload.normalized_args.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
27
|
+
if (args.length > 0) {
|
|
28
|
+
normalized.normalized_args = args;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(payload.provided_fields)) {
|
|
32
|
+
const fields = payload.provided_fields.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
33
|
+
if (fields.length > 0) {
|
|
34
|
+
normalized.provided_fields = fields;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (Array.isArray(payload.missing)) {
|
|
38
|
+
const missing = payload.missing.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
39
|
+
if (missing.length > 0) {
|
|
40
|
+
normalized.missing = missing;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (typeof payload.suggested_retry === "string" && payload.suggested_retry.trim().length > 0) {
|
|
44
|
+
normalized.suggested_retry = payload.suggested_retry.trim();
|
|
45
|
+
}
|
|
46
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
47
|
+
}
|
|
48
|
+
function renderRecoveryBundle(recovery) {
|
|
49
|
+
const normalized = normalizeRecoveryPayload(recovery);
|
|
50
|
+
if (!normalized) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
const lines = ["Recovery bundle:"];
|
|
54
|
+
if (normalized.attempted_command) {
|
|
55
|
+
lines.push(` attempted_command: ${normalized.attempted_command}`);
|
|
56
|
+
}
|
|
57
|
+
if (normalized.normalized_args && normalized.normalized_args.length > 0) {
|
|
58
|
+
lines.push(` normalized_args: ${normalized.normalized_args.join(" ")}`);
|
|
59
|
+
}
|
|
60
|
+
if (normalized.provided_fields && normalized.provided_fields.length > 0) {
|
|
61
|
+
lines.push(` provided_fields: ${normalized.provided_fields.join(", ")}`);
|
|
62
|
+
}
|
|
63
|
+
if (normalized.missing && normalized.missing.length > 0) {
|
|
64
|
+
lines.push(` missing: ${normalized.missing.join(", ")}`);
|
|
65
|
+
}
|
|
66
|
+
if (normalized.suggested_retry) {
|
|
67
|
+
lines.push(` suggested_retry: ${normalized.suggested_retry}`);
|
|
68
|
+
}
|
|
69
|
+
return lines;
|
|
70
|
+
}
|
|
16
71
|
export function renderGuidanceMessage(message) {
|
|
17
72
|
const lines = [
|
|
18
73
|
`Error: ${message.title}`,
|
|
@@ -35,6 +90,11 @@ export function renderGuidanceMessage(message) {
|
|
|
35
90
|
lines.push("");
|
|
36
91
|
lines.push(...renderList("Next steps:", message.nextSteps));
|
|
37
92
|
}
|
|
93
|
+
const recoveryLines = renderRecoveryBundle(message.recovery);
|
|
94
|
+
if (recoveryLines.length > 0) {
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push(...recoveryLines);
|
|
97
|
+
}
|
|
38
98
|
return lines.join("\n");
|
|
39
99
|
}
|
|
40
100
|
function guidanceToJsonEnvelope(message, exitCode) {
|
|
@@ -55,6 +115,9 @@ function guidanceToJsonEnvelope(message, exitCode) {
|
|
|
55
115
|
if (message.nextSteps && message.nextSteps.length > 0) {
|
|
56
116
|
payload.next_steps = message.nextSteps;
|
|
57
117
|
}
|
|
118
|
+
if (message.recovery) {
|
|
119
|
+
payload.recovery = message.recovery;
|
|
120
|
+
}
|
|
58
121
|
return payload;
|
|
59
122
|
}
|
|
60
123
|
function guidanceToClassification(message) {
|
|
@@ -74,6 +137,9 @@ function guidanceToClassification(message) {
|
|
|
74
137
|
if (message.nextSteps && message.nextSteps.length > 0) {
|
|
75
138
|
payload.next_steps = message.nextSteps;
|
|
76
139
|
}
|
|
140
|
+
if (message.recovery) {
|
|
141
|
+
payload.recovery = message.recovery;
|
|
142
|
+
}
|
|
77
143
|
return payload;
|
|
78
144
|
}
|
|
79
145
|
function normalizeMessage(message) {
|
|
@@ -106,6 +172,7 @@ function applyPmCliErrorContext(guidance, rawMessage, context) {
|
|
|
106
172
|
const examples = normalizeContextList(context.examples) ?? guidance.examples;
|
|
107
173
|
const nextSteps = normalizeContextList(context.nextSteps) ?? guidance.nextSteps;
|
|
108
174
|
const fallbackTitle = guidance.code === "command_failed" && context.code ? buildFallbackTitleFromMessage(normalizedRawMessage) : undefined;
|
|
175
|
+
const recovery = normalizeRecoveryPayload(context.recovery) ?? guidance.recovery;
|
|
109
176
|
return {
|
|
110
177
|
...guidance,
|
|
111
178
|
code,
|
|
@@ -116,6 +183,7 @@ function applyPmCliErrorContext(guidance, rawMessage, context) {
|
|
|
116
183
|
why: typeof context.why === "string" && context.why.trim().length > 0 ? context.why.trim() : guidance.why,
|
|
117
184
|
examples,
|
|
118
185
|
nextSteps,
|
|
186
|
+
recovery,
|
|
119
187
|
};
|
|
120
188
|
}
|
|
121
189
|
function buildPmCliErrorGuidance(rawMessage, context) {
|
|
@@ -267,12 +335,65 @@ function commandExampleForRequiredOption(commandName, optionFlag, allowedTypes)
|
|
|
267
335
|
}
|
|
268
336
|
return [`pm ${commandName ?? "<command>"} --help`];
|
|
269
337
|
}
|
|
338
|
+
function normalizeRequiredOptionLabel(rawValue) {
|
|
339
|
+
const normalized = rawValue.trim();
|
|
340
|
+
const firstLongFlag = normalized.match(/--[A-Za-z0-9][A-Za-z0-9_-]*/)?.[0];
|
|
341
|
+
return firstLongFlag ?? normalized;
|
|
342
|
+
}
|
|
343
|
+
function renderPmCommandFromArgs(argv) {
|
|
344
|
+
if (!Array.isArray(argv) || argv.length === 0) {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
return renderPmCommand(argv);
|
|
348
|
+
}
|
|
349
|
+
function normalizeOptionFlags(values) {
|
|
350
|
+
if (!Array.isArray(values)) {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
const normalized = values.map((value) => value.trim()).filter((value) => value.length > 0);
|
|
354
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
355
|
+
}
|
|
356
|
+
function buildCommanderRecoveryPayload(context, overrides = {}) {
|
|
357
|
+
const providedFields = normalizeOptionFlags(context?.providedOptionFlags);
|
|
358
|
+
const normalizedArgs = Array.isArray(context?.normalizedInvocationArgs) && context?.normalizedInvocationArgs.length > 0
|
|
359
|
+
? context.normalizedInvocationArgs
|
|
360
|
+
: undefined;
|
|
361
|
+
const attemptedCommand = typeof context?.attemptedCommand === "string" ? context.attemptedCommand : renderPmCommandFromArgs(normalizedArgs);
|
|
362
|
+
const retryCommand = typeof context?.suggestedRetryCommand === "string" ? context.suggestedRetryCommand : undefined;
|
|
363
|
+
return normalizeRecoveryPayload({
|
|
364
|
+
attempted_command: attemptedCommand,
|
|
365
|
+
normalized_args: normalizedArgs,
|
|
366
|
+
provided_fields: providedFields,
|
|
367
|
+
suggested_retry: retryCommand,
|
|
368
|
+
...overrides,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function appendIfMissing(entries, value) {
|
|
372
|
+
if (!value || entries.includes(value)) {
|
|
373
|
+
return entries;
|
|
374
|
+
}
|
|
375
|
+
return [...entries, value];
|
|
376
|
+
}
|
|
270
377
|
function buildCommanderErrorGuidance(rawMessage, commandName, allowedTypes, context) {
|
|
271
378
|
const message = normalizeMessage(rawMessage);
|
|
272
379
|
const requiredOption = message.match(/required option '([^']+)' not specified/);
|
|
273
380
|
if (requiredOption) {
|
|
274
|
-
const optionFlag = requiredOption[1];
|
|
381
|
+
const optionFlag = normalizeRequiredOptionLabel(requiredOption[1]);
|
|
275
382
|
const isType = optionFlag.startsWith("--type");
|
|
383
|
+
const retryCommand = context?.suggestedRetryCommand;
|
|
384
|
+
const providedFlags = normalizeOptionFlags(context?.providedOptionFlags);
|
|
385
|
+
const missing = [optionFlag];
|
|
386
|
+
const examples = commandExampleForRequiredOption(commandName, optionFlag, allowedTypes);
|
|
387
|
+
const examplesWithRetry = retryCommand ? appendIfMissing(examples, retryCommand) : examples;
|
|
388
|
+
const nextStepsBase = isType
|
|
389
|
+
? [`Allowed type values: ${allowedTypes}`, `Run "pm ${commandName ?? "create"} --help --type <value>" for type-aware policy details.`]
|
|
390
|
+
: [`Run "pm ${commandName ?? "<command>"} --help" for required option guidance.`];
|
|
391
|
+
const nextStepsWithRetry = retryCommand
|
|
392
|
+
? appendIfMissing(nextStepsBase, `Replay with preserved arguments: ${retryCommand}`)
|
|
393
|
+
: nextStepsBase;
|
|
394
|
+
const nextSteps = providedFlags && providedFlags.length > 0
|
|
395
|
+
? appendIfMissing(nextStepsWithRetry, `Already provided options: ${providedFlags.join(", ")}`)
|
|
396
|
+
: nextStepsWithRetry;
|
|
276
397
|
return makeGuidanceMessage({
|
|
277
398
|
code: "missing_required_option",
|
|
278
399
|
title: `Missing required option ${optionFlag}`,
|
|
@@ -281,10 +402,9 @@ function buildCommanderErrorGuidance(rawMessage, commandName, allowedTypes, cont
|
|
|
281
402
|
why: isType
|
|
282
403
|
? "--type selects item contract and policy routing, including required/disabled option rules."
|
|
283
404
|
: "Required flags define mandatory command intent and prevent ambiguous execution.",
|
|
284
|
-
examples:
|
|
285
|
-
nextSteps
|
|
286
|
-
|
|
287
|
-
: [`Run "pm ${commandName ?? "<command>"} --help" for required option guidance.`],
|
|
405
|
+
examples: examplesWithRetry,
|
|
406
|
+
nextSteps,
|
|
407
|
+
recovery: buildCommanderRecoveryPayload(context, { missing }),
|
|
288
408
|
});
|
|
289
409
|
}
|
|
290
410
|
const missingArgument = message.match(/missing required argument '([^']+)'/);
|
|
@@ -297,11 +417,14 @@ function buildCommanderErrorGuidance(rawMessage, commandName, allowedTypes, cont
|
|
|
297
417
|
required: `Provide ${argumentName} in the expected command position.`,
|
|
298
418
|
why: "Positional arguments identify the target entity or action context for the command.",
|
|
299
419
|
examples: [`pm ${commandName ?? "<command>"} --help`],
|
|
420
|
+
recovery: buildCommanderRecoveryPayload(context, { missing: [argumentName] }),
|
|
300
421
|
});
|
|
301
422
|
}
|
|
302
423
|
const unknownOption = message.match(/unknown option '([^']+)'/);
|
|
303
424
|
if (unknownOption) {
|
|
304
425
|
const optionName = unknownOption[1];
|
|
426
|
+
const suggestions = normalizeOptionFlags(context?.unknownOptionSuggestions);
|
|
427
|
+
const retryCommand = context?.suggestedRetryCommand;
|
|
305
428
|
if (commandName === "update" && (optionName === "--file" || optionName === "--doc")) {
|
|
306
429
|
return makeGuidanceMessage({
|
|
307
430
|
code: "unsupported_update_option",
|
|
@@ -314,15 +437,31 @@ function buildCommanderErrorGuidance(rawMessage, commandName, allowedTypes, cont
|
|
|
314
437
|
'pm docs pm-a1b2 --add "path=README.md,scope=project,note=user-facing contract"',
|
|
315
438
|
],
|
|
316
439
|
nextSteps: ['Run "pm files --help" and "pm docs --help" for add/remove payload formats.'],
|
|
440
|
+
recovery: buildCommanderRecoveryPayload(context, {
|
|
441
|
+
missing: suggestions,
|
|
442
|
+
}),
|
|
317
443
|
});
|
|
318
444
|
}
|
|
445
|
+
const nextSteps = [
|
|
446
|
+
"Run command help to confirm the exact option contracts for this command path.",
|
|
447
|
+
...(suggestions && suggestions.length > 0 ? [`Nearest supported options: ${suggestions.join(", ")}`] : []),
|
|
448
|
+
...(retryCommand ? [`Replay with suggested correction: ${retryCommand}`] : []),
|
|
449
|
+
];
|
|
450
|
+
const examples = [
|
|
451
|
+
...(retryCommand ? [retryCommand] : []),
|
|
452
|
+
`pm ${commandName ?? "<command>"} --help`,
|
|
453
|
+
];
|
|
319
454
|
return makeGuidanceMessage({
|
|
320
455
|
code: "unknown_option",
|
|
321
456
|
title: `Unknown option ${optionName}`,
|
|
322
457
|
happened: `Commander does not recognize option ${optionName} for this command path.`,
|
|
323
458
|
required: "Use supported options only, or move option to the correct subcommand.",
|
|
324
459
|
why: "Option contracts are command-specific and intentionally validated.",
|
|
325
|
-
examples
|
|
460
|
+
examples,
|
|
461
|
+
nextSteps,
|
|
462
|
+
recovery: buildCommanderRecoveryPayload(context, {
|
|
463
|
+
missing: suggestions,
|
|
464
|
+
}),
|
|
326
465
|
});
|
|
327
466
|
}
|
|
328
467
|
const unknownCommand = message.match(/unknown command '([^']+)'/);
|
|
@@ -338,6 +477,7 @@ function buildCommanderErrorGuidance(rawMessage, commandName, allowedTypes, cont
|
|
|
338
477
|
why: "Command registry includes core commands plus active extension command handlers.",
|
|
339
478
|
examples: runtimeExamples ?? ["pm --help"],
|
|
340
479
|
nextSteps: runtimeNextSteps ?? ["Verify spelling and active extensions, then rerun."],
|
|
480
|
+
recovery: buildCommanderRecoveryPayload(context),
|
|
341
481
|
});
|
|
342
482
|
}
|
|
343
483
|
return makeGuidanceMessage({
|
|
@@ -347,6 +487,7 @@ function buildCommanderErrorGuidance(rawMessage, commandName, allowedTypes, cont
|
|
|
347
487
|
required: "Use the command with valid arguments and options.",
|
|
348
488
|
why: "Commander validates CLI contracts before execution.",
|
|
349
489
|
examples: ["pm --help", `pm ${commandName ?? "<command>"} --help`],
|
|
490
|
+
recovery: buildCommanderRecoveryPayload(context),
|
|
350
491
|
});
|
|
351
492
|
}
|
|
352
493
|
export function formatPmCliErrorForDisplay(rawMessage, context) {
|