openplanr 2.2640.1 → 2.2640.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cli/commands/planning-artifacts.js +1 -1
  3. package/dist/cli/commands/planning-artifacts.js.map +1 -1
  4. package/dist/cli/commands/runtime-output.d.ts +11 -0
  5. package/dist/cli/commands/runtime-output.d.ts.map +1 -0
  6. package/dist/cli/commands/runtime-output.js +26 -0
  7. package/dist/cli/commands/runtime-output.js.map +1 -0
  8. package/dist/cli/commands/runtime.d.ts.map +1 -1
  9. package/dist/cli/commands/runtime.js +30 -17
  10. package/dist/cli/commands/runtime.js.map +1 -1
  11. package/dist/cli/commands/setup.d.ts.map +1 -1
  12. package/dist/cli/commands/setup.js +2 -3
  13. package/dist/cli/commands/setup.js.map +1 -1
  14. package/dist/cli/commands/upgrade.d.ts +1 -1
  15. package/dist/cli/commands/upgrade.d.ts.map +1 -1
  16. package/dist/cli/commands/upgrade.js +23 -54
  17. package/dist/cli/commands/upgrade.js.map +1 -1
  18. package/dist/dashboard/.vite/manifest.json +1 -1
  19. package/dist/dashboard/assets/{index-9DLb9EO5.js → index-CaX9EJp4.js} +2 -2
  20. package/dist/dashboard/assets/{index-9DLb9EO5.js.map → index-CaX9EJp4.js.map} +1 -1
  21. package/dist/dashboard/dashboard-manifest.json +13 -13
  22. package/dist/dashboard/index.html +1 -1
  23. package/dist/services/artifact-service.d.ts.map +1 -1
  24. package/dist/services/artifact-service.js +4 -1
  25. package/dist/services/artifact-service.js.map +1 -1
  26. package/dist/services/runtime-change-summary.d.ts +33 -0
  27. package/dist/services/runtime-change-summary.d.ts.map +1 -0
  28. package/dist/services/runtime-change-summary.js +88 -0
  29. package/dist/services/runtime-change-summary.js.map +1 -0
  30. package/dist/services/runtime-manager/inventory.d.ts.map +1 -1
  31. package/dist/services/runtime-manager/inventory.js +8 -1
  32. package/dist/services/runtime-manager/inventory.js.map +1 -1
  33. package/dist/services/runtime-manager-service.d.ts +6 -0
  34. package/dist/services/runtime-manager-service.d.ts.map +1 -1
  35. package/dist/services/runtime-manager-service.js +28 -0
  36. package/dist/services/runtime-manager-service.js.map +1 -1
  37. package/dist/services/upgrade-offer-service.d.ts.map +1 -1
  38. package/dist/services/upgrade-offer-service.js +6 -30
  39. package/dist/services/upgrade-offer-service.js.map +1 -1
  40. package/dist/services/upgrade-report.d.ts +13 -0
  41. package/dist/services/upgrade-report.d.ts.map +1 -0
  42. package/dist/services/upgrade-report.js +90 -0
  43. package/dist/services/upgrade-report.js.map +1 -0
  44. package/dist/services/upgrade-service.d.ts +48 -29
  45. package/dist/services/upgrade-service.d.ts.map +1 -1
  46. package/dist/services/upgrade-service.js +236 -48
  47. package/dist/services/upgrade-service.js.map +1 -1
  48. package/dist/templates/backlog/backlog-item.md.hbs +0 -3
  49. package/docs/CLI.md +7 -2
  50. package/docs/CROSS_RUNTIME_SETUP.md +2 -2
  51. package/lib/host-packages/adapter-registry.json +5 -5
  52. package/lib/host-packages/capability-map.json +1 -1
  53. package/lib/host-packages/claude/.claude-plugin/marketplace.json +2 -2
  54. package/lib/host-packages/claude/openplanr/.claude-plugin/plugin.json +1 -1
  55. package/lib/host-packages/claude/openplanr/.openplanr-content.json +20 -20
  56. package/lib/host-packages/claude/openplanr/README.md +1 -1
  57. package/lib/host-packages/claude/openplanr/skills/design/scripts/design-artifact-shell.mjs +1 -1
  58. package/lib/host-packages/claude/openplanr/skills/design/scripts/design-document.mjs +1 -1
  59. package/lib/host-packages/claude/openplanr/skills/design/scripts/design-review-handoff.mjs +1 -1
  60. package/lib/host-packages/claude/openplanr/skills/design/scripts/runtime/packages/protocol/package.json +1 -1
  61. package/lib/host-packages/claude/openplanr/skills/design-loop/scripts/design-artifact-shell.mjs +1 -1
  62. package/lib/host-packages/claude/openplanr/skills/design-loop/scripts/design-document.mjs +1 -1
  63. package/lib/host-packages/claude/openplanr/skills/design-loop/scripts/design-review-handoff.mjs +1 -1
  64. package/lib/host-packages/claude/openplanr/skills/design-loop/scripts/runtime/packages/protocol/package.json +1 -1
  65. package/lib/host-packages/claude/openplanr/skills/design-review/scripts/design-artifact-shell.mjs +1 -1
  66. package/lib/host-packages/claude/openplanr/skills/design-review/scripts/design-document.mjs +1 -1
  67. package/lib/host-packages/claude/openplanr/skills/design-review/scripts/design-review-handoff.mjs +1 -1
  68. package/lib/host-packages/claude/openplanr/skills/design-review/scripts/runtime/packages/protocol/package.json +1 -1
  69. package/lib/host-packages/claude/openplanr/skills/doctor/references/upgrade.md +61 -63
  70. package/lib/host-packages/claude/openplanr/skills/plan/scripts/design-artifact-shell.mjs +1 -1
  71. package/lib/host-packages/claude/openplanr/skills/plan/scripts/design-document.mjs +1 -1
  72. package/lib/host-packages/claude/openplanr/skills/plan/scripts/design-review-handoff.mjs +1 -1
  73. package/lib/host-packages/claude/openplanr/skills/plan/scripts/runtime/packages/protocol/package.json +1 -1
  74. package/lib/host-packages/cursor/openplanr/.openplanr-content.json +19 -19
  75. package/lib/host-packages/cursor/openplanr/manifest.json +1 -1
  76. package/lib/host-packages/cursor/openplanr/rules/planr-design/scripts/design-artifact-shell.mjs +1 -1
  77. package/lib/host-packages/cursor/openplanr/rules/planr-design/scripts/design-document.mjs +1 -1
  78. package/lib/host-packages/cursor/openplanr/rules/planr-design/scripts/design-review-handoff.mjs +1 -1
  79. package/lib/host-packages/cursor/openplanr/rules/planr-design/scripts/runtime/packages/protocol/package.json +1 -1
  80. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/scripts/design-artifact-shell.mjs +1 -1
  81. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/scripts/design-document.mjs +1 -1
  82. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/scripts/design-review-handoff.mjs +1 -1
  83. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/scripts/runtime/packages/protocol/package.json +1 -1
  84. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/scripts/design-artifact-shell.mjs +1 -1
  85. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/scripts/design-document.mjs +1 -1
  86. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/scripts/design-review-handoff.mjs +1 -1
  87. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/scripts/runtime/packages/protocol/package.json +1 -1
  88. package/lib/host-packages/cursor/openplanr/rules/planr-doctor/references/upgrade.md +61 -63
  89. package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/design-artifact-shell.mjs +1 -1
  90. package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/design-document.mjs +1 -1
  91. package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/design-review-handoff.mjs +1 -1
  92. package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/runtime/packages/protocol/package.json +1 -1
  93. package/lib/host-packages/openai/.claude-plugin/marketplace.json +2 -2
  94. package/lib/host-packages/openai/openplanr/.codex-plugin/plugin.json +1 -1
  95. package/lib/host-packages/openai/openplanr/.openplanr-content.json +19 -19
  96. package/lib/host-packages/openai/openplanr/skills/design/scripts/design-artifact-shell.mjs +1 -1
  97. package/lib/host-packages/openai/openplanr/skills/design/scripts/design-document.mjs +1 -1
  98. package/lib/host-packages/openai/openplanr/skills/design/scripts/design-review-handoff.mjs +1 -1
  99. package/lib/host-packages/openai/openplanr/skills/design/scripts/runtime/packages/protocol/package.json +1 -1
  100. package/lib/host-packages/openai/openplanr/skills/design-loop/scripts/design-artifact-shell.mjs +1 -1
  101. package/lib/host-packages/openai/openplanr/skills/design-loop/scripts/design-document.mjs +1 -1
  102. package/lib/host-packages/openai/openplanr/skills/design-loop/scripts/design-review-handoff.mjs +1 -1
  103. package/lib/host-packages/openai/openplanr/skills/design-loop/scripts/runtime/packages/protocol/package.json +1 -1
  104. package/lib/host-packages/openai/openplanr/skills/design-review/scripts/design-artifact-shell.mjs +1 -1
  105. package/lib/host-packages/openai/openplanr/skills/design-review/scripts/design-document.mjs +1 -1
  106. package/lib/host-packages/openai/openplanr/skills/design-review/scripts/design-review-handoff.mjs +1 -1
  107. package/lib/host-packages/openai/openplanr/skills/design-review/scripts/runtime/packages/protocol/package.json +1 -1
  108. package/lib/host-packages/openai/openplanr/skills/doctor/references/upgrade.md +61 -63
  109. package/lib/host-packages/openai/openplanr/skills/plan/scripts/design-artifact-shell.mjs +1 -1
  110. package/lib/host-packages/openai/openplanr/skills/plan/scripts/design-document.mjs +1 -1
  111. package/lib/host-packages/openai/openplanr/skills/plan/scripts/design-review-handoff.mjs +1 -1
  112. package/lib/host-packages/openai/openplanr/skills/plan/scripts/runtime/packages/protocol/package.json +1 -1
  113. package/package.json +2 -2
@@ -0,0 +1,90 @@
1
+ import { display, logger } from '../utils/logger.js';
2
+ import { joinNames } from './runtime-change-summary.js';
3
+ const HIGHLIGHTS_PER_RELEASE = 5;
4
+ function releaseNotesUrl(version) {
5
+ return `https://github.com/openplanr/OpenPlanr/releases/tag/openplanr%40${version}`;
6
+ }
7
+ /** The first sentence of a changelog entry, or the whole entry when it has one sentence. */
8
+ export function firstSentence(text) {
9
+ return /^(.+?[.!?])\s+(?=[A-Z`"(*])/s.exec(text)?.[1] ?? text;
10
+ }
11
+ /** Word-wraps `text` to lines of at most `width` columns; a longer word gets its own line. */
12
+ export function wrapText(text, width) {
13
+ const lines = [];
14
+ let line = '';
15
+ for (const word of text.split(/\s+/).filter(Boolean)) {
16
+ if (line && line.length + 1 + word.length > width) {
17
+ lines.push(line);
18
+ line = word;
19
+ }
20
+ else {
21
+ line = line ? `${line} ${word}` : word;
22
+ }
23
+ }
24
+ if (line)
25
+ lines.push(line);
26
+ return lines;
27
+ }
28
+ function terminalWidth() {
29
+ return Math.min(process.stdout.columns || 80, 100);
30
+ }
31
+ /** Per release, up to five first sentences in `highlights` mode, or every entry in full. */
32
+ export function printReleaseNotes(sections, installedVersion, mode) {
33
+ if (sections.length === 0)
34
+ return;
35
+ display.blank();
36
+ display.heading("What's new");
37
+ const width = terminalWidth() - 6;
38
+ for (const section of sections) {
39
+ display.line(` ${section.version}`);
40
+ const entries = mode === 'full'
41
+ ? section.entries
42
+ : section.entries.slice(0, HIGHLIGHTS_PER_RELEASE).map(firstSentence);
43
+ for (const entry of entries) {
44
+ const [first, ...rest] = wrapText(entry, width);
45
+ display.line(` • ${first}`);
46
+ for (const line of rest)
47
+ display.line(` ${line}`);
48
+ }
49
+ const hidden = section.entries.length - entries.length;
50
+ if (hidden > 0)
51
+ display.line(` … and ${hidden} more`);
52
+ }
53
+ display.line(` Full notes: ${releaseNotesUrl(installedVersion)}`);
54
+ }
55
+ /** The commands that bring each installed coding agent up to date, then the restart. */
56
+ export function printNextSteps(steps, error) {
57
+ display.blank();
58
+ display.heading('Next');
59
+ if (error) {
60
+ logger.warn(`${error} Run \`planr upgrade status\` to see what else needs updating.`);
61
+ return;
62
+ }
63
+ if (steps.length === 0) {
64
+ logger.success('Your coding agents are up to date.');
65
+ return;
66
+ }
67
+ steps.forEach((step, index) => {
68
+ display.numbered(index + 1, step.command);
69
+ display.line(` ${step.host}: ${step.detail}`);
70
+ });
71
+ const hosts = [...new Set(steps.filter((step) => step.runtime).map((step) => step.host))];
72
+ display.line(hosts.length > 0
73
+ ? ` Then restart ${joinNames(hosts)} and check with \`planr upgrade status\`.`
74
+ : ' Then check with `planr upgrade status`.');
75
+ }
76
+ /** How `planr upgrade apply` and the inline upgrade offer report an upgrade. */
77
+ export function printUpgradeReport(result, mode) {
78
+ if (!result.ok) {
79
+ logger.error(result.failure?.message ?? 'The upgrade did not complete.');
80
+ if (result.restoredTo)
81
+ logger.info(`Restored the previous version ${result.restoredTo}.`);
82
+ return;
83
+ }
84
+ logger.success(`Upgraded OpenPlanr from ${result.previousVersion} to ${result.installedVersion}.`);
85
+ if (result.releaseNotesError)
86
+ logger.warn(result.releaseNotesError);
87
+ printReleaseNotes(result.releaseNotes, result.installedVersion, mode);
88
+ printNextSteps(result.nextSteps, result.nextStepsError);
89
+ }
90
+ //# sourceMappingURL=upgrade-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade-report.js","sourceRoot":"","sources":["../../src/services/upgrade-report.ts"],"names":[],"mappings":"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"}
@@ -1,19 +1,18 @@
1
1
  /**
2
2
  * `planr upgrade`: reconciles the installed CLI, bundled pipeline and host plugin with the
3
3
  * published compatible set (npm `latest`, cached, short fetch timeout), upgrades the CLI half
4
- * with verify-after-install and restore, runs crossed migrations and prescribes the plugin
5
- * commands for the user to run. Entry points: `reconcileInstalledTuple`, `planCliUpgrade`,
4
+ * with verify-after-install and restore, runs crossed migrations and reports the command that
5
+ * updates each installed coding agent. Entry points: `reconcileInstalledTuple`, `planCliUpgrade`,
6
6
  * `executeCliHalfUpgrade`. It never changes the host plugin itself.
7
7
  */
8
- import { type ClaudeCommandRunner, type ClaudePluginOperation } from './claude-plugin-service.js';
8
+ import { type ClaudeCommandRunner } from './claude-plugin-service.js';
9
+ import { type RuntimeId } from './runtime-manager-service.js';
9
10
  /**
10
11
  * The command that registers the bundled `openplanr-local` marketplace and installs
11
- * `planr@openplanr-local` from it. Prescribed instead of raw `claude plugin` commands when
12
+ * `planr@openplanr-local` from it. Prescribed instead of `planr runtime update` when
12
13
  * Claude Code has no such marketplace yet, because only setup records the installation.
13
14
  */
14
15
  export declare const CLAUDE_PLUGIN_SETUP_COMMAND = "planr setup --runtime claude --scope user";
15
- /** Shown above the prescribed plugin-half commands by `planr upgrade apply` and the inline offer. */
16
- export declare const PLUGIN_HALF_INSTRUCTION = "Run these yourself, in order, or ask the agent to run planr-doctor\u2019s upgrade skill. They bring the plugin `planr setup` installs, `planr@openplanr-local`, up to the version bundled with this CLI:";
17
16
  /**
18
17
  * One component of the published compatibility manifest (`ecosystem.json`'s
19
18
  * `components.*`). Each artifact carries its own version plus the mutual
@@ -120,19 +119,36 @@ export declare function planCliUpgrade(reconciliation: UpgradeReconciliation): U
120
119
  * version header so a summary can never reach back past the target's section.
121
120
  */
122
121
  export declare function summarizeChangelogBetween(oldVersion: string, newVersion: string): string[];
122
+ /** One released version's changelog entries. */
123
+ export interface ReleaseNoteSection {
124
+ version: string;
125
+ entries: string[];
126
+ }
127
+ /** `parseReleaseNotes` over the changelog this CLI ships, or nothing when it ships none. */
128
+ export declare function summarizeReleaseNotes(oldVersion: string, newVersion: string): ReleaseNoteSection[];
129
+ /**
130
+ * The changelog entries released after `oldVersion` up to `newVersion`, newest first, without
131
+ * commit hashes or dependency bumps. Without an `oldVersion` header only the target's own
132
+ * section is returned.
133
+ */
134
+ export declare function parseReleaseNotes(changelog: string, oldVersion: string, newVersion: string): ReleaseNoteSection[];
135
+ /**
136
+ * A command that brings one installed coding agent up to what this CLI bundles.
137
+ * Without `runtime` it concerns OpenPlanr's own runtime state rather than one agent.
138
+ */
139
+ export interface UpgradeNextStep {
140
+ runtime?: RuntimeId;
141
+ host: string;
142
+ command: string;
143
+ detail: string;
144
+ }
123
145
  /**
124
- * The manifest-refresh guarantee: the marketplace refresh is printed FIRST —
125
- * "without which the installer reinstalls the stale version." A stable sort keeps
126
- * every other operation in the order the bundled inspection produced, and each is
127
- * rendered by `formatClaudePluginOperationCommand` (the same argv setup would run),
128
- * so the prescription can never drift from what would actually execute. A missing
129
- * `openplanr-local` marketplace means `planr setup` never ran on this machine, and
130
- * setup is the one command that both registers it and records the installation.
131
- * A retired plugin beside it needs `--replace-managed`: setup refuses the removal
132
- * without the flag, and `--runtime` skips the guided prompt that would offer it.
133
- * This is a pure formatter — it prints the plugin half, it never runs it.
146
+ * One command per installed coding agent whose OpenPlanr files or plugin trail this CLI,
147
+ * planned with the same preview `planr runtime update` applies.
134
148
  */
135
- export declare function prescribePluginHalfCommands(operations: ClaudePluginOperation[], marketplaceRoot: string): string[];
149
+ export declare function upgradeNextSteps(projectDir: string, options?: {
150
+ claudeCommandRunner?: ClaudeCommandRunner;
151
+ }): Promise<UpgradeNextStep[]>;
136
152
  /** One migration's outcome as the injected registry runner reports it. */
137
153
  export interface MigrationRunResult {
138
154
  id: string;
@@ -150,23 +166,20 @@ export interface MigrationRunResult {
150
166
  export type MigrationRunner = (fromVersion: string, toVersion: string, ctx: {
151
167
  projectDir: string;
152
168
  }) => Promise<MigrationRunResult[]>;
153
- /**
154
- * The plugin-half prescription, derived independently of whether the CLI half moved.
155
- *
156
- * Two situations need these commands: a completed CLI upgrade, and a tuple where the CLI
157
- * is already current but the plugins trail (the state every release creates for anyone
158
- * who upgrades the npm half first). The second path used to reach no prescription at all
159
- * while its own message promised one. Deriving the commands here means both surfaces read
160
- * from one place and can never print different instructions for the same machine.
161
- */
162
- export declare function pluginHalfPrescription(claudeCommandRunner?: ClaudeCommandRunner): string[];
169
+ /** Runs the OpenPlanr CLI now installed on disk with the given arguments. */
170
+ export type InstalledCliRunner = (args: string[]) => NpmCommandResult;
171
+ /** The next steps as the upgraded CLI reports them from `planr upgrade status --json`. */
172
+ export declare function readInstalledCliNextSteps(projectDir: string, runner?: InstalledCliRunner): {
173
+ nextSteps: UpgradeNextStep[];
174
+ error?: string;
175
+ };
163
176
  export interface ExecuteCliHalfUpgradeInput {
164
177
  projectDir: string;
165
178
  targetCliVersion: string;
166
179
  /** Injectable npm runner; defaults to the real (or `OPENPLANR_NPM_BIN`) npm. */
167
180
  npmCommandRunner?: NpmCommandRunner;
168
- /** Injectable `claude` runner for the prescription's inspection (hermetic tests). */
169
- claudeCommandRunner?: ClaudeCommandRunner;
181
+ /** Injectable runner for the upgraded CLI; defaults to a new process of this entry point. */
182
+ installedCliRunner?: InstalledCliRunner;
170
183
  /**
171
184
  * Migration runner, run after the CLI half verifies. Omitted (no runner)
172
185
  * means no migrations are attempted; the `apply` command injects the real
@@ -177,9 +190,15 @@ export interface ExecuteCliHalfUpgradeInput {
177
190
  export interface ExecuteCliHalfUpgradeResult {
178
191
  ok: boolean;
179
192
  cliUpgraded: boolean;
193
+ previousVersion: string;
180
194
  installedVersion: string;
181
195
  restoredTo?: string;
182
196
  changelogBullets: string[];
197
+ releaseNotes: ReleaseNoteSection[];
198
+ releaseNotesError?: string;
199
+ nextSteps: UpgradeNextStep[];
200
+ nextStepsError?: string;
201
+ /** The commands of `nextSteps`, for readers of the older field. */
183
202
  pluginHalfCommands: string[];
184
203
  /** Per-migration results for every registered migration this upgrade crossed. */
185
204
  migrations: MigrationRunResult[];
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade-service.d.ts","sourceRoot":"","sources":["../../src/services/upgrade-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"upgrade-service.d.ts","sourceRoot":"","sources":["../../src/services/upgrade-service.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * `planr upgrade`: reconciles the installed CLI, bundled pipeline and host plugin with the
3
3
  * published compatible set (npm `latest`, cached, short fetch timeout), upgrades the CLI half
4
- * with verify-after-install and restore, runs crossed migrations and prescribes the plugin
5
- * commands for the user to run. Entry points: `reconcileInstalledTuple`, `planCliUpgrade`,
4
+ * with verify-after-install and restore, runs crossed migrations and reports the command that
5
+ * updates each installed coding agent. Entry points: `reconcileInstalledTuple`, `planCliUpgrade`,
6
6
  * `executeCliHalfUpgrade`. It never changes the host plugin itself.
7
7
  */
8
8
  import { spawnSync } from 'node:child_process';
@@ -13,18 +13,17 @@ import { fileURLToPath } from 'node:url';
13
13
  import { z } from 'zod';
14
14
  import { parseExternalJson } from '../utils/external-json.js';
15
15
  import { logger } from '../utils/logger.js';
16
- import { formatClaudePluginOperationCommand, inspectBundledClaudePluginIntegration, OPENPLANR_CLAUDE_PLUGIN, } from './claude-plugin-service.js';
16
+ import { inspectBundledClaudePluginIntegration, OPENPLANR_CLAUDE_PLUGIN, } from './claude-plugin-service.js';
17
17
  import { resolvePipelinePackage } from './pipeline-package-service.js';
18
18
  import { readOpenPlanrVersion } from './provenance-service.js';
19
- import { bundledHostRoot, classifyComponentDrift, runtimeRoot } from './runtime-manager-service.js';
19
+ import { RUNTIME_LABELS, summarizeRuntimeChanges } from './runtime-change-summary.js';
20
+ import { bundledHostRoot, classifyComponentDrift, installedRuntimeScopes, previewSetup, RuntimeManagerError, runtimeRoot, } from './runtime-manager-service.js';
20
21
  /**
21
22
  * The command that registers the bundled `openplanr-local` marketplace and installs
22
- * `planr@openplanr-local` from it. Prescribed instead of raw `claude plugin` commands when
23
+ * `planr@openplanr-local` from it. Prescribed instead of `planr runtime update` when
23
24
  * Claude Code has no such marketplace yet, because only setup records the installation.
24
25
  */
25
26
  export const CLAUDE_PLUGIN_SETUP_COMMAND = 'planr setup --runtime claude --scope user';
26
- /** Shown above the prescribed plugin-half commands by `planr upgrade apply` and the inline offer. */
27
- export const PLUGIN_HALF_INSTRUCTION = 'Run these yourself, in order, or ask the agent to run planr-doctor’s upgrade skill. They bring the plugin `planr setup` installs, `planr@openplanr-local`, up to the version bundled with this CLI:';
28
27
  /**
29
28
  * The published compatible set is the npm registry's `latest` document for the
30
29
  * CLI: its version is what a global install lands, and its exact `planr-pipeline`
@@ -406,7 +405,7 @@ export function planCliUpgrade(reconciliation) {
406
405
  return {
407
406
  proceed: false,
408
407
  targetCliVersion: null,
409
- reason: 'The published compatibility manifest is unavailable; no upgrade target can be determined.',
408
+ reason: 'The latest OpenPlanr version could not be checked; the npm registry is unreachable.',
410
409
  };
411
410
  }
412
411
  const target = published.cli.version;
@@ -414,34 +413,34 @@ export function planCliUpgrade(reconciliation) {
414
413
  return {
415
414
  proceed: false,
416
415
  targetCliVersion: null,
417
- reason: 'The installed tuple already matches the published compatible set; nothing to upgrade.',
416
+ reason: `OpenPlanr ${installed.cli} is up to date.`,
418
417
  };
419
418
  }
420
419
  if (status === 'upgrade-available') {
421
420
  return {
422
421
  proceed: true,
423
422
  targetCliVersion: target,
424
- reason: `An upgrade is available: the CLI can move from ${installed.cli} to ${target}.`,
423
+ reason: `OpenPlanr ${target} is available; ${installed.cli} is installed.`,
425
424
  };
426
425
  }
427
426
  if (status === 'incompatible' && compareStableVersions(installed.cli, target) < 0) {
428
427
  return {
429
428
  proceed: true,
430
429
  targetCliVersion: target,
431
- reason: `The tuple is incompatible and the CLI is behind; moving the CLI from ${installed.cli} to ${target}.`,
430
+ reason: `OpenPlanr ${target} is available, and the installed ${installed.cli} no longer matches its plugins.`,
432
431
  };
433
432
  }
434
433
  if (status === 'incompatible') {
435
434
  return {
436
435
  proceed: false,
437
436
  targetCliVersion: null,
438
- reason: `The tuple is incompatible but the CLI (${installed.cli}) is not behind the published ${target}; the plugin half must move — run the prescribed commands below.`,
437
+ reason: `OpenPlanr ${installed.cli} is current, but a coding agent's plugin is behind it. Run the commands below.`,
439
438
  };
440
439
  }
441
440
  return {
442
441
  proceed: false,
443
442
  targetCliVersion: null,
444
- reason: 'The tuple could not be judged.',
443
+ reason: 'The installed OpenPlanr version could not be judged.',
445
444
  };
446
445
  }
447
446
  /**
@@ -528,44 +527,211 @@ export function summarizeChangelogBetween(oldVersion, newVersion) {
528
527
  endIndex = firstHeaderAfterStart;
529
528
  return extractChangelogBullets(lines.slice(startIndex + 1, endIndex));
530
529
  }
530
+ const CHANGESET_COMMIT_PREFIX = /^(?:\[`?[0-9a-f]{7,40}`?\]\([^)]*\):?|[0-9a-f]{7,40}:)\s+/;
531
+ /** `parseReleaseNotes` over the changelog this CLI ships, or nothing when it ships none. */
532
+ export function summarizeReleaseNotes(oldVersion, newVersion) {
533
+ const changelogPath = locateChangelog();
534
+ if (!changelogPath)
535
+ return [];
536
+ return parseReleaseNotes(readFileSync(changelogPath, 'utf8'), oldVersion, newVersion);
537
+ }
531
538
  /**
532
- * The manifest-refresh guarantee: the marketplace refresh is printed FIRST —
533
- * "without which the installer reinstalls the stale version." A stable sort keeps
534
- * every other operation in the order the bundled inspection produced, and each is
535
- * rendered by `formatClaudePluginOperationCommand` (the same argv setup would run),
536
- * so the prescription can never drift from what would actually execute. A missing
537
- * `openplanr-local` marketplace means `planr setup` never ran on this machine, and
538
- * setup is the one command that both registers it and records the installation.
539
- * A retired plugin beside it needs `--replace-managed`: setup refuses the removal
540
- * without the flag, and `--runtime` skips the guided prompt that would offer it.
541
- * This is a pure formatter — it prints the plugin half, it never runs it.
539
+ * The changelog entries released after `oldVersion` up to `newVersion`, newest first, without
540
+ * commit hashes or dependency bumps. Without an `oldVersion` header only the target's own
541
+ * section is returned.
542
542
  */
543
- export function prescribePluginHalfCommands(operations, marketplaceRoot) {
544
- if (operations.some((operation) => operation.kind === 'add-marketplace')) {
545
- const retiresLegacyPlugin = operations.some((operation) => operation.kind === 'remove');
546
- return [
547
- retiresLegacyPlugin
548
- ? `${CLAUDE_PLUGIN_SETUP_COMMAND} --replace-managed`
549
- : CLAUDE_PLUGIN_SETUP_COMMAND,
550
- ];
551
- }
552
- const rank = (operation) => operation.kind === 'refresh-marketplace' ? 0 : 1;
553
- return [...operations]
554
- .sort((a, b) => rank(a) - rank(b))
555
- .map((operation) => formatClaudePluginOperationCommand(operation, marketplaceRoot));
543
+ export function parseReleaseNotes(changelog, oldVersion, newVersion) {
544
+ const lines = changelog.split(/\r?\n/);
545
+ const startIndex = lines.findIndex((line) => changelogHeaderMatches(line, newVersion));
546
+ if (startIndex === -1)
547
+ return [];
548
+ const hasOldSection = lines.some((line) => changelogHeaderMatches(line, oldVersion));
549
+ const sections = [];
550
+ let entry;
551
+ let skipEntry = false;
552
+ const closeEntry = () => {
553
+ if (entry && !skipEntry)
554
+ sections[sections.length - 1]?.entries.push(entry.join(' '));
555
+ entry = undefined;
556
+ skipEntry = false;
557
+ };
558
+ for (const line of lines.slice(startIndex)) {
559
+ const header = /^##\s+\[?([^\]\s]+)\]?$/.exec(line.trim());
560
+ if (header) {
561
+ closeEntry();
562
+ if (header[1] === oldVersion || (!hasOldSection && sections.length > 0))
563
+ break;
564
+ sections.push({ version: header[1], entries: [] });
565
+ continue;
566
+ }
567
+ const item = /^-\s+(.*\S)\s*$/.exec(line);
568
+ if (item) {
569
+ closeEntry();
570
+ const text = item[1].replace(CHANGESET_COMMIT_PREFIX, '');
571
+ skipEntry = /^Updated dependencies\b/.test(text);
572
+ entry = [text];
573
+ }
574
+ else if (entry && /^\s+\S/.test(line)) {
575
+ entry.push(line.trim().replace(/^-\s+/, ''));
576
+ }
577
+ }
578
+ closeEntry();
579
+ return sections.filter((section) => section.entries.length > 0);
580
+ }
581
+ const RUNTIME_COMMAND_NAMES = {
582
+ 'claude-code': 'claude',
583
+ codex: 'codex',
584
+ cursor: 'cursor',
585
+ };
586
+ function nextStepCommand(runtime, scope, skillMode, operations) {
587
+ const claudeKinds = operations
588
+ .filter((operation) => operation.runtime === 'claude-code')
589
+ .map((operation) => operation.kind);
590
+ // Only setup registers the local marketplace and may remove a retired plugin.
591
+ if (claudeKinds.includes('add-marketplace') || claudeKinds.includes('remove')) {
592
+ return `${CLAUDE_PLUGIN_SETUP_COMMAND}${claudeKinds.includes('remove') ? ' --replace-managed' : ''} --yes`;
593
+ }
594
+ // `runtime update` has no skill-mode flag and would move a unified-plugin install to direct skills.
595
+ if (runtime === 'codex' && skillMode === 'unified-plugin') {
596
+ return `planr setup --runtime codex --scope ${scope} --skill-mode unified-plugin --yes`;
597
+ }
598
+ return `planr runtime update ${RUNTIME_COMMAND_NAMES[runtime]} --scope ${scope} --yes`;
599
+ }
600
+ /** Recorded installs, plus a Claude plugin installed without a record of it. */
601
+ async function nextStepCandidates(projectDir, claudeCommandRunner) {
602
+ let candidates = [];
603
+ let stateError;
604
+ try {
605
+ candidates = await installedRuntimeScopes(projectDir);
606
+ }
607
+ catch (error) {
608
+ if (!(error instanceof RuntimeManagerError))
609
+ throw error;
610
+ stateError = error;
611
+ }
612
+ if (!candidates.some(({ runtime, scope }) => runtime === 'claude-code' && scope === 'user')) {
613
+ const plugin = inspectBundledHostPlugin(claudeCommandRunner).plugins.find((entry) => entry.name === OPENPLANR_CLAUDE_PLUGIN);
614
+ if (plugin?.installed)
615
+ candidates.unshift({ runtime: 'claude-code', scope: 'user' });
616
+ }
617
+ return { candidates, ...(stateError ? { stateError } : {}) };
618
+ }
619
+ /** The step one install needs, or `undefined` when it already matches this CLI. */
620
+ async function planNextStep({ runtime, scope, skillMode }, projectDir, claudeCommandRunner) {
621
+ const preview = await previewSetup({
622
+ projectDir,
623
+ cliVersion: readOpenPlanrVersion(),
624
+ runtime,
625
+ scope,
626
+ dryRun: true,
627
+ ...(skillMode ? { skillMode } : {}),
628
+ ...(claudeCommandRunner ? { claudeCommandRunner } : {}),
629
+ });
630
+ const change = summarizeRuntimeChanges(preview, {
631
+ bookkeepingRoot: runtimeRoot(),
632
+ applied: false,
633
+ }).find((entry) => entry.runtime === runtime);
634
+ if (!change?.changed)
635
+ return undefined;
636
+ return {
637
+ runtime,
638
+ host: change.host,
639
+ command: nextStepCommand(runtime, scope, skillMode, preview.runtimeOperations),
640
+ detail: change.summary,
641
+ };
556
642
  }
557
643
  /**
558
- * The plugin-half prescription, derived independently of whether the CLI half moved.
559
- *
560
- * Two situations need these commands: a completed CLI upgrade, and a tuple where the CLI
561
- * is already current but the plugins trail (the state every release creates for anyone
562
- * who upgrades the npm half first). The second path used to reach no prescription at all
563
- * while its own message promised one. Deriving the commands here means both surfaces read
564
- * from one place and can never print different instructions for the same machine.
644
+ * One command per installed coding agent whose OpenPlanr files or plugin trail this CLI,
645
+ * planned with the same preview `planr runtime update` applies.
565
646
  */
566
- export function pluginHalfPrescription(claudeCommandRunner) {
567
- const marketplaceRoot = bundledHostRoot('claude');
568
- return prescribePluginHalfCommands(inspectBundledClaudePluginIntegration(marketplaceRoot, claudeCommandRunner).operations, marketplaceRoot);
647
+ export async function upgradeNextSteps(projectDir, options = {}) {
648
+ const steps = [];
649
+ const doctorStep = (error, runtime) => {
650
+ if (steps.some((step) => step.detail === error.message))
651
+ return;
652
+ steps.push({
653
+ ...(runtime ? { runtime } : {}),
654
+ host: runtime ? RUNTIME_LABELS[runtime] : 'OpenPlanr',
655
+ command: 'planr doctor',
656
+ detail: error.message,
657
+ });
658
+ };
659
+ const { candidates, stateError } = await nextStepCandidates(projectDir, options.claudeCommandRunner);
660
+ if (stateError)
661
+ doctorStep(stateError);
662
+ for (const candidate of candidates) {
663
+ try {
664
+ const step = await planNextStep(candidate, projectDir, options.claudeCommandRunner);
665
+ if (step)
666
+ steps.push(step);
667
+ }
668
+ catch (error) {
669
+ if (!(error instanceof Error))
670
+ throw error;
671
+ doctorStep(error, candidate.runtime);
672
+ }
673
+ }
674
+ return steps;
675
+ }
676
+ // After `npm install -g` this process still executes the previous version's modules,
677
+ // so only a fresh process of the same entry point runs the upgraded code.
678
+ function defaultInstalledCliRunner(args) {
679
+ const entry = process.argv[1];
680
+ if (!entry) {
681
+ return {
682
+ status: null,
683
+ stdout: '',
684
+ stderr: '',
685
+ error: new Error('The entry point of the running CLI is unknown.'),
686
+ };
687
+ }
688
+ const result = spawnSync(process.execPath, [...process.execArgv, entry, ...args], {
689
+ encoding: 'utf8',
690
+ windowsHide: true,
691
+ timeout: 120_000,
692
+ });
693
+ return {
694
+ status: result.status,
695
+ stdout: result.stdout ?? '',
696
+ stderr: result.stderr ?? '',
697
+ ...(result.error ? { error: result.error } : {}),
698
+ };
699
+ }
700
+ const nextStepsReportSchema = z.object({
701
+ nextSteps: z.array(z.object({
702
+ runtime: z.enum(['claude-code', 'codex', 'cursor']).optional(),
703
+ host: z.string(),
704
+ command: z.string(),
705
+ detail: z.string(),
706
+ })),
707
+ });
708
+ const installedStatusSchema = z.union([
709
+ nextStepsReportSchema,
710
+ z.object({ ok: z.literal(false), problem: z.string() }),
711
+ ]);
712
+ /** The next steps as the upgraded CLI reports them from `planr upgrade status --json`. */
713
+ export function readInstalledCliNextSteps(projectDir, runner = defaultInstalledCliRunner) {
714
+ const result = runner(['--project-dir', projectDir, 'upgrade', 'status', '--json']);
715
+ if (result.error) {
716
+ return { nextSteps: [], error: `The upgraded CLI could not be run: ${result.error.message}.` };
717
+ }
718
+ const report = result.stdout.trim().split(/\r?\n/).pop() ?? '';
719
+ try {
720
+ const parsed = parseExternalJson(report, installedStatusSchema, 'planr upgrade status --json');
721
+ if ('nextSteps' in parsed)
722
+ return { nextSteps: parsed.nextSteps };
723
+ return {
724
+ nextSteps: [],
725
+ error: `The upgraded CLI could not list its next steps: ${parsed.problem}`,
726
+ };
727
+ }
728
+ catch (error) {
729
+ const detail = result.stderr.trim() || (error instanceof Error ? error.message : String(error));
730
+ return {
731
+ nextSteps: [],
732
+ error: `The upgraded CLI did not report its next steps (exit ${result.status}): ${detail}`,
733
+ };
734
+ }
569
735
  }
570
736
  /**
571
737
  * Execute the one half the CLI owns — a global npm install — and
@@ -593,7 +759,10 @@ export async function executeCliHalfUpgrade(input) {
593
759
  ok: false,
594
760
  cliUpgraded: false,
595
761
  installedVersion: readOpenPlanrVersion(),
762
+ previousVersion,
596
763
  changelogBullets: [],
764
+ releaseNotes: [],
765
+ nextSteps: [],
597
766
  pluginHalfCommands: [],
598
767
  migrations: [],
599
768
  failure: {
@@ -618,7 +787,10 @@ export async function executeCliHalfUpgrade(input) {
618
787
  cliUpgraded: false,
619
788
  installedVersion: restoredVersion,
620
789
  restoredTo: previousVersion,
790
+ previousVersion,
621
791
  changelogBullets: [],
792
+ releaseNotes: [],
793
+ nextSteps: [],
622
794
  pluginHalfCommands: [],
623
795
  migrations: [],
624
796
  failure: { step: 'verify', message },
@@ -644,7 +816,10 @@ export async function executeCliHalfUpgrade(input) {
644
816
  ok: false,
645
817
  cliUpgraded: true,
646
818
  installedVersion: verifiedVersion,
819
+ previousVersion,
647
820
  changelogBullets: [],
821
+ releaseNotes: [],
822
+ nextSteps: [],
648
823
  pluginHalfCommands: [],
649
824
  migrations,
650
825
  failure: {
@@ -659,13 +834,26 @@ export async function executeCliHalfUpgrade(input) {
659
834
  // "no entries", not as a failed upgrade (which would misreport a machine that
660
835
  // is, in fact, upgraded).
661
836
  const changelogBullets = summarizeChangelogBetween(previousVersion, verifiedVersion);
662
- const pluginHalfCommands = pluginHalfPrescription(input.claudeCommandRunner);
837
+ let releaseNotes = [];
838
+ let releaseNotesError;
839
+ try {
840
+ releaseNotes = summarizeReleaseNotes(previousVersion, verifiedVersion);
841
+ }
842
+ catch (error) {
843
+ releaseNotesError = `The release notes could not be read: ${error instanceof Error ? error.message : String(error)}`;
844
+ }
845
+ const next = readInstalledCliNextSteps(input.projectDir, input.installedCliRunner);
663
846
  return {
664
847
  ok: true,
665
848
  cliUpgraded: true,
849
+ previousVersion,
666
850
  installedVersion: verifiedVersion,
667
851
  changelogBullets,
668
- pluginHalfCommands,
852
+ releaseNotes,
853
+ ...(releaseNotesError ? { releaseNotesError } : {}),
854
+ nextSteps: next.nextSteps,
855
+ ...(next.error ? { nextStepsError: next.error } : {}),
856
+ pluginHalfCommands: next.nextSteps.map((step) => step.command),
669
857
  migrations,
670
858
  };
671
859
  }