@prodero/curator 0.1.0 → 0.2.0

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 (50) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/CHANGELOG.md +19 -2
  3. package/README.md +32 -33
  4. package/dist/cli.js +58 -20
  5. package/dist/contracts.js +1 -0
  6. package/dist/conversation-presentation.js +62 -27
  7. package/dist/core/agent-instructions.js +31 -18
  8. package/dist/core/curation-graphical.js +22 -10
  9. package/dist/core/curation-question.js +28 -0
  10. package/dist/core/curation-semantic-contract.js +4 -1
  11. package/dist/core/curation-semantic-jobs.js +78 -18
  12. package/dist/core/knowledge-graph.js +5 -4
  13. package/dist/core/local-preferences.js +76 -0
  14. package/dist/core/memory-index.js +2 -0
  15. package/dist/core/memory-link-repair.js +6 -2
  16. package/dist/core/memory-sync-curator.js +25 -5
  17. package/dist/core/memory-sync.js +124 -29
  18. package/dist/core/npm-update.js +289 -0
  19. package/dist/core/project.js +8 -8
  20. package/dist/core/session-index.js +10 -6
  21. package/dist/core/sessions.js +3 -3
  22. package/dist/core/settings.js +34 -16
  23. package/dist/core/skills.js +21 -2
  24. package/dist/core/storage.js +55 -1
  25. package/dist/core/sync-transaction.js +85 -49
  26. package/dist/core/update.js +9 -3
  27. package/dist/curation-review-adapter.js +1 -1
  28. package/dist/diagnostic-boundary.js +4 -3
  29. package/dist/memory-presentation.js +26 -16
  30. package/dist/memory-sync-operation.js +88 -0
  31. package/dist/presentation-server.js +25 -10
  32. package/dist/presentation.js +92 -61
  33. package/dist/server.js +59 -51
  34. package/dist/settings-definition.js +3 -3
  35. package/dist/status-presentation.js +20 -0
  36. package/dist/text-layout.js +278 -0
  37. package/dist/update-command.js +53 -0
  38. package/dist/update-instructions.js +4 -0
  39. package/dist-ui/index.html +117 -115
  40. package/docs/COMPATIBILITY.md +29 -10
  41. package/docs/MIGRATIONS.md +4 -0
  42. package/docs/RECOVERY.md +3 -1
  43. package/docs/RELEASING.md +68 -10
  44. package/docs/hosts.md +25 -7
  45. package/docs/images/prodero-context-ready-cropped.png +0 -0
  46. package/docs/images/prodero-overview-cropped.png +0 -0
  47. package/docs/images/prodero-session-learned-cropped.png +0 -0
  48. package/docs/ui-themes.md +10 -4
  49. package/instructions/AGENTS.md +24 -18
  50. package/package.json +6 -2
@@ -0,0 +1,20 @@
1
+ import { publicDiagnostic } from './core/diagnostics.js';
2
+ import { PRODERO_VERSION } from './core/constants.js';
3
+ import { MEMORY_COPY } from './memory-presentation.js';
4
+ const plain = (value) => value.replace(/[\u0000-\u001f\u007f-\u009f]/g, ' ').trim();
5
+ export function statusFailure(error, scope, migration) {
6
+ const message = plain(publicDiagnostic(error)), code = /^(PRODERO_[A-Z0-9_]+):/.exec(message)?.[1] ?? 'PRODERO_STATUS_FAILED';
7
+ return { ok: false, version: PRODERO_VERSION, scope, error: { code, message, recovery: migration ? plain(publicDiagnostic(migration.recovery ?? (migration.state === 'runtime-too-old' ? 'Update Curator before reading this project.' : 'Run prodero migrate status with this project root to review the required update.'))) : 'Check the command arguments, selected root, and preserved configuration before retrying.' }, ...(migration ? { migration: { ...migration, ...(migration.diagnostic ? { diagnostic: plain(publicDiagnostic(migration.diagnostic)) } : {}), ...(migration.recovery ? { recovery: plain(publicDiagnostic(migration.recovery)) } : {}) } } : {}) };
8
+ }
9
+ export function renderStatusFailure(result) {
10
+ return [`Prodero ${result.version} · ${result.scope} status unavailable`, `${result.error.code}: ${result.error.message}`, result.error.recovery, ...(result.migration ? [`Project update: ${result.migration.state}`] : [])].join('\n');
11
+ }
12
+ const yes = (value) => value === null ? 'unknown' : value ? 'yes' : 'no';
13
+ export function renderMachineStatus(result) {
14
+ return [`Prodero ${result.version}`, `Machine integrations${result.ok ? '' : ' · attention required'}`, ...result.hosts.flatMap(h => [`${h.name}: ${h.state.replaceAll('-', ' ')}`, ` Detected: ${yes(h.detected)} · MCP configured: ${yes(h.mcpConfigured)} · Launcher installed: ${yes(h.launcherInstalled)}${h.runtimeMode ? ` · Runtime: ${h.runtimeMode}` : ''}`, ` ${plain(publicDiagnostic(h.message))}`])].join('\n');
15
+ }
16
+ export function renderProjectStatus(result) {
17
+ const p = result.project, u = result.update;
18
+ return [`Prodero ${result.version}`, `Project: ${plain(p.name)} (${plain(p.root)})`, `Initialized: ${yes(p.initialized)} · Git repository: ${yes(p.git)} · Project instructions: ${yes(p.agentsFile)}`, ...(p.initialized ? [`Project schema: ${p.schemaVersion}`, `Knowledge: ${p.knowledgeCount} current · ${p.localKnowledgeCount} Local · ${p.versionedKnowledgeCount} Versioned`, `Sessions: ${p.sessionCount} total · ${p.activeSessionCount} active · ${p.pendingSessionCount} pending`, `Skills: ${p.skillCount} active · ${p.skillCandidateCount} candidates`, `Checkpoints: ${p.checkpointCount} · Last sync: ${p.lastSyncAt ?? 'never'}`, `Needs attention: ${p.conflictCount} conflicts · ${p.knowledgeGraph.brokenReferences} broken references · ${p.promotionRecommendedCount} sharing suggestions`].concat(p.memoryHealth?.missingProvenanceItems ? [`Provenance: ${p.memoryHealth.missingProvenanceItems} current records with incomplete metadata. ${MEMORY_COPY.incompleteProvenance}`] : []) : ['Project is not initialized. Run prodero init with --root set to this directory.']), u.status === 'current' ? `Update: current (${u.current})` : u.status === 'update-available' ? `Update available: ${u.current} → ${u.latest}` : `Update check ${u.status}: ${plain(publicDiagnostic(u.error ?? 'No release information is available.'))}`, ...(u.status === 'update-available' && u.manualUpdateCommand ? [u.manualUpdateCommand] : [])].join('\n');
19
+ }
20
+ //# sourceMappingURL=status-presentation.js.map
@@ -0,0 +1,278 @@
1
+ import { numberedActions } from './conversation-presentation.js';
2
+ const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
3
+ const graphemes = (s) => Array.from(segmenter.segment(s), x => x.segment);
4
+ const glyphWidth = (s) => {
5
+ const n = s.codePointAt(0) ?? 0;
6
+ if (/^[\p{Mark}]+$/u.test(s))
7
+ return 0;
8
+ return /[\p{Extended_Pictographic}\p{Regional_Indicator}\u20e3]/u.test(s) || n >= 0x1100 && (n <= 0x115f || n >= 0x2e80 && n <= 0xa4cf || n >= 0xac00 && n <= 0xd7a3 || n >= 0xf900 && n <= 0xfaff || n >= 0xfe10 && n <= 0xfe6f || n >= 0xff01 && n <= 0xff60 || n >= 0x20000 && n <= 0x3ffff) ? 2 : 1;
9
+ };
10
+ export const textWidth = (s) => graphemes(s).reduce((n, g) => n + glyphWidth(g), 0);
11
+ // Formatting never permits terminal escapes, bidi controls or injected fence endings.
12
+ // Newlines and literal code/body spacing survive in the verbatim fallback below.
13
+ export const safeText = (s) => String(s ?? '').replace(/\x1b(?:\][^\x07]*(?:\x07|\x1b\\)|\[[0-?]*[ -/]*[@-~])/g, '').replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, '');
14
+ export function wrapText(raw, width) {
15
+ const result = [];
16
+ for (const paragraph of safeText(raw).replace(/\t/g, ' ').split(/\r?\n/)) {
17
+ let line = '', used = 0;
18
+ for (const g of graphemes(paragraph)) {
19
+ const size = glyphWidth(g);
20
+ if (used + size > width && line) {
21
+ const cut = line.lastIndexOf(' ');
22
+ if (cut > 0) {
23
+ result.push(line.slice(0, cut));
24
+ line = line.slice(cut + 1);
25
+ used = textWidth(line);
26
+ }
27
+ else {
28
+ result.push(line);
29
+ line = '';
30
+ used = 0;
31
+ }
32
+ }
33
+ line += g;
34
+ used += size;
35
+ }
36
+ result.push(line);
37
+ }
38
+ return result;
39
+ }
40
+ /** Dynamic fences retain complete Markdown/code examples without escaping their wording. */
41
+ export function textFence(content) {
42
+ const text = safeText(content);
43
+ let longest = 2;
44
+ for (const m of text.matchAll(/`+/g))
45
+ longest = Math.max(longest, m[0].length);
46
+ const fence = '`'.repeat(longest + 1);
47
+ return `${fence}text\n${text}\n${fence}`;
48
+ }
49
+ export function entryKey(index) {
50
+ let n = index + 1, key = '';
51
+ do {
52
+ n--;
53
+ key = String.fromCharCode(65 + n % 26) + key;
54
+ n = Math.floor(n / 26);
55
+ } while (n);
56
+ return key;
57
+ }
58
+ const number = (n) => n.toLocaleString('en-US');
59
+ export function budgetLines(b) {
60
+ if (!Number.isFinite(b.used) || !Number.isFinite(b.total) || b.used < 0 || b.total <= 0)
61
+ return ['Budget unavailable.'];
62
+ const percent = Math.round(b.used / b.total * 100);
63
+ const filled = Math.max(0, Math.min(25, Math.round(b.used / b.total * 25)));
64
+ return ['▰'.repeat(filled) + '▱'.repeat(25 - filled) + ` ${percent}%`, `${number(b.used)} / ${number(b.total)} est. tokens`, `${b.source} · Prodero allocation only.`, ...(b.used > b.total ? ['Allocation exceeds its budget.'] : [])];
65
+ }
66
+ export function renderProductText(vm, diagnostics = false, expanded = diagnostics, plain = false) {
67
+ const scene = vm.textScene, action = scene?.action, v = vm.conversation;
68
+ const width = 38, prefix = plain ? '| ' : '│ ', rule = plain ? '-' : '─';
69
+ const screens = { open: '01', status: '01', 'status-details': '01', initialized: '01', knowledge: '02', 'knowledge-mutated': '02', prepare: '03', context: '03', 'memory-sync': '04', 'curation-question': '04', curation: '05', 'instruction-review': '06', 'instruction-optimize': '06', 'promotion-preview': '07', 'promotion-applied': '07', 'session-close': '08', 'session-review': '08', 'config-get': '09', 'update-instructions': '10', migration: '10', error: '00' };
70
+ const screen = vm.knowledgeTable ? '02' : screens[action ?? ''] ?? '00';
71
+ const head = `${plain ? '+-' : '╭─'} >_ PRODERO `;
72
+ const lines = [head + rule.repeat(width - textWidth(head) - 4) + ` /${screen}`];
73
+ const line = (text = '') => {
74
+ const indent = (text.match(/^ +/)?.[0] ?? '').slice(0, 12);
75
+ lines.push(...wrapText(text.slice(indent.length), width - 2 - indent.length).map(s => prefix.trimEnd() + (s.trimEnd() ? ' ' + indent + s.trimEnd() : '')));
76
+ };
77
+ const section = (title) => {
78
+ if (textWidth(safeText(title)) > width - 5) {
79
+ lines.push((plain ? '+' : '├') + rule.repeat(width - 1));
80
+ line(title);
81
+ return;
82
+ }
83
+ const parts = wrapText(title, width - 5);
84
+ const p = `${plain ? '+-' : '├─'} ${parts.shift()} `;
85
+ lines.push(p + rule.repeat(Math.max(0, width - textWidth(p))));
86
+ parts.forEach(part => line(part));
87
+ };
88
+ const inset = () => { line(); lines.push(prefix + ' ' + rule.repeat(27)); line(); };
89
+ const title = vm.title.replace(/^Prodero · /, '');
90
+ line(action === 'update-instructions' ? 'Prodero · terminal update' : scene?.project ? `${scene.project} · project memory` : 'Prodero');
91
+ line();
92
+ const prepared = action === 'prepare' || action === 'context';
93
+ const full = expanded || !v || v.kind === 'details';
94
+ const compactReady = prepared && !full && !v?.blocking && !v?.attention.length;
95
+ if (compactReady) {
96
+ const compact = ['>_ PRODERO /03 · Context Ready'];
97
+ if (scene?.project)
98
+ compact.push(...wrapText(scene.project, width));
99
+ compact.push(...wrapText(v?.facts.find(x => x.includes(' required · ')) ?? '', width));
100
+ if (scene?.budget)
101
+ compact.push(...wrapText(`${number(scene.budget.used)} / ${number(scene.budget.total)} est. Prodero tokens`, width));
102
+ compact.push('Ready · continuing the task');
103
+ return textFence(compact.join('\n'));
104
+ }
105
+ // Exact bodies are deliberately unframed: long code/paths and whitespace are not
106
+ // truncated or rewritten merely to fit a phone. The panel remains bounded.
107
+ const bodies = [];
108
+ const verbatim = (heading, items) => {
109
+ if (!items.length)
110
+ return;
111
+ bodies.push(`${heading}:\n` + items.map(i => [i.title, i.body, ...i.notes].filter(Boolean).join('\n')).join('\n\n ' + rule.repeat(27) + '\n\n'));
112
+ };
113
+ if (scene?.home && !full) {
114
+ const h = scene.home;
115
+ if (h.initialized) {
116
+ if (action === 'initialized') {
117
+ line('Initialized');
118
+ line();
119
+ }
120
+ section('CONTEXT');
121
+ if (scene.budget)
122
+ budgetLines(scene.budget).forEach(x => line(plain ? x.replaceAll('▰', '#').replaceAll('▱', '.') : x));
123
+ else
124
+ line('Task allocation not prepared here.');
125
+ line();
126
+ section('MEMORY');
127
+ if (h.records !== undefined)
128
+ line(`${number(h.records)} records`);
129
+ if (h.local !== undefined && h.versioned !== undefined)
130
+ line(`${number(h.local)} local · ${number(h.versioned)} versioned`);
131
+ line();
132
+ section('ATTENTION');
133
+ if (h.deferred !== undefined)
134
+ line(`? ${h.deferred} deferred questions`);
135
+ if (h.pending !== undefined)
136
+ line(`Δ ${h.pending} unapplied change groups`);
137
+ (v?.attention ?? []).forEach(x => line(x));
138
+ if (!v?.attention.length && h.deferred === undefined && h.pending === undefined)
139
+ line(action === 'status' && h.records === undefined ? 'Lightweight status; counts not loaded.' : 'No attention needed.');
140
+ // Session activity is real data, not evidence of a background agent.
141
+ v?.facts.filter(x => x.startsWith('Session')).forEach(x => line(x));
142
+ }
143
+ else {
144
+ section(v?.state ?? title);
145
+ (v?.facts ?? vm.notices).forEach(x => line(x));
146
+ v?.attention.forEach(x => line(x));
147
+ }
148
+ }
149
+ else if (prepared) {
150
+ section(v?.blocking ? v.state : v?.attention.length ? 'Attention' : action === 'prepare' ? 'Context Ready' : 'View Context');
151
+ if (!compactReady && scene?.task)
152
+ line(`Task: ${scene.task}`);
153
+ if (!v?.blocking)
154
+ line(action === 'context' ? 'Read-only context view.' : v?.attention.length ? 'Task is not blocked; see attention.' : 'Ready · continuing the task');
155
+ const facts = v?.facts.filter(x => !x.startsWith('Context ')) ?? [];
156
+ if (!compactReady) {
157
+ line();
158
+ section('PROVIDED TO AGENT');
159
+ }
160
+ facts.forEach(x => line(x));
161
+ if (scene?.budget) {
162
+ if (!compactReady) {
163
+ line();
164
+ section('PRODERO CONTEXT');
165
+ budgetLines(scene.budget).forEach(x => line(plain ? x.replaceAll('▰', '#').replaceAll('▱', '.') : x));
166
+ }
167
+ else
168
+ line(`${number(scene.budget.used)} / ${number(scene.budget.total)} est. Prodero tokens`);
169
+ }
170
+ if (v?.attention.length) {
171
+ line();
172
+ section('ATTENTION');
173
+ v.attention.forEach(x => line(x));
174
+ }
175
+ if (full)
176
+ for (const s of vm.sections) {
177
+ section(s.title);
178
+ s.items.forEach((i, index) => { if (index)
179
+ inset(); line(i.title); });
180
+ verbatim(s.title, s.items);
181
+ }
182
+ }
183
+ else if (vm.knowledgeTable) {
184
+ const t = vm.knowledgeTable;
185
+ section('KNOWLEDGE');
186
+ line(t.rows.length ? `Showing ${t.offset + 1}-${t.offset + t.rows.length} of ${t.total}` : 'No knowledge matches these filters.');
187
+ t.rows.forEach((r, index) => {
188
+ if (index)
189
+ inset();
190
+ else
191
+ line();
192
+ line(`${r.key} · ${r.title}`);
193
+ line(` ${r.layer}`);
194
+ const preview = vm.sections.find(s => s.title === 'Knowledge')?.items[index]?.body;
195
+ if (preview) {
196
+ const flat = safeText(preview).replace(/\s+/g, ' ').trim();
197
+ const summary = graphemes(flat).slice(0, 90).join('');
198
+ line(' ' + summary + (summary.length < flat.length ? '…' : ''));
199
+ }
200
+ });
201
+ if (t.rows.length) {
202
+ line();
203
+ line('Enter a letter to open its record.');
204
+ }
205
+ if (expanded)
206
+ verbatim('Knowledge', vm.sections.find(s => s.title === 'Knowledge')?.items ?? []);
207
+ }
208
+ else {
209
+ if (action === 'memory-sync') {
210
+ section('CURATE MEMORY');
211
+ const state = full ? title.replace(/^Curate Memory · /, '') : v?.state ?? title;
212
+ if (state !== 'Curate Memory')
213
+ line(state);
214
+ }
215
+ else
216
+ section(action === 'session-close' ? 'Session closed' : v?.kind !== 'details' && !full ? v?.state ?? title : title);
217
+ if (action === 'session-close')
218
+ line(v?.state ?? 'Check saved state');
219
+ if (full && v?.reviewRequired)
220
+ line('Review required · not approval.');
221
+ if (!full && v?.kind !== 'details') {
222
+ v?.facts.forEach(x => line(x));
223
+ if (v?.attention.length) {
224
+ line();
225
+ section('ATTENTION');
226
+ v.attention.forEach(x => line(x));
227
+ }
228
+ }
229
+ else {
230
+ vm.metrics.forEach(m => line(`${m.label} ${m.value}${m.detail ? ' · ' + m.detail : ''}`));
231
+ if (!vm.sections.some(s => s.title === 'Knowledge detail'))
232
+ vm.notices.forEach(x => line(x));
233
+ for (const s of vm.sections) {
234
+ section(s.title);
235
+ s.items.forEach((i, n) => { if (n)
236
+ inset(); line(i.title); });
237
+ verbatim(s.title, s.items);
238
+ }
239
+ }
240
+ }
241
+ // Exact reviews precede the approval menu; reading and approving remain distinct.
242
+ if (v?.prompt) {
243
+ line();
244
+ wrapText(v.prompt, width - 2).forEach(x => line(x));
245
+ }
246
+ lines.push((plain ? '+' : '╰') + rule.repeat(width - 1));
247
+ const actions = numberedActions(vm, full);
248
+ const menu = [];
249
+ if (actions.length) {
250
+ menu.push('', 'Available actions:', '');
251
+ for (const a of actions) {
252
+ const key = ` ${a.number} > `;
253
+ const parts = wrapText(a.label, width - key.length);
254
+ menu.push(key + parts[0], ...parts.slice(1).map(x => ' '.repeat(key.length) + x));
255
+ if (a.approval)
256
+ menu.push(' Explicit approval required.');
257
+ if (a.input)
258
+ menu.push(...wrapText(a.input.prompt, width - 6).map(x => ' ' + x));
259
+ }
260
+ menu.push('', 'Reply with a number or the command name.');
261
+ }
262
+ // Keep the service separator outside the record and its metadata.
263
+ if (vm.sections.some(s => s.title === 'Knowledge detail'))
264
+ bodies.push('-------------\n' + vm.notices.join('\n'));
265
+ if (diagnostics || action === 'curation' || full && action === 'migration')
266
+ for (const s of vm.diagnostics ?? [])
267
+ verbatim(s.title, s.items);
268
+ const panel = textFence(lines.join('\n'));
269
+ const exact = bodies.map(textFence);
270
+ const approval = full && actions.some(a => a.approval) && (action === 'memory-sync' || action === 'migration')
271
+ ? action === 'memory-sync' ? 'Approve these exact changes?' : `${actions.find(a => a.approval).label}?` : '';
272
+ return [panel, ...exact, menu.length ? textFence(menu.join('\n').replace(/^\n/, '').trimEnd()) : '', approval].filter(Boolean).join('\n\n');
273
+ }
274
+ /** Public sanitized errors have no executable retry/apply action. */
275
+ export function renderTextError(message, plain = false) {
276
+ return renderProductText({ version: 1, title: 'Prodero · Error', metrics: [], notices: [message], sections: [], actions: [], textScene: { action: 'error' } }, false, true, plain);
277
+ }
278
+ //# sourceMappingURL=text-layout.js.map
@@ -0,0 +1,53 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { runNpmUpdate, NpmUpdateError } from './core/npm-update.js';
3
+ const help = `Usage: prodero update [exact-version] [--yes]
4
+
5
+ Finish work and close Codex and Claude before updating. The terminal waits for
6
+ npm and verifies the installed CLI; reopen your applications and reconnect afterward.
7
+ Only an ordinary same-user global npm installation with Node 22.14–22.x/npm 10.9.x
8
+ is supported. Install scripts are disabled; project and host settings are untouched.
9
+
10
+ Without a version, an interactive terminal checks the latest public release.
11
+ Non-interactive use requires an exact stable version and --yes, confirming that
12
+ work has ended and applications are closed. Equal versions are a no-op; downgrades
13
+ are refused. No sudo, automatic application closing or package rollback is used.`;
14
+ /** Closing stdin or Ctrl-C at the maintenance prompt is a refusal. */
15
+ export function terminalConfirmation(question, input = process.stdin, output = process.stdout) {
16
+ const terminal = createInterface({ input, output });
17
+ return new Promise(resolve => { let done = false; const finish = (answer) => { if (done)
18
+ return; done = true; terminal.close(); resolve(answer); }; terminal.once('close', () => finish(false)); terminal.once('SIGINT', () => finish(false)); terminal.question(question).then(answer => finish(/^y(?:es)?$/i.test(answer.trim())), () => finish(false)); });
19
+ }
20
+ export async function updateCommand(args, dependencies = {}) {
21
+ if (args.length === 1 && ['--help', '-h'].includes(args[0])) {
22
+ (dependencies.write ?? console.log)(help);
23
+ return 0;
24
+ }
25
+ let yes = false, targetVersion;
26
+ for (const arg of args) {
27
+ if (arg === '--yes') {
28
+ if (yes)
29
+ throw new NpmUpdateError('NPM_UPDATE_ARGUMENT', 'Specify --yes only once.');
30
+ yes = true;
31
+ }
32
+ else if (arg.startsWith('-') || targetVersion !== undefined)
33
+ throw new NpmUpdateError('NPM_UPDATE_ARGUMENT', 'Use prodero update [exact-version] [--yes].');
34
+ else
35
+ targetVersion = arg;
36
+ }
37
+ const write = dependencies.write ?? console.log;
38
+ const confirm = dependencies.confirm ?? terminalConfirmation;
39
+ try {
40
+ const result = await runNpmUpdate({ targetVersion, yes }, { ...dependencies, write, confirm, interactive: dependencies.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY) });
41
+ write(result.message);
42
+ return result.exitCode;
43
+ }
44
+ catch (error) {
45
+ if (error instanceof NpmUpdateError) {
46
+ write(`${error.code}: ${error.message}`);
47
+ return error.exitCode;
48
+ }
49
+ write('NPM_UPDATE_FAILED: Cannot complete or verify this update. Inspect this npm installation before retrying.');
50
+ return 1;
51
+ }
52
+ }
53
+ //# sourceMappingURL=update-command.js.map
@@ -0,0 +1,4 @@
1
+ /** Shared copy only: safe to bundle into the App; no execution or host access. */
2
+ export const MANUAL_UPDATE_COMMAND = 'prodero update';
3
+ export const UPDATE_INSTRUCTIONS = 'Finish the current work and close your MCP hosts. In a terminal, run prodero update and review its plan before approving installation. After the terminal reports completion, restart and reconnect your hosts. Open Prodero again and review any project knowledge migration separately. This chat action only shows instructions; it has not installed or staged a runtime, refreshed project integration, or changed your development runtime selection.';
4
+ //# sourceMappingURL=update-instructions.js.map