@postplus/cli 0.1.21 → 0.1.23

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.
@@ -1,6 +1,6 @@
1
1
  import { refreshRemoteAuthSession } from './auth-session.js';
2
2
  import { clearAuthState, generateAuthStatusReport } from './auth.js';
3
- import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, } from './client-compatibility.js';
3
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, } from './client-compatibility.js';
4
4
  import { requireHostedBaseUrl } from './hosted-release.js';
5
5
  import { resolveCliSessionTokenState } from './local-state.js';
6
6
  export async function refreshRemoteAuth() {
@@ -47,9 +47,9 @@ export async function revokeRemoteAuth() {
47
47
  });
48
48
  const payload = (await response.json());
49
49
  if (!response.ok) {
50
- if ('code' in payload &&
51
- payload.code === 'postplus_client_upgrade_required') {
52
- throw new Error(formatPostPlusClientUpgradeError(payload));
50
+ const compatibilityError = formatPostPlusCompatibilityError(payload);
51
+ if (compatibilityError) {
52
+ throw new Error(compatibilityError);
53
53
  }
54
54
  throw new Error('error' in payload && typeof payload.error === 'string'
55
55
  ? payload.error
@@ -1,4 +1,5 @@
1
- import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
1
+ import { execFileSync } from 'node:child_process';
2
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
2
3
  import { requireHostedBaseUrl } from './hosted-release.js';
3
4
  import { setLocalSession } from './local-state.js';
4
5
  export const CLI_AUTH_LOGIN_TIMEOUT_MS = 30 * 60 * 1000;
@@ -16,6 +17,10 @@ export async function loginWithCloudHandoff() {
16
17
  'Waiting for browser sign-in...',
17
18
  '',
18
19
  ].join('\n'));
20
+ const didOpen = openCloudAuthVerificationUrlIfConfigured(started.verificationUrl);
21
+ if (didOpen) {
22
+ process.stdout.write('Browser opened for sign-in.\n\n');
23
+ }
19
24
  const handoffPayload = await waitForCloudAuthLogin({
20
25
  apiBaseUrl: baseUrl,
21
26
  expiresAt: started.expiresAt,
@@ -63,6 +68,16 @@ export async function startCloudAuthLogin(apiBaseUrl) {
63
68
  }
64
69
  return payload;
65
70
  }
71
+ export function openCloudAuthVerificationUrlIfConfigured(verificationUrl) {
72
+ const command = process.env.POSTPLUS_CLI_AUTH_OPEN_URL_COMMAND?.trim();
73
+ if (!command) {
74
+ return false;
75
+ }
76
+ execFileSync(command, [verificationUrl], {
77
+ stdio: 'ignore',
78
+ });
79
+ return true;
80
+ }
66
81
  async function waitForCloudAuthLogin(input) {
67
82
  const expiresAtMs = Date.parse(input.expiresAt);
68
83
  const deadlineMs = Number.isFinite(expiresAtMs)
@@ -130,8 +145,9 @@ export function formatCliSessionAuthError(payload) {
130
145
  'Once the environment is ready, the CLI will automatically obtain and store its session.',
131
146
  ].join(' ');
132
147
  }
133
- if (payload.code === 'postplus_client_upgrade_required') {
134
- return formatPostPlusClientUpgradeError(payload);
148
+ const compatibilityError = formatPostPlusCompatibilityError(payload);
149
+ if (compatibilityError) {
150
+ return compatibilityError;
135
151
  }
136
152
  if (typeof payload.error === 'string' && payload.error.trim().length > 0) {
137
153
  return payload.error;
@@ -158,8 +174,9 @@ function isCliAuthLoginPendingPayload(payload) {
158
174
  return 'status' in payload && payload.status === 'pending';
159
175
  }
160
176
  function formatRemoteAuthLoginError(payload) {
161
- if ('code' in payload && payload.code === 'postplus_client_upgrade_required') {
162
- return formatPostPlusClientUpgradeError(payload);
177
+ const compatibilityError = formatPostPlusCompatibilityError(payload);
178
+ if (compatibilityError) {
179
+ return compatibilityError;
163
180
  }
164
181
  return 'error' in payload &&
165
182
  typeof payload.error === 'string' &&
@@ -1,6 +1,6 @@
1
- import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
1
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, writeCurrentCliVersionToLocalConfig, } from './client-compatibility.js';
2
2
  import { requireHostedBaseUrl } from './hosted-release.js';
3
- import { resolveCliSessionTokenState, setLocalSession, } from './local-state.js';
3
+ import { resolveCliSessionTokenState, setLocalSession } from './local-state.js';
4
4
  export async function resolveFreshRemoteAuth(options = {}) {
5
5
  const [apiBaseUrl, cliSessionTokenState] = await Promise.all([
6
6
  requireHostedBaseUrl(),
@@ -53,9 +53,9 @@ export async function refreshRemoteAuthSession(input) {
53
53
  });
54
54
  const payload = (await response.json());
55
55
  if (!response.ok) {
56
- if ('code' in payload &&
57
- payload.code === 'postplus_client_upgrade_required') {
58
- throw new Error(formatPostPlusClientUpgradeError(payload));
56
+ const compatibilityError = formatPostPlusCompatibilityError(payload);
57
+ if (compatibilityError) {
58
+ throw new Error(compatibilityError);
59
59
  }
60
60
  throw new Error('error' in payload && typeof payload.error === 'string'
61
61
  ? payload.error
@@ -83,7 +83,8 @@ function isRemoteAuthRefreshSuccessPayload(payload) {
83
83
  payload !== null &&
84
84
  typeof payload.cliSessionToken ===
85
85
  'string' &&
86
- payload.cliSessionToken.trim().length > 0 &&
86
+ payload.cliSessionToken.trim().length >
87
+ 0 &&
87
88
  typeof payload.accountId === 'string' &&
88
89
  typeof payload.userId === 'string');
89
90
  }
@@ -1,5 +1,5 @@
1
1
  import { resolveFreshRemoteAuth } from './auth-session.js';
2
- import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, } from './client-compatibility.js';
2
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, } from './client-compatibility.js';
3
3
  export async function validateRemoteAuth() {
4
4
  let auth = await resolveFreshRemoteAuth();
5
5
  let response = await fetchWhoami(auth);
@@ -11,9 +11,9 @@ export async function validateRemoteAuth() {
11
11
  }
12
12
  const payload = (await response.json());
13
13
  if (!response.ok) {
14
- if ('code' in payload &&
15
- payload.code === 'postplus_client_upgrade_required') {
16
- throw new Error(formatPostPlusClientUpgradeError(payload));
14
+ const compatibilityError = formatPostPlusCompatibilityError(payload);
15
+ if (compatibilityError) {
16
+ throw new Error(compatibilityError);
17
17
  }
18
18
  throw new Error('error' in payload && typeof payload.error === 'string'
19
19
  ? payload.error
@@ -0,0 +1,100 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { readLocalConfig, updateLocalConfig } from './local-state.js';
3
+ export const POSTPLUS_CLIENT_CONTRACT_VERSION = 1;
4
+ export const POSTPLUS_CLIENT_RUNTIME = 'postplus-cli';
5
+ export const POSTPLUS_CLIENT_COMPATIBILITY_HEADERS = {
6
+ cliVersion: 'x-postplus-cli-version',
7
+ contractVersion: 'x-postplus-client-contract-version',
8
+ runtime: 'x-postplus-client-runtime',
9
+ skillsReleaseId: 'x-postplus-skills-release-id',
10
+ skillName: 'x-postplus-skill-name',
11
+ };
12
+ export async function buildPostPlusClientCompatibilityHeaders(input = {}) {
13
+ const [cliVersion, config] = await Promise.all([
14
+ readCurrentCliVersion(),
15
+ readLocalConfig(),
16
+ ]);
17
+ const headers = {
18
+ [POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.cliVersion]: cliVersion,
19
+ [POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.contractVersion]: String(POSTPLUS_CLIENT_CONTRACT_VERSION),
20
+ [POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.runtime]: POSTPLUS_CLIENT_RUNTIME,
21
+ };
22
+ const skillsReleaseId = config?.managedSkills?.releaseId?.trim();
23
+ const skillName = input.skillName?.trim();
24
+ if (skillsReleaseId) {
25
+ headers[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.skillsReleaseId] =
26
+ skillsReleaseId;
27
+ }
28
+ if (skillName) {
29
+ headers[POSTPLUS_CLIENT_COMPATIBILITY_HEADERS.skillName] = skillName;
30
+ }
31
+ return headers;
32
+ }
33
+ export async function writeCurrentCliVersionToLocalConfig() {
34
+ const cliVersion = await readCurrentCliVersion();
35
+ await updateLocalConfig((current) => ({
36
+ ...(current ?? {}),
37
+ cliVersion,
38
+ }));
39
+ }
40
+ export async function readCurrentCliVersion() {
41
+ const packageJsonPath = new URL('../package.json', import.meta.url);
42
+ const raw = await readFile(packageJsonPath, 'utf8');
43
+ const parsed = JSON.parse(raw);
44
+ if (typeof parsed.version !== 'string' || !parsed.version.trim()) {
45
+ throw new Error('Could not read the current PostPlus CLI version.');
46
+ }
47
+ return parsed.version.trim();
48
+ }
49
+ export function formatPostPlusClientUpgradeError(payload) {
50
+ const record = payload && typeof payload === 'object' && !Array.isArray(payload)
51
+ ? payload
52
+ : {};
53
+ const cliCommand = record.compatibility?.upgrade?.cli?.command ??
54
+ 'npm install -g @postplus/cli';
55
+ const skillsCommand = record.compatibility?.upgrade?.skills?.command ?? 'postplus update';
56
+ const restart = record.compatibility?.upgrade?.restartAgentSession
57
+ ? ' Then restart your agent session.'
58
+ : '';
59
+ return [
60
+ typeof record.error === 'string' && record.error.trim().length > 0
61
+ ? record.error.trim()
62
+ : 'Your PostPlus CLI or PostPlus skills are out of date.',
63
+ `Update CLI: ${cliCommand}.`,
64
+ `Update skills: ${skillsCommand}.`,
65
+ restart.trim(),
66
+ ]
67
+ .filter(Boolean)
68
+ .join(' ');
69
+ }
70
+ export function formatPostPlusCloudReleaseInProgressError(payload) {
71
+ const record = payload && typeof payload === 'object' && !Array.isArray(payload)
72
+ ? payload
73
+ : {};
74
+ return typeof record.error === 'string' && record.error.trim().length > 0
75
+ ? record.error.trim()
76
+ : 'PostPlus Cloud is updating. Please retry in about one minute.';
77
+ }
78
+ export function formatPostPlusCompatibilityError(payload) {
79
+ if (isPostPlusClientUpgradePayload(payload)) {
80
+ return formatPostPlusClientUpgradeError(payload);
81
+ }
82
+ if (isPostPlusCloudReleaseInProgressPayload(payload)) {
83
+ return formatPostPlusCloudReleaseInProgressError(payload);
84
+ }
85
+ return null;
86
+ }
87
+ export function isPostPlusClientUpgradePayload(payload) {
88
+ return (payload &&
89
+ typeof payload === 'object' &&
90
+ !Array.isArray(payload) &&
91
+ 'code' in payload &&
92
+ payload.code === 'postplus_client_upgrade_required');
93
+ }
94
+ export function isPostPlusCloudReleaseInProgressPayload(payload) {
95
+ return (payload &&
96
+ typeof payload === 'object' &&
97
+ !Array.isArray(payload) &&
98
+ 'code' in payload &&
99
+ payload.code === 'postplus_cli_cloud_release_in_progress');
100
+ }
package/build/doctor.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { resolveFreshRemoteAuth, } from './auth-session.js';
2
- import { buildPostPlusClientCompatibilityHeaders, formatPostPlusClientUpgradeError, } from './client-compatibility.js';
2
+ import { buildPostPlusClientCompatibilityHeaders, formatPostPlusCompatibilityError, } from './client-compatibility.js';
3
3
  import { resolveHostedBaseUrl } from './hosted-release.js';
4
4
  import { formatLocalDependencyReport, generateLocalDependencyReport, } from './local-dependencies.js';
5
5
  function createPass(id, label, detail, severity = 'required') {
@@ -185,8 +185,9 @@ function readReadinessCheckFailureLabel(value) {
185
185
  : 'unknown check';
186
186
  }
187
187
  function readErrorMessage(payload, fallback) {
188
- if (payload.code === 'postplus_client_upgrade_required') {
189
- return formatPostPlusClientUpgradeError(payload);
188
+ const compatibilityError = formatPostPlusCompatibilityError(payload);
189
+ if (compatibilityError) {
190
+ return compatibilityError;
190
191
  }
191
192
  return typeof payload.error === 'string' && payload.error.trim().length > 0
192
193
  ? payload.error
package/build/index.js CHANGED
@@ -3,12 +3,12 @@ import { formatAuthRefreshReport, refreshRemoteAuth, revokeRemoteAuthAndReport,
3
3
  import { loginWithCloudHandoff } from './auth-login.js';
4
4
  import { formatAuthValidateReport, validateRemoteAuth, } from './auth-validate.js';
5
5
  import { clearAuthState, formatAuthStatusReport, generateAuthStatusReport, } from './auth.js';
6
+ import { readCurrentCliVersion } from './client-compatibility.js';
6
7
  import { formatDoctorReport, generateDoctorReport } from './doctor.js';
7
8
  import { assertConfigFilePermissions } from './local-state.js';
8
9
  import { POSTPLUS_SKILLS_INSTALL_COMMAND, loadPublicSkillCatalog, } from './skill-catalog.js';
9
10
  import { runPostPlusSkillUninstall, runPostPlusSkillUpdate, } from './skill-management.js';
10
11
  import { formatStatusReport, generateStatusReport } from './status.js';
11
- import { readCurrentCliVersion, } from './client-compatibility.js';
12
12
  import { refreshUpdateCheckCache } from './update-check.js';
13
13
  function printAuthHelp() {
14
14
  process.stdout.write(`PostPlus CLI — auth commands
@@ -9,7 +9,7 @@ export async function generateLocalDependencyReport(options = {}) {
9
9
  const checks = await Promise.all(requirements.map(({ dependency, skillIds }) => checkLocalDependency(dependency, skillIds, runDependencyCheck)));
10
10
  return {
11
11
  ok: checks.every((check) => check.ok),
12
- revision: catalog.revision,
12
+ releaseId: catalog.releaseId,
13
13
  source: catalog.source,
14
14
  requiredCount: checks.length,
15
15
  checks,
@@ -17,11 +17,11 @@ export async function generateLocalDependencyReport(options = {}) {
17
17
  }
18
18
  export function formatLocalDependencyReport(report) {
19
19
  if (report.requiredCount === 0) {
20
- return `No local runtime dependencies are required by released PostPlus skills (${report.revision}).`;
20
+ return `No local runtime dependencies are required by released PostPlus skills (${report.releaseId}).`;
21
21
  }
22
22
  const missing = report.checks.filter((check) => !check.ok);
23
23
  if (missing.length === 0) {
24
- return `Ready (${report.requiredCount} local dependencies present; catalog ${report.revision})`;
24
+ return `Ready (${report.requiredCount} local dependencies present; catalog ${report.releaseId})`;
25
25
  }
26
26
  return `Missing ${missing.length}/${report.requiredCount}: ${missing
27
27
  .map((check) => `${check.dependency} for ${formatSkillList(check.skillIds)}`)
@@ -108,15 +108,15 @@ export async function readManagedSkillBaseline() {
108
108
  const config = await readLocalConfig();
109
109
  const managedSkills = config?.managedSkills;
110
110
  if (!managedSkills ||
111
- typeof managedSkills.revision !== 'string' ||
111
+ typeof managedSkills.releaseId !== 'string' ||
112
112
  !Array.isArray(managedSkills.skillNames)) {
113
113
  return {
114
- revision: null,
114
+ releaseId: null,
115
115
  skillNames: [],
116
116
  };
117
117
  }
118
118
  return {
119
- revision: managedSkills.revision,
119
+ releaseId: managedSkills.releaseId,
120
120
  skillNames: normalizeSkillNames(managedSkills.skillNames),
121
121
  };
122
122
  }
@@ -124,7 +124,7 @@ export async function writeManagedSkillBaseline(input) {
124
124
  return updateLocalConfig((current) => ({
125
125
  ...(current ?? {}),
126
126
  managedSkills: {
127
- revision: input.revision,
127
+ releaseId: input.releaseId,
128
128
  skillNames: normalizeSkillNames(input.skillNames),
129
129
  updatedAt: new Date().toISOString(),
130
130
  },
@@ -1,4 +1,6 @@
1
1
  export const POSTPLUS_SKILLS_REPO = 'PostPlusAI/postplus-skills';
2
+ export const POSTPLUS_SKILLS_SOURCE_ENV = 'POSTPLUS_SKILLS_SOURCE';
3
+ export const POSTPLUS_SKILLS_CATALOG_URL_ENV = 'POSTPLUS_SKILLS_CATALOG_URL';
2
4
  export const POSTPLUS_SKILLS_AGENT_TARGETS = [
3
5
  'claude-code',
4
6
  'codex',
@@ -9,12 +11,14 @@ export const POSTPLUS_SKILLS_AGENT_TARGETS = [
9
11
  'trae-cn',
10
12
  ];
11
13
  const POSTPLUS_SKILLS_AGENT_ARGS = POSTPLUS_SKILLS_AGENT_TARGETS.join(' ');
12
- export const POSTPLUS_SKILLS_INSTALL_COMMAND = `npx -y skills add PostPlusAI/postplus-skills --global --full-depth --skill '*' --agent ${POSTPLUS_SKILLS_AGENT_ARGS} --yes`;
13
- export const POSTPLUS_SKILLS_LIST_COMMAND = 'npx -y skills add PostPlusAI/postplus-skills --list --full-depth';
14
+ export const POSTPLUS_SKILLS_INSTALL_COMMAND = formatPostPlusSkillsInstallCommand();
15
+ export const POSTPLUS_SKILLS_LIST_COMMAND = formatPostPlusSkillsListCommand();
14
16
  const POSTPLUS_SKILLS_INDEX_URL = 'https://raw.githubusercontent.com/PostPlusAI/postplus-skills/main/skills/INDEX.md';
15
17
  const POSTPLUS_SKILLS_CATALOG_URL = 'https://raw.githubusercontent.com/PostPlusAI/postplus-skills/main/skills/catalog.json';
16
- export async function loadPublicSkillCatalog(fetchFn = fetch) {
17
- const response = await fetchFn(POSTPLUS_SKILLS_CATALOG_URL, {
18
+ export async function loadPublicSkillCatalog(fetchFn = fetch, env = process.env) {
19
+ const catalogUrl = resolvePostPlusSkillsCatalogUrl(env);
20
+ const skillsSource = resolvePostPlusSkillsSource(env);
21
+ const response = await fetchFn(catalogUrl, {
18
22
  headers: {
19
23
  accept: 'application/json',
20
24
  },
@@ -24,16 +28,29 @@ export async function loadPublicSkillCatalog(fetchFn = fetch) {
24
28
  throw new Error(`Failed to load PostPlus skill catalog (${response.status}): ${response.statusText}`);
25
29
  }
26
30
  const raw = await response.text();
27
- const payload = parseJsonResponse(raw, POSTPLUS_SKILLS_CATALOG_URL);
31
+ const payload = parseJsonResponse(raw, catalogUrl);
28
32
  const catalog = parsePublicSkillCatalog(payload);
29
33
  return {
30
34
  ...catalog,
31
- catalogUrl: POSTPLUS_SKILLS_CATALOG_URL,
35
+ catalogUrl,
32
36
  indexUrl: POSTPLUS_SKILLS_INDEX_URL,
33
- installCommand: POSTPLUS_SKILLS_INSTALL_COMMAND,
34
- listCommand: POSTPLUS_SKILLS_LIST_COMMAND,
37
+ installCommand: formatPostPlusSkillsInstallCommand(skillsSource),
38
+ listCommand: formatPostPlusSkillsListCommand(skillsSource),
39
+ source: skillsSource,
35
40
  };
36
41
  }
42
+ export function resolvePostPlusSkillsSource(env = process.env) {
43
+ return env[POSTPLUS_SKILLS_SOURCE_ENV]?.trim() || POSTPLUS_SKILLS_REPO;
44
+ }
45
+ export function resolvePostPlusSkillsCatalogUrl(env = process.env) {
46
+ return env[POSTPLUS_SKILLS_CATALOG_URL_ENV]?.trim() || POSTPLUS_SKILLS_CATALOG_URL;
47
+ }
48
+ export function formatPostPlusSkillsInstallCommand(source = POSTPLUS_SKILLS_REPO) {
49
+ return `npx -y skills add ${source} --global --full-depth --skill '*' --agent ${POSTPLUS_SKILLS_AGENT_ARGS} --yes`;
50
+ }
51
+ export function formatPostPlusSkillsListCommand(source = POSTPLUS_SKILLS_REPO) {
52
+ return `npx -y skills add ${source} --list --full-depth`;
53
+ }
37
54
  function parseJsonResponse(raw, url) {
38
55
  try {
39
56
  return JSON.parse(raw);
@@ -53,15 +70,15 @@ function parsePublicSkillCatalog(payload) {
53
70
  throw new Error('PostPlus public skill catalog is invalid.');
54
71
  }
55
72
  const record = payload;
56
- const revision = typeof record.revision === 'string' && record.revision.trim()
57
- ? record.revision.trim()
73
+ const releaseId = typeof record.releaseId === 'string' && record.releaseId.trim()
74
+ ? record.releaseId.trim()
58
75
  : null;
59
76
  const source = typeof record.source === 'string' && record.source.trim()
60
77
  ? record.source.trim()
61
78
  : null;
62
79
  if (record.schemaVersion !== 1 ||
63
80
  source !== POSTPLUS_SKILLS_REPO ||
64
- !revision) {
81
+ !releaseId) {
65
82
  throw new Error('PostPlus public skill catalog metadata is invalid.');
66
83
  }
67
84
  if (!Array.isArray(record.skills)) {
@@ -105,7 +122,7 @@ function parsePublicSkillCatalog(payload) {
105
122
  throw new Error('PostPlus public skill catalog is invalid: no released skills were found.');
106
123
  }
107
124
  return {
108
- revision,
125
+ releaseId,
109
126
  skills,
110
127
  source,
111
128
  };
@@ -1,7 +1,7 @@
1
- import { runCommand, runInteractiveCommand } from './command-runner.js';
2
1
  import { writeCurrentCliVersionToLocalConfig } from './client-compatibility.js';
2
+ import { runCommand, runInteractiveCommand } from './command-runner.js';
3
3
  import { clearManagedSkillBaseline, readManagedSkillBaseline, writeManagedSkillBaseline, } from './local-state.js';
4
- import { POSTPLUS_SKILLS_AGENT_TARGETS, POSTPLUS_SKILLS_INSTALL_COMMAND, POSTPLUS_SKILLS_REPO, loadPublicSkillCatalog, } from './skill-catalog.js';
4
+ import { POSTPLUS_SKILLS_AGENT_TARGETS, formatPostPlusSkillsInstallCommand, resolvePostPlusSkillsSource, loadPublicSkillCatalog, } from './skill-catalog.js';
5
5
  const NPX_SKILLS = ['-y', 'skills'];
6
6
  export async function runPostPlusSkillUpdate(dependencies = {
7
7
  runInteractiveCommand,
@@ -24,7 +24,7 @@ export async function runPostPlusSkillUpdate(dependencies = {
24
24
  }
25
25
  }
26
26
  await writeManagedSkillBaseline({
27
- revision: catalog.revision,
27
+ releaseId: catalog.releaseId,
28
28
  skillNames,
29
29
  });
30
30
  await writeCurrentCliVersionToLocalConfig();
@@ -66,14 +66,14 @@ export async function generateSkillInstallStatusReport(dependencies = {
66
66
  return {
67
67
  ok: missingSkills.length === 0,
68
68
  error: null,
69
- installCommand: POSTPLUS_SKILLS_INSTALL_COMMAND,
69
+ installCommand: formatPostPlusSkillsInstallCommand(catalog.source),
70
70
  installedCount: installedNames.size,
71
- managedRevision: baseline.revision,
71
+ managedSkillsReleaseId: baseline.releaseId,
72
72
  missingSkills,
73
73
  requiredCount: requiredSkills.size,
74
74
  retiredManagedSkills,
75
75
  scopes,
76
- source: POSTPLUS_SKILLS_REPO,
76
+ source: catalog.source,
77
77
  updateCommand: formatPostPlusSkillUpdateCommand(),
78
78
  uninstallCommand: formatPostPlusSkillUninstallCommand(),
79
79
  };
@@ -84,14 +84,14 @@ export async function generateSkillInstallStatusReport(dependencies = {
84
84
  error: error instanceof Error
85
85
  ? error.message
86
86
  : 'Failed to inspect installed PostPlus skills.',
87
- installCommand: POSTPLUS_SKILLS_INSTALL_COMMAND,
87
+ installCommand: formatPostPlusSkillsInstallCommand(catalog.source),
88
88
  installedCount: 0,
89
- managedRevision: baseline.revision,
89
+ managedSkillsReleaseId: baseline.releaseId,
90
90
  missingSkills: [...requiredSkills],
91
91
  requiredCount: requiredSkills.size,
92
92
  retiredManagedSkills,
93
93
  scopes: [],
94
- source: POSTPLUS_SKILLS_REPO,
94
+ source: catalog.source,
95
95
  updateCommand: formatPostPlusSkillUpdateCommand(),
96
96
  uninstallCommand: formatPostPlusSkillUninstallCommand(),
97
97
  };
@@ -109,7 +109,7 @@ export function formatSkillInstallStatusReport(report) {
109
109
  lines.push(`[FAIL] Installed released skills: ${report.installedCount}/${report.requiredCount}`);
110
110
  }
111
111
  lines.push(` Source: ${report.source}`);
112
- lines.push(` Managed baseline: ${report.managedRevision ?? 'none'}`);
112
+ lines.push(` Managed baseline: ${report.managedSkillsReleaseId ?? 'none'}`);
113
113
  lines.push(` Scope: ${report.scopes.length > 0 ? report.scopes.join(', ') : 'none detected'}`);
114
114
  if (report.retiredManagedSkills.length > 0) {
115
115
  lines.push(` Retired managed skills: ${formatSkillList(report.retiredManagedSkills, 8)}`, ` Cleanup: ${report.updateCommand}`);
@@ -123,7 +123,22 @@ export function formatSkillInstallStatusReport(report) {
123
123
  return lines.join('\n');
124
124
  }
125
125
  export function buildPostPlusSkillUpdateArgs(skillNames) {
126
- return [...NPX_SKILLS, 'update', ...skillNames, '--global', '--yes'];
126
+ if (skillNames.length === 0) {
127
+ throw new Error('PostPlus public skill catalog has no released skills.');
128
+ }
129
+ const skillsSource = resolvePostPlusSkillsSource();
130
+ return [
131
+ ...NPX_SKILLS,
132
+ 'add',
133
+ skillsSource,
134
+ '--global',
135
+ '--full-depth',
136
+ '--skill',
137
+ '*',
138
+ '--agent',
139
+ ...POSTPLUS_SKILLS_AGENT_TARGETS,
140
+ '--yes',
141
+ ];
127
142
  }
128
143
  export function buildPostPlusSkillUninstallArgs(skillNames) {
129
144
  return [
@@ -20,14 +20,14 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
20
20
  return buildUpdateReport({
21
21
  cache,
22
22
  currentVersion,
23
- currentSkillRevision: managedSkillBaseline.revision,
23
+ currentSkillsReleaseId: managedSkillBaseline.releaseId,
24
24
  source: 'cache',
25
25
  });
26
26
  }
27
27
  try {
28
- const [latestCliVersion, latestSkillRevision] = await Promise.all([
28
+ const [latestCliVersion, latestSkillsReleaseId] = await Promise.all([
29
29
  fetchLatestCliVersion(dependencies.fetchFn),
30
- fetchLatestSkillRevision(dependencies.fetchFn),
30
+ fetchLatestSkillReleaseId(dependencies.fetchFn),
31
31
  ]);
32
32
  const nextCache = {
33
33
  checkedAt: new Date().toISOString(),
@@ -36,14 +36,14 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
36
36
  latestVersion: latestCliVersion,
37
37
  },
38
38
  skills: {
39
- latestRevision: latestSkillRevision,
39
+ latestReleaseId: latestSkillsReleaseId,
40
40
  },
41
41
  };
42
42
  await writeUpdateCheckCache(nextCache);
43
43
  return buildUpdateReport({
44
44
  cache: nextCache,
45
45
  currentVersion,
46
- currentSkillRevision: managedSkillBaseline.revision,
46
+ currentSkillsReleaseId: managedSkillBaseline.releaseId,
47
47
  source: 'remote',
48
48
  });
49
49
  }
@@ -54,7 +54,7 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
54
54
  ...buildUpdateReport({
55
55
  cache,
56
56
  currentVersion,
57
- currentSkillRevision: managedSkillBaseline.revision,
57
+ currentSkillsReleaseId: managedSkillBaseline.releaseId,
58
58
  source: 'cache',
59
59
  }),
60
60
  warning,
@@ -71,8 +71,8 @@ export async function generateUpdateStatusReport(input = {}, dependencies = {
71
71
  updateCommand: 'npm install -g @postplus/cli',
72
72
  },
73
73
  skills: {
74
- currentRevision: managedSkillBaseline.revision,
75
- latestRevision: null,
74
+ currentReleaseId: managedSkillBaseline.releaseId,
75
+ latestReleaseId: null,
76
76
  updateAvailable: false,
77
77
  updateCommand: 'postplus update',
78
78
  },
@@ -93,8 +93,8 @@ export function formatUpdateStatusReport(report) {
93
93
  lines.push(` Update: ${report.cli.updateCommand}`);
94
94
  }
95
95
  const skillMarker = report.skills.updateAvailable ? '[WARN]' : '[PASS]';
96
- lines.push(`${skillMarker} Skills: ${report.skills.latestRevision
97
- ? `release ${shortRevision(report.skills.latestRevision)}`
96
+ lines.push(`${skillMarker} Skills: ${report.skills.latestReleaseId
97
+ ? `release ${shortReleaseId(report.skills.latestReleaseId)}`
98
98
  : 'release unknown'}`);
99
99
  if (report.skills.updateAvailable) {
100
100
  lines.push(` Update: ${report.skills.updateCommand}`);
@@ -118,9 +118,9 @@ function buildUpdateReport(input) {
118
118
  updateCommand: 'npm install -g @postplus/cli',
119
119
  },
120
120
  skills: {
121
- currentRevision: input.currentSkillRevision,
122
- latestRevision: input.cache.skills.latestRevision,
123
- updateAvailable: input.cache.skills.latestRevision !== input.currentSkillRevision,
121
+ currentReleaseId: input.currentSkillsReleaseId,
122
+ latestReleaseId: input.cache.skills.latestReleaseId,
123
+ updateAvailable: input.cache.skills.latestReleaseId !== input.currentSkillsReleaseId,
124
124
  updateCommand: 'postplus update',
125
125
  },
126
126
  warning: null,
@@ -143,12 +143,12 @@ async function fetchLatestCliVersion(fetchFn) {
143
143
  }
144
144
  return payload.version.trim();
145
145
  }
146
- async function fetchLatestSkillRevision(fetchFn) {
146
+ async function fetchLatestSkillReleaseId(fetchFn) {
147
147
  try {
148
- return (await loadPublicSkillCatalog(fetchFn)).revision;
148
+ return (await loadPublicSkillCatalog(fetchFn)).releaseId;
149
149
  }
150
150
  catch (error) {
151
- throw new Error(`Failed to check latest ${POSTPLUS_SKILLS_REPO} revision: ${error instanceof Error ? error.message : String(error)}`);
151
+ throw new Error(`Failed to check latest ${POSTPLUS_SKILLS_REPO} releaseId: ${error instanceof Error ? error.message : String(error)}`);
152
152
  }
153
153
  }
154
154
  async function readUpdateCheckCache() {
@@ -158,7 +158,7 @@ async function readUpdateCheckCache() {
158
158
  if (typeof parsed.checkedAt !== 'string' ||
159
159
  typeof parsed.cli?.currentVersion !== 'string' ||
160
160
  typeof parsed.cli?.latestVersion !== 'string' ||
161
- typeof parsed.skills?.latestRevision !== 'string') {
161
+ typeof parsed.skills?.latestReleaseId !== 'string') {
162
162
  return null;
163
163
  }
164
164
  return parsed;
@@ -202,6 +202,6 @@ function parseVersion(value) {
202
202
  .map((part) => Number.parseInt(part, 10))
203
203
  .map((part) => (Number.isFinite(part) ? part : 0));
204
204
  }
205
- function shortRevision(revision) {
206
- return revision.length > 12 ? revision.slice(0, 12) : revision;
205
+ function shortReleaseId(releaseId) {
206
+ return releaseId.length > 12 ? releaseId.slice(0, 12) : releaseId;
207
207
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postplus/cli",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017",
5
5
  "type": "module",
6
6
  "description": "PostPlus CLI for PostPlus Cloud auth, status, and diagnostics.",
@@ -11,6 +11,7 @@
11
11
  "build/auth-session.js",
12
12
  "build/auth-validate.js",
13
13
  "build/auth.js",
14
+ "build/client-compatibility.js",
14
15
  "build/command-runner.js",
15
16
  "build/doctor.js",
16
17
  "build/hosted-release.js",