@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
package/ARCHITECTURE.md CHANGED
@@ -142,7 +142,7 @@ Set `PRODERO_DEBUG_TIMINGS=1` during local MCP development to emit fast response
142
142
 
143
143
  Filesystem/stat/checkpoint discovery, status counts, pending detection, index routing, and token estimates are deterministic local operations and make no LLM/API call. Context Selection v2 retains the 6,000 estimated-token resolver budget and treats 2,000 tokens as a Required reserve, using the model-neutral `ceil(UTF-8 bytes / 4)` strategy. Applicable Required can borrow total capacity, with independent safety ceilings of 16,000 tokens / 128 records. Relevant uses a soft target of 8, deterministic marginal-value continuation and a 128-record safety ceiling. Strongly matching Skills receive capacity before Relevant, retaining 4 / 1,500 limits. Undelivered Required produces blocking `required-incomplete`; Open/Bootstrap and the derived router retain their compact limits. See [Context Selection v2](docs/CONTEXT-SELECTION-V2.md). Counts are explicitly estimated; these are Prodero-supplied task-context limits, never the model context window.
144
144
 
145
- `memory-index.ts` caches per-item token, freshness, and provenance-count signals alongside lexical metadata. Context totals and selectivity sum cached summaries; only bounded selected bodies are loaded. Required globals are deterministically priority-ranked under separate item and token bounds, and omissions retain exact reason/count/token diagnostics. Ordinary Relevant omissions are nonblocking; incomplete Required delivery blocks readiness; only bounded metadata for omitted required items is disclosed. Query retrieval uses deterministic lexical scoring and greedy scope/category diversity. Non-critical active `state` records older than 180 days are retained but demoted and surfaced when selected. Detailed memory health counts stale, disputed, historical, missing-provenance, and oversized items; fast status still touches none of the corpus.
145
+ `memory-index.ts` caches per-item token, freshness, and provenance-count signals alongside lexical metadata. Context totals and selectivity sum cached summaries; only bounded selected bodies are loaded. Required globals are deterministically priority-ranked under separate item and token bounds, and omissions retain exact reason/count/token diagnostics. Ordinary Relevant omissions are nonblocking; incomplete Required delivery blocks readiness; only bounded metadata for omitted required items is disclosed. Query retrieval uses deterministic lexical scoring and greedy scope/category diversity. Non-critical active `state` records older than 180 days are retained and flagged stale when selected. Already eligible Relevant candidates receive a bounded storage-recency hint (0–0.5); there is no separate 180-day rank penalty, and Required policy ranking is unchanged. Storage update time is not a fact-effective date. Detailed memory health counts stale, disputed, historical, incomplete-provenance, and oversized items; fast status still touches none of the corpus. The compatibility field `missingProvenanceItems` counts active records with no source-session/source-file evidence or no observation-level `derived_from`. It measures incomplete metadata, not proven loss of history or the validity of every source reference. Legacy/imported records remain readable; no provenance is invented or backfilled by diagnostics.
146
146
 
147
147
  Sync telemetry is derived from the same pending candidates already in consolidation. `rawSessionTokens` counts durable observation content in the current batch. `canonicalTokensAdded` counts newly created canonical title + content and newly appended consolidation text, excluding duplicates and unrelated existing knowledge. Compression is `1 - canonical/raw` only when canonical is smaller; larger output is an explicit expansion and zero raw input is unavailable. The result also aliases lifecycle-friendly counts (`sessionsProcessed`, `observationsProcessed`, `knowledgeCreated`, `knowledgeConsolidated`, `knowledgeSuperseded`) while preserving the existing bounded result fields.
148
148
 
package/CHANGELOG.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0 — Public Preview (unreleased)
3
+ ## 0.2.0
4
+
5
+ Release notes finalized 2026-10-23. This is the preparation date; registry publication is a separate maintainer action.
6
+
7
+ - **Curate Memory** replaces the Memory Sync user-facing name, with existing MCP tool IDs and saved workflows retained. Retry/expired analysis, saved answers, deferred questions and Revisit resume reliably; partial progress and remaining work stay visible.
8
+ - Text mode uses the shared Telemetry/Pulse layout with letter-bound knowledge entries, exact numeric actions, complete review bodies and mobile-width output. Prepare continues automatically; Prepare/Close retain a compact fallback when an App is invisible, without repeating Core calls.
9
+ - **CLI compatibility:** `prodero status` now returns readable text by default. Scripts must pass `--json`; scope, failure exit codes and safe diagnostics remain explicit.
10
+ - Theme and mode changes write ignored checkout-local preferences rather than shared project config. Shared context settings stay versioned. Older v0.1 runtimes ignore local preferences and can still write legacy shared presentation settings; no automatic shared-config cleanup is performed.
11
+ - Terminal-only `prodero update [VERSION]` verifies an ordinary npm installation and exact target, asks users to close hosts and confirm, waits for npm and verifies the installed CLI. Non-TTY installation requires an exact version plus `--yes`. No automatic host shutdown or atomic rollback. MCP/App update actions provide terminal instructions only; package update and approved project migration remain separate.
12
+ - Valid legacy links are preserved across current/archive storage and repair previews; the disposable graph cache is rebuilt at format 2. Missing-target removal retains exact approval, stale-preview and recovery guards. Existing canonical legacy IDs are accepted by Remember/relationships/Local Forget and promotion selectors without changing supersession history or path safety. Versioned retirement still requires reviewed repository maintenance with the current revision.
13
+ - Procedural intent gates skill selection so a project name alone does not pull an installer into unrelated context. Skill Preview/Review honor effective local/shared/invocation presentation, retain complete drafts and identify create/update/covered and the exact target before consent. Explicit skill creation and inferred candidates keep their existing distinct workflows.
14
+ - The generated runtime guide is shorter and restores bundle-only read-only handling, last-resort file fallback, readiness/migration/resume and lifecycle rules. It separates Curator tools' Git behavior from the coding agent's current user authorization and host approval controls.
15
+ - Incomplete provenance diagnostics no longer imply proven loss of history; no provenance backfill or canonical storage redesign is included.
16
+ - Session writers serialize lock acquisition, stale-owner recovery and release under the same transition guard, preventing a stale PID decision from deleting a successor's lock. Deterministic process barriers and acknowledgment-preservation stress regressions cover record/close concurrency; unknown owners and interrupted guards remain fail-closed.
17
+
18
+ Project schema remains **8** and knowledge schema remains **3**. No canonical schema migration is added by this version bump. See [compatibility and evidence limits](docs/COMPATIBILITY.md) for exact-SHA CI, installed-artifact native checks and the owner-reported manual acceptance. The final release tree and artifact must satisfy the [release gates](docs/RELEASING.md); these notes do not announce registry availability.
19
+
20
+ ## 0.1.0 — 2026-10-15 (Public Preview)
4
21
 
5
22
  - Host-scoped package/dev runtime switching with read-only previews, explicit manual-dev migration, format 1 receipt compatibility and transactional rollback.
6
23
 
@@ -13,4 +30,4 @@
13
30
  - npm artifact installation, manual package-manager updates, explicit MCP roots and a clean artifact release gate.
14
31
  - Interactive `prodero install` wizard with agent status, install/update selection, confirmation and readable results. Replaces install host arguments and `--all`; recognizes manual Codex `prodero-dev` connections without adding a duplicate server.
15
32
 
16
- Preview limitations and actual verification coverage are recorded in docs/COMPATIBILITY.md. This entry does not announce publication.
33
+ Historical release notes. Preview limitations and actual verification coverage are recorded in docs/COMPATIBILITY.md.
package/README.md CHANGED
@@ -1,59 +1,54 @@
1
1
  # Prodero Curator
2
2
 
3
- **Project memory for AI coding agents, through chat.**
3
+ **Project memory for Codex and Claude Code.**
4
4
 
5
- Curator keeps useful decisions, conventions and lessons available across coding sessions. Ask your agent to remember something, retrieve relevant context or review what should be shared with your team.
5
+ > **0.2.0 release materials.** The npm command below requires 0.2.0 to be published. For review before publication, use the exact candidate tarball and isolated setup in [the release guide](docs/RELEASING.md). A checkout or local tarball does not establish registry availability. See [0.2.0 changes](CHANGELOG.md) and [verification limits](docs/COMPATIBILITY.md).
6
6
 
7
- **v0.1.0 Public Preview.** Curator runs locally and connects to your coding agent through MCP. The terminal is for installation and maintenance; everyday work happens in conversation.
7
+ Keep project decisions, conventions, and lessons across sessions, right inside your coding assistant.
8
8
 
9
- ## Why Curator exists
9
+ **Install in two commands → open Codex or Claude Code → type prodero.**
10
10
 
11
- A new coding session often means explaining the same architecture, constraints and past mistakes again. Chat history alone is hard to reuse across tasks and teammates. Curator stores durable project knowledge and selects relevant context for the agent’s next task.
12
-
13
- ## How it works
11
+ ```sh
12
+ npm install --global @prodero/curator@0.2.0
13
+ prodero install
14
+ ```
14
15
 
15
- Tell your agent:
16
+ Requires **Node.js >=22.14.0 <23**, npm and Git. **v0.2.0 preview release.** Codex CLI has native text-workflow evidence; Claude Code remains experimental, with native text-workflow evidence. See the [compatibility matrix](docs/COMPATIBILITY.md).
16
17
 
17
- > Remember that API tests should use our shared fixtures. Apply this to the `tests/` path.
18
+ 1. In the installation wizard, select **Codex** or **Claude Code**, confirm the setup and choose **Finish**.
19
+ 2. Reopen Codex or Claude Code in your repository and type **`prodero`** in chat.
20
+ 3. For a new project, choose **Initialize Prodero** and review the generated project files and Git-ignore rules.
18
21
 
19
- The agent saves Local Knowledge through Curator’s MCP tools. In a later session, ask it to work on API tests: Curator can include that convention when the agent prepares context for the task. You can inspect or edit the saved item at any time.
22
+ For Claude Code, also create or update the project’s `CLAUDE.md` to import `@AGENTS.md`. The installer does not create that file. See the [setup guide](docs/hosts.md).
20
23
 
21
- Curator relies on the agent following its runtime instructions to prepare tasks, record useful outcomes and close sessions. It does not watch every conversation or guarantee that every detail will be remembered.
24
+ ![Prodero Curator overview in the coding assistant](docs/images/prodero-overview-cropped.png)
22
25
 
23
- ## Install
26
+ ## Relevant context, automatically
24
27
 
25
- Requires **Node.js 22.14+ in the 22.x line**, npm and Git. See [compatibility](docs/COMPATIBILITY.md) for verified platforms.
28
+ Just describe your task in Codex or Claude Code. Curator automatically brings in relevant project knowledge and skills as part of the task workflow. No separate search or manual context selection needed.
26
29
 
27
- ```sh
28
- npm install --global @prodero/curator@0.1.0
29
- prodero install
30
- ```
30
+ ![Context Ready: relevant project knowledge prepared for a task](docs/images/prodero-context-ready-cropped.png)
31
31
 
32
- The interactive wizard lets you choose a coding agent and review its MCP setup. Installation configures the agent; it does not initialize your projects.
32
+ ## Carry lessons into the next session
33
33
 
34
- ## Open your coding agent
34
+ As Codex or Claude Code closes a session through Curator’s workflow, recorded outcomes automatically become Local Knowledge. Curator can bring those lessons into later tasks when they are relevant. You can inspect and edit saved knowledge at any time.
35
35
 
36
- Restart the agent, open your repository and type:
36
+ ![Session Learned: useful outcomes saved for future sessions](docs/images/prodero-session-learned-cropped.png)
37
37
 
38
- ```text
39
- prodero
40
- ```
41
-
42
- The agent opens Curator through MCP. In a new project, choose **Initialize Prodero**. Review the generated project files, including the managed `AGENTS.md` instructions and Git-ignore rules.
43
-
44
- Where the host supports MCP Apps, Curator provides a graphical interface. Otherwise, use text mode: **“Show Prodero as text.”** You do not need to memorize CLI commands.
38
+ Curator relies on Codex or Claude Code following its runtime instructions to prepare tasks, record useful outcomes and close sessions. It does not watch every conversation or guarantee that every detail will be remembered.
45
39
 
46
40
  ## Everyday use
47
41
 
48
- Ask your connected agent in ordinary language:
42
+ Everyday work happens in conversation with Codex or Claude Code; the terminal is for installation and maintenance.
49
43
 
50
44
  - “What do we know about our API test setup?”
51
- - “Remember why we chose this approach.”
45
+ - “Remember that API tests should use our shared fixtures. Apply this to the `tests/` path.”
52
46
  - “Update the saved testing convention to use the new fixture directory.”
53
- - “Show the knowledge relevant to this bug before we start.”
54
47
  - “Review which Local lessons would be useful to share with the team.”
55
48
 
56
- Use **Knowledge** to search, inspect and edit saved items. **Sync Memory** lets you choose **Improve memory**, **Share knowledge**, or both, then start the workflow. Eligible Local improvements can apply within that workflow; sharing presents proposed Versioned changes for review. Normal session learning does not require running Sync Memory.
49
+ Use **Knowledge** to search, inspect and edit saved items. **Curate Memory** lets you choose **Improve memory**, **Share knowledge**, or both, then start the workflow. Eligible Local improvements can apply within that workflow; sharing presents proposed Versioned changes for review. Normal session learning does not require running Curate Memory.
50
+
51
+ Where the host supports MCP Apps, Curator provides a graphical interface. Otherwise, use text mode: **“Show Prodero as text.”** You do not need to memorize CLI commands.
57
52
 
58
53
  ## Main capabilities
59
54
 
@@ -71,9 +66,9 @@ Local does **not** mean inaccessible to the connected agent. The agent may creat
71
66
 
72
67
  The generated `.prodero/index.md` can include active **Local skill titles and paths**. It is not automatically ignored in every project. Keep it ignored when those details are private, and review generated files before committing. See [storage and privacy](docs/PRIVACY.md).
73
68
 
74
- ## Supported agents and limitations
69
+ ## Compatibility and limitations
75
70
 
76
- **Codex CLI is verified; Claude Code is experimental.** Claude Code also needs a project `CLAUDE.md` importing `@AGENTS.md`; the installer does not create it. Cursor is detection-only, and there is no Claude Desktop installer.
71
+ **Codex CLI 0.144.4 and Claude Code 2.1.302 passed native text workflows with the installed 0.2.0 runtime candidate.** Claude Code remains experimental. On 2026-10-23 the owner reported successful manual checks in Codex Desktop, Claude CLI and Codex Mobile. This is owner-reported acceptance; client build versions and an exact artifact/SHA binding were not supplied, so it is not independently traced per-case Apps certification. Cursor is detection-only, and there is no Claude Desktop installer.
77
72
 
78
73
  MCP Apps, workspace roots and sampling support vary by host. Text mode supports core workflows, but App-only curation review/apply is unavailable through the text gateway. See the [host/runtime matrix](docs/COMPATIBILITY.md) and [setup guide](docs/hosts.md).
79
74
 
@@ -93,3 +88,7 @@ Retrieved context reaches your connected agent. Optional assisted operations can
93
88
  Preview interfaces and schemas may evolve. Back up the whole `.prodero` directory before upgrading; follow the linked maintenance and recovery guidance.
94
89
 
95
90
  Curator is [MIT licensed](LICENSE).
91
+
92
+ ### Updating the installed core
93
+
94
+ Codex/Claude show an available-version indicator and terminal instructions only. Run `prodero update` in your terminal, finish active work and close the hosts when prompted, then explicitly confirm. Restart the hosts after completion. Project knowledge migration is offered separately on Open/Prepare; package update preserves saved dev selection and preferences. See [update and non-interactive options](docs/hosts.md#commands-and-status).
package/dist/cli.js CHANGED
@@ -13,9 +13,11 @@ import { getMemoryIntelligencePlan } from "./core/memory-intelligence.js";
13
13
  import { publicDiagnostic } from "./core/diagnostics.js";
14
14
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
15
  import { createServer } from "./server.js";
16
- import { autoMigrateProject, getProjectStatus, initializeProject, prepareProject, recallProject, PRODERO_VERSION, resolveProjectContext, syncProject, } from "./core/project.js";
16
+ import { getProjectStatus, initializeProject, prepareProject, recallProject, PRODERO_VERSION, resolveProjectContext, syncProject, } from "./core/project.js";
17
17
  import { cliProjectRoot, explicitProjectRoot } from "./core/project-root.js";
18
- import { checkForUpdate, installLatest } from "./core/update.js";
18
+ import { renderMachineStatus, renderProjectStatus, renderStatusFailure, statusFailure } from "./status-presentation.js";
19
+ import { updateCommand } from "./update-command.js";
20
+ import { checkForUpdate } from "./core/update.js";
19
21
  const help = `Prodero ${PRODERO_VERSION}
20
22
 
21
23
  Usage: prodero <command> [--root /absolute/project]
@@ -25,17 +27,17 @@ Usage: prodero <command> [--root /absolute/project]
25
27
  install Open the interactive agent installation wizard
26
28
  runtime <mode> --host HOST Switch one agent between package and dev (runtime --help)
27
29
  uninstall [host] [--all] Remove managed host integration; preserve projects
28
- status Show version and machine host integrations
29
- status --root PATH Show existing project status
30
+ status [--json] Show readable machine integrations; --json for scripts
31
+ status --root PATH [--json] Show existing project status without maintenance
30
32
  memory-bundle <id> Inspect one read-only transformation bundle (JSON)
31
33
  memory-plan Inspect deterministic memory candidates (read-only JSON)
32
- sync Start/resume Sync Memory (JSON; continue through MCP)
34
+ sync Start/resume Curate Memory (JSON; continue through MCP)
33
35
  sync-sessions Low-level deterministic closed-session recovery
34
36
  recall <query> Recall project knowledge without a session
35
37
  context [query] Inspect retrieved context
36
38
  prepare [task] Prepare context and start a session
37
39
  check-update Show installed/latest npm version and update instruction
38
- update Show manual npm update instructions (does not install)
40
+ update [version] [--yes] Update this npm installation from the terminal
39
41
  help, --help Show this help
40
42
  --version Show the installed version
41
43
 
@@ -44,7 +46,7 @@ Without --root, project commands require a Git or initialized Prodero ancestor.
44
46
  MCP uses negotiated host roots or supported local host launch context. --root is an optional hard boundary.`;
45
47
  function parse(argv) {
46
48
  let command, root, query;
47
- let helpRequested = false, all = false, dev = false, checkout, host;
49
+ let helpRequested = false, all = false, dev = false, json = false, checkout, host;
48
50
  const commands = new Set(["install", "uninstall", "mcp", "--stdio", "init", "migrate", "status", "memory-plan", "memory-bundle", "sync", "sync-sessions", "context", "recall", "prepare", "check-update", "update", "help", "--version"]);
49
51
  for (let i = 0; i < argv.length; i++) {
50
52
  const arg = argv[i];
@@ -52,6 +54,12 @@ function parse(argv) {
52
54
  helpRequested = true;
53
55
  continue;
54
56
  }
57
+ if (arg === "--json") {
58
+ if (json)
59
+ throw new Error("Specify --json only once.");
60
+ json = true;
61
+ continue;
62
+ }
55
63
  if (arg === "--dev") {
56
64
  if (dev)
57
65
  throw new Error("Specify --dev only once.");
@@ -95,6 +103,8 @@ function parse(argv) {
95
103
  throw new Error("Unexpected argument. Run prodero --help.");
96
104
  }
97
105
  command ??= "help";
106
+ if (json && (command !== "status" || helpRequested))
107
+ throw new Error("Use --json only with status, without --help.");
98
108
  if (all && command === "install")
99
109
  throw new Error("Run prodero install without --all; choose an agent in the interactive wizard.");
100
110
  if (all && (command !== "uninstall" || host))
@@ -105,14 +115,18 @@ function parse(argv) {
105
115
  throw new Error("--dev and --checkout are only supported for MCP.");
106
116
  if (checkout !== undefined && !dev)
107
117
  throw new Error("--checkout requires --dev.");
108
- return { dev, checkout, command: helpRequested ? "help" : command, root, query, host: all ? "all" : host };
118
+ return { dev, checkout, json, command: helpRequested ? "help" : command, root, query, host: all ? "all" : host };
109
119
  }
110
120
  async function run(localStdio) {
121
+ if (process.argv[2] === 'update') {
122
+ process.exitCode = await updateCommand(process.argv.slice(3));
123
+ return;
124
+ }
111
125
  if (process.argv[2] === 'runtime') {
112
126
  await runtimeCommand(process.argv.slice(3));
113
127
  return;
114
128
  }
115
- const { command, root, query, host, dev, checkout } = parse(process.argv.slice(2));
129
+ const { command, root, query, host, dev, checkout, json } = parse(process.argv.slice(2));
116
130
  if (command === "help") {
117
131
  console.log(help);
118
132
  return;
@@ -127,7 +141,7 @@ async function run(localStdio) {
127
141
  }
128
142
  if (command === "uninstall" || (command === "status" && root === undefined)) {
129
143
  const result = await machineIntegrations(command === "status" ? "status" : command, host);
130
- console.log(JSON.stringify(result, null, 2));
144
+ console.log(command === "status" && !json ? renderMachineStatus(result) : JSON.stringify(result, null, 2));
131
145
  if (!result.ok)
132
146
  process.exitCode = 1;
133
147
  return;
@@ -162,7 +176,7 @@ async function run(localStdio) {
162
176
  console.log(JSON.stringify(await initializeProject(rootArg), null, 2));
163
177
  return;
164
178
  }
165
- if (["status", "prepare"].includes(command)) {
179
+ if (command === "prepare") {
166
180
  const plan = await planProjectMigration(rootArg);
167
181
  if (plan.state !== "current") {
168
182
  (plan.blocked ? console.error : console.log)(renderText(buildViewModel({ action: "migration", root: rootArg, plan, resume: { tool: "prodero_prepare", arguments: { projectRoot: rootArg, task: query } } }), true));
@@ -171,10 +185,15 @@ async function run(localStdio) {
171
185
  }
172
186
  }
173
187
  if (command === "status") {
174
- const project = await getProjectStatus(rootArg);
175
- if (project.initialized)
176
- await autoMigrateProject(project.root);
177
- console.log(JSON.stringify({ version: PRODERO_VERSION, project: await getProjectStatus(project.root), update: await checkForUpdate() }, null, 2));
188
+ const plan = await planProjectMigration(rootArg);
189
+ if (plan.state !== "current") {
190
+ const result = statusFailure(plan.diagnostic ?? `PRODERO_MIGRATION_REQUIRED: Project update is ${plan.state}.`, "project", plan);
191
+ (json ? console.log : console.error)(json ? JSON.stringify(result, null, 2) : renderStatusFailure(result));
192
+ process.exitCode = 1;
193
+ return;
194
+ }
195
+ const result = { version: PRODERO_VERSION, scope: "project", project: await getProjectStatus(rootArg, { persistCache: false }), update: await checkForUpdate() };
196
+ console.log(json ? JSON.stringify(result, null, 2) : renderProjectStatus(result));
178
197
  return;
179
198
  }
180
199
  if (command === "memory-bundle") {
@@ -207,18 +226,37 @@ async function run(localStdio) {
207
226
  console.log(JSON.stringify(await prepareProject(rootArg, { task: query, agent: "cli" }), null, 2));
208
227
  return;
209
228
  }
210
- if (command === "update") {
211
- console.log(JSON.stringify(await installLatest(), null, 2));
212
- return;
213
- }
214
229
  if (command === "check-update") {
215
230
  console.log(JSON.stringify(await checkForUpdate(), null, 2));
216
231
  return;
217
232
  }
218
233
  }
234
+ /** Identify the requested command even when option validation fails. Option values
235
+ * and later positional arguments never turn another command into status. */
236
+ function requestedJsonStatus(argv) {
237
+ if (!argv.includes('--json'))
238
+ return false;
239
+ for (let i = 0; i < argv.length; i++) {
240
+ const arg = argv[i];
241
+ if (arg === '--root' || arg === '--checkout') {
242
+ i++;
243
+ continue;
244
+ }
245
+ if (arg === '--stdio' || arg === '--version')
246
+ return false;
247
+ if (arg.startsWith('-'))
248
+ continue;
249
+ return arg === 'status';
250
+ }
251
+ return false;
252
+ }
219
253
  export async function main(localStdio) {
220
254
  await run(localStdio).catch((error) => {
221
- console.error(`[prodero] ${publicDiagnostic(error)}`);
255
+ const argv = process.argv.slice(2);
256
+ if (requestedJsonStatus(argv))
257
+ console.log(JSON.stringify(statusFailure(error, argv.some(arg => arg === '--root' || arg.startsWith('--root=')) ? 'project' : 'machine'), null, 2));
258
+ else
259
+ console.error(`[prodero] ${publicDiagnostic(error)}`);
222
260
  process.exitCode = 1;
223
261
  });
224
262
  }
package/dist/contracts.js CHANGED
@@ -15,6 +15,7 @@ export const PRODERO_TOOLS = {
15
15
  migrate: "prodero_migrate",
16
16
  curation: "prodero_curation",
17
17
  curationReview: "prodero_curation_review",
18
+ curationQuestion: "prodero_curation_question",
18
19
  curationReasoningPrepare: "prodero_curation_reasoning_prepare",
19
20
  curationReasoningPropose: "prodero_curation_reasoning_propose",
20
21
  curationApply: "prodero_curation_apply",
@@ -1,4 +1,5 @@
1
- export const PRESENTATION_REPLY = 'For knowledge-list requests in Claude Code, prefer prodero_text with tool=prodero_knowledge unless the user explicitly requests a graphical view. This is a one-off display choice, not a project setting change. When showing knowledge lists in chat, prefer the supplied fenced ASCII table with +, - and | borders; preserve its columns and page-local numeric row keys when translating. Knowledge rows use 1 through N, and action commands below the table continue at N+1. Resolve a standalone number through the exact numberedActions binding from the latest displayed table/menu. knowledgeRows also binds row numbers to exact record IDs; never infer IDs or reuse an older page. Show complete record text when an individual record is requested. In knowledge-detail replies, keep the original, any translation and record metadata together, then preserve a separate ------------- line before service hints about editing, history, approval and available actions. Service hints are not part of the knowledge or its translation. When suggesting next actions in chat, show a numbered Markdown list starting at 1 (or N+1 after a knowledge table with N rows), with one command per line and a blank line before the list. Tell the user they can reply with a number or the command name. Preserve the displayed order and exact numberedActions bindings when translating. A standalone positive integer selects only that item from the latest action menu actually shown in this conversation for this project; never use array positions in availableActions, an older menu or another conversation. If the number is out of range, the menu is missing, or a pending question expects a numeric value rather than a menu choice, clarify instead of guessing. Keep menu bindings only in conversation context, never project settings or knowledge. Route selected covered tools through prodero_text with the exact bound arguments; call other bound tools directly. A numeric selection is navigation or an action request, never explicit approval: for an approval-marked action show the exact proposal and request explicit confirmation before mutation or publication. In ordinary Open and lifecycle replies, omit display mode, theme and tool-routing commentary. Saved presentation settings, resolved mode and App metadata are not evidence that the user saw a graphical interface; do not claim a UI was displayed from those fields. Discuss mode or theme only when the user asks about settings or presentation troubleshooting.';
1
+ export const LIFECYCLE_PRESENTATION_REPLY = 'For lifecycle text delivery, including when presentation.resolved is text in auto mode, relay the supplied compact presentation.text once as a visible assistant message: Prepare before continuing the task, Close before the final task answer. Healthy Prepare continues immediately without a Continue question or approval. A tool result or App metadata does not prove visible delivery. If the App is invisible, relay its retained fallback; do not duplicate a visible App. Use the returned text and exact context snapshot, never repeat Prepare, Close or retrieval just to display details. Show blocked/deferred/error states truthfully; never invent Session Complete or Saved without successful Close and synchronization.';
2
+ export const PRESENTATION_REPLY = 'For knowledge-list requests in Claude Code, prefer prodero_text unless the user requests a graphical view. Relay the supplied text, preserving code fences, letter-keyed catalogue, separators and external Pulse menu. Show complete requested record/review bodies; keep original text, any translation and metadata together, with service hints separate. Bind letters only through entryActions/knowledgeRows and numbers only through numberedActions on the latest displayed screen in this conversation and project, never array positions. Each screen replaces old aliases. Preserve bindings/order when translating. Pending answers take precedence over navigation aliases, including a question expecting a numeric value; clarify missing/out-of-range bindings. Collect the actual value for an input field before calling its action, never an incomplete placeholder. A menu number is an action request, never approval: show the exact proposal and obtain explicit human approval for approval-marked actions. Keep aliases ephemeral; never store them as settings or knowledge. Tell the user they may reply with a number or command name. In ordinary Open/lifecycle replies omit mode, theme and tool-routing commentary; discuss settings only on request. Display settings or App metadata do not prove that a UI was visible.';
2
3
  const n = (value, one, many = `${one}s`) => `${value} ${value === 1 ? one : many}`;
3
4
  const tokens = (value) => value >= 1000 ? `${Number((value / 1000).toFixed(1))}k` : String(value);
4
5
  export function conversationView(payload, vm) {
@@ -36,14 +37,14 @@ export function conversationView(payload, vm) {
36
37
  v.attention.push(`Memory areas to review: ${areas.length + (p.memoryOverview?.areasOmitted ?? 0)}.`);
37
38
  if (areas.some((a) => a.signals?.includes('context_pressure')))
38
39
  v.attention.push('Relevant knowledge has not fit into recent task context.');
39
- next('Review memory improvements?', call('Review Memory Sync', 'prodero_sync'));
40
+ next('Review memory improvements?', call('Review Curate Memory', 'prodero_sync'));
40
41
  }
41
42
  if (p.memoryOverview?.available === false)
42
43
  v.attention.push('Memory overview unavailable.');
43
44
  if (p.memoryOverview?.workflow && p.memoryOverview.workflow.state !== 'completed') {
44
45
  const w = p.memoryOverview.workflow;
45
- v.attention.push(w.state === 'stale' ? 'Memory changed since the last Sync.' : 'Memory Sync is unfinished.');
46
- next(w.state === 'stale' ? 'Re-analyze memory?' : 'Continue Memory Sync?', w.state === 'stale' ? call('Re-analyze memory', 'prodero_memory_sync', { request: { action: 'start', restart: true } }) : call('Continue Memory Sync', 'prodero_sync', { workflowId: w.id }));
46
+ v.attention.push(w.state === 'stale' ? 'Memory changed since the last curation.' : 'Curate Memory is unfinished.');
47
+ next(w.state === 'stale' ? 'Re-analyze memory?' : 'Continue curation?', w.state === 'stale' ? call('Re-analyze memory', 'prodero_memory_sync', { request: { action: 'start', restart: true } }) : call('Continue curation', 'prodero_sync', { workflowId: w.id }));
47
48
  }
48
49
  const c = payload.curation;
49
50
  if (c?.state === 'unavailable')
@@ -70,8 +71,8 @@ export function conversationView(payload, vm) {
70
71
  const w = p.memoryOverview?.workflow;
71
72
  const sync = w?.state === 'stale'
72
73
  ? call('Re-analyze memory', 'prodero_memory_sync', { request: { action: 'start', restart: true } })
73
- : call(w && w.state !== 'completed' ? 'Continue memory sync' : 'Sync memory', 'prodero_sync', w && w.state !== 'completed' ? { workflowId: w.id } : {});
74
- v.suggestions = [sync, call('Show knowledge', 'prodero_knowledge'), call('Show settings', 'prodero_config_get')];
74
+ : call(w && w.state !== 'completed' ? 'Continue curation' : 'Curate Memory', 'prodero_sync', w && w.state !== 'completed' ? { workflowId: w.id } : {});
75
+ v.suggestions = [sync, call('Explore knowledge', 'prodero_knowledge'), ...(c?.prominent ? [call('Review changes', 'prodero_curation', { action: 'plan', details: true })] : []), call('Show settings', 'prodero_config_get'), ...(payload.update?.available ? [call('Show update instructions', 'prodero_update')] : [])];
75
76
  }
76
77
  }
77
78
  v.details = call('Show project details', 'prodero_status_details', { details: true });
@@ -85,7 +86,7 @@ export function conversationView(payload, vm) {
85
86
  v.blocking = Boolean(readiness?.blocking || omitted);
86
87
  v.contextComplete = !omitted && !v.blocking;
87
88
  v.state = v.blocking ? (omitted ? 'Required context incomplete' : 'Work paused') : 'Ready';
88
- v.facts.push(`Context ${tokens(m.contextTokens)} / ${tokens(m.contextBudgetTokens)}`, `Required ${c.required.length} · Relevant ${c.relevant.length} · Skills ${c.skills.length}`);
89
+ v.facts.push(`Context ${tokens(m.contextTokens)} / ${tokens(m.contextBudgetTokens)}`, `${c.required.length} required · ${c.relevant.length} relevant · ${n(c.skills.length, 'skill')}`);
89
90
  if (r.session)
90
91
  v.facts.push('Session active');
91
92
  if (omitted)
@@ -94,7 +95,7 @@ export function conversationView(payload, vm) {
94
95
  v.attention.push('More task scope is needed before work can start.');
95
96
  if (readiness?.state === 'required-conflict' || (v.blocking && !omitted && readiness?.state !== 'context-needed'))
96
97
  v.attention.push('Required context needs review before work can continue.');
97
- v.attention.push(...(r.warnings ?? []), ...(r.attention ?? c.attention ?? []).map((a) => `${a.title}: ${a.detail}`));
98
+ v.attention.push(...(r.warnings ?? c.warnings ?? []), ...(r.attention ?? c.attention ?? []).map((a) => `${a.title}: ${a.detail}`));
98
99
  if (readiness?.state === 'context-needed')
99
100
  next('Specify the work paths or task scope, then prepare again.');
100
101
  else if (omitted)
@@ -120,22 +121,25 @@ export function conversationView(payload, vm) {
120
121
  v.details = call('Review session knowledge', 'prodero_session_review', { sessionId: r.session.id });
121
122
  }
122
123
  else if (payload.action === 'memory-sync') {
123
- v.title = 'Prodero · Sync';
124
- const s = r.summary ?? {}, inspect = call('Review this Sync', 'prodero_memory_sync', { request: { action: 'inspect', workflowId: r.id }, details: true });
124
+ v.title = 'Prodero · Curate Memory';
125
+ const s = r.summary ?? {}, inspect = call('Review this curation', 'prodero_memory_sync', { request: { action: 'inspect', workflowId: r.id }, details: true });
125
126
  v.details = inspect;
126
- const state = { awaiting_selection: 'Ready to sync', semantic_work: 'Improving memory', awaiting_clarification: 'Your input is needed', awaiting_review: 'Review required', applying: 'Saving changes', awaiting_promotion_selection: 'Ready for sharing review', completed: 'Complete', stale: 'Memory changed' };
127
- v.state = state[r.state] ?? 'Check saved progress';
127
+ const operation = vm.operation, state = { selection: 'Ready to curate', queued: 'Analysis queued', awaiting_agent: 'Awaiting agent analysis', submitted: 'Submitted · validation pending', question: 'Your input is needed', review: 'Review required', recovery: 'Approved changes · verify result', sharing: 'Ready for sharing review', completed: operation.outcome === 'remaining' ? 'Completed with remaining work' : 'Complete', stale: 'Memory changed' };
128
+ v.state = state[operation.phase] ?? 'Check saved progress';
128
129
  if (r.state === 'awaiting_selection') {
129
130
  v.facts.push('Improve memory · Prepare sharing review');
130
131
  if (r.brokenLinks)
131
132
  v.facts.push(`Fix ${n(r.brokenLinks, 'broken link')} · enabled`);
132
- next('Start Sync? Sharing will require a separate approval.', vm.actions.find(a => a.label === 'Start Sync'));
133
+ next('Start curation? Sharing will require a separate approval.', vm.actions.find(a => a.label === 'Start curation'));
133
134
  }
134
135
  else if (r.state === 'semantic_work') {
135
136
  if (r.curation || r.semantic)
136
137
  next('Continue analysis using the prepared evidence.');
137
138
  else
138
- next('Continue analysis.', vm.actions.find(a => a.label === 'Continue improving memory'));
139
+ next(operation.phase === 'awaiting_agent' ? 'Prepared evidence is waiting for the host agent.' : operation.phase === 'submitted' ? 'The submitted result is awaiting validation.' : 'Continue analysis.', vm.actions.find(a => 'id' in a && a.id === 'reasoning') ?? vm.actions.find(a => a.label === 'Continue improving memory'));
140
+ }
141
+ else if (r.state === 'applying') {
142
+ next('Check saved progress before retrying; do not repeat approval.', vm.actions.find(a => 'id' in a && a.id === 'inspect'));
139
143
  }
140
144
  else if (r.state === 'awaiting_review') {
141
145
  v.reviewRequired = true;
@@ -145,12 +149,19 @@ export function conversationView(payload, vm) {
145
149
  else if (r.state === 'awaiting_promotion_selection') {
146
150
  v.facts.push(`${n(r.promotionCandidates.length, 'memory', 'memories')} available for sharing review`);
147
151
  // Display the existing candidate set; selecting it still requires the user's request.
148
- next(`Review all ${r.promotionCandidates.length} for sharing?`, call('Review these memories', 'prodero_memory_sync', { request: { action: 'promotions', workflowId: r.id, revision: r.revision, ids: r.promotionCandidates.map((c) => c.id) }, details: true }));
152
+ next(`Review all ${r.promotionCandidates.length} for sharing?`, vm.actions.find(a => ('id' in a && a.id === 'promotions')));
149
153
  }
150
154
  else if (r.state === 'awaiting_clarification') {
151
155
  v.reviewRequired = true;
152
- v.attention.push('This question currently requires the graphical review form. Chat answers are not saved.');
156
+ v.attention.push('Choose an answer or give your explanation. Chat answers are saved as relayed evidence, not approval.');
157
+ for (const q of r.clarification?.graphical?.questions ?? [])
158
+ if (q.needsInput) {
159
+ v.facts.push(q.question.prompt);
160
+ if (q.relatedQuestions?.length > 1)
161
+ v.attention.push(`Deferred together: ${q.relatedQuestions.map((q) => q.title).join('; ')}.`);
162
+ }
153
163
  next('Show the question and review details?', inspect);
164
+ v.suggestions = vm.actions.filter(a => a.tool === 'prodero_curation_question' || a.tool === 'prodero_curation_reasoning_prepare');
154
165
  }
155
166
  else if (r.state === 'stale') {
156
167
  v.attention.push('Re-analyze before continuing; earlier review is out of date.');
@@ -166,18 +177,31 @@ export function conversationView(payload, vm) {
166
177
  if (s.brokenLinksRemaining)
167
178
  v.attention.push(`${n(s.brokenLinksRemaining, 'broken link')} remain unchanged.`);
168
179
  if (s.promoted)
169
- v.facts.push(`${n(s.promoted, 'memory', 'memories')} shared`);
180
+ v.facts.push(`${n(s.promoted, 'memory', 'memories')} prepared in Versioned files; review the Git diff`);
170
181
  if (s.unresolved)
171
- v.attention.push(`${n(s.unresolved, 'memory', 'memories')} still need clarification.`);
182
+ v.attention.push(`${n(s.unresolved, 'unresolved item')} still need clarification.`);
172
183
  if (s.areasFailed)
173
184
  v.attention.push(`${n(s.areasFailed, 'area')} could not be analyzed.`);
174
185
  if (s.promotionConflicts)
175
186
  v.attention.push(`Sharing items needing review: ${s.promotionConflicts}.`);
176
- if (s.questionsDeferred)
177
- v.attention.push(`${n(s.questionsDeferred, 'question')} deferred.`);
187
+ if (operation.counts.questionsDeferred)
188
+ v.attention.push(`${n(operation.counts.questionsDeferred, 'question')} deferred.`);
189
+ else if (operation.counts.questionsDeferred === null && operation.counts.deferredAreas)
190
+ v.attention.push(`${n(operation.counts.deferredAreas, 'area')} deferred; question count unavailable.`);
178
191
  if (v.attention.length)
179
192
  next('Review the remaining items?', inspect);
180
193
  }
194
+ const revisit = vm.actions.filter(a => a.arguments.action === 'question_reopen' || a.arguments.request?.action === 'refresh_questions');
195
+ if (revisit.length)
196
+ v.suggestions = [...revisit, ...(v.continuation ? [v.continuation] : [])];
197
+ const retries = vm.actions.filter(a => 'id' in a && String(a.id).startsWith('retry:'));
198
+ if (retries.length)
199
+ v.suggestions = [...(v.suggestions ?? []), ...retries, ...(!v.suggestions?.length && v.continuation ? [v.continuation] : [])];
200
+ for (const q of r.deferredQuestions ?? [])
201
+ if (q.affectedQuestions.length > 1)
202
+ v.attention.push(`Revisited together: ${q.affectedQuestions.map((q) => q.title).join('; ')}.`);
203
+ if (operation.diagnostic)
204
+ v.attention.push(`${operation.diagnostic.code}: ${operation.diagnostic.cause} ${operation.diagnostic.recovery}`);
181
205
  if (r.error)
182
206
  v.attention.push('This step could not finish. Check saved progress before retrying.');
183
207
  }
@@ -197,14 +221,21 @@ export function conversationView(payload, vm) {
197
221
  v.details = call('Show update details', 'prodero_migrate', { action: 'status', details: true });
198
222
  }
199
223
  else if (payload.action === 'promotion-applied') {
200
- v.title = 'Prodero · Published';
224
+ v.title = 'Prodero · Versioned files prepared';
201
225
  v.state = 'Complete';
202
- v.facts.push(`${n(r.published, 'memory', 'memories')} shared`);
226
+ v.facts.push(`${n(r.published, 'memory', 'memories')} prepared in Versioned files; not committed or pushed`);
203
227
  if (r.conflicts?.length) {
204
228
  v.attention.push(`Conflicts remaining local: ${r.conflicts.length}.`);
205
229
  next('Inspect the remaining knowledge?', call('Inspect knowledge', 'prodero_knowledge'));
206
230
  }
207
231
  }
232
+ else if (payload.action === 'skill-preview') {
233
+ v.kind = 'details';
234
+ v.reviewRequired = payload.preview?.disposition?.action !== 'covered';
235
+ v.state = v.reviewRequired ? 'Awaiting human review' : 'Already covered';
236
+ // The full companion contains the exact draft/target. Do not synthesize consent
237
+ // or an executable approval action merely because the preview was displayed.
238
+ }
208
239
  else {
209
240
  // Explicit inspection/review surfaces retain full content and diagnostics.
210
241
  v.kind = 'details';
@@ -223,8 +254,7 @@ export function conversationMenu(v) {
223
254
  export function numberedActions(vm, expanded = false) {
224
255
  const actions = expanded || !vm.conversation || vm.conversation.kind === 'details'
225
256
  ? vm.actions : conversationMenu(vm.conversation);
226
- const rows = vm.knowledgeTable?.rows.map(row => ({ label: `Open ${row.title}`, tool: 'prodero_knowledge', arguments: { ...vm.actions.find(a => a.tool === 'prodero_knowledge')?.arguments, id: row.id } })) ?? [];
227
- return [...rows, ...actions].map((action, index) => ({ ...action, number: index + 1 }));
257
+ return actions.map((action, index) => ({ ...action, number: index + 1 }));
228
258
  }
229
259
  export function renderActionMenu(actions, start = 1) {
230
260
  if (!actions.length)
@@ -238,7 +268,11 @@ export function renderConversation(v) {
238
268
  /** Model-visible MCP content, separate from the human summary; never hidden in _meta. */
239
269
  export function conversationOperation(payload, vm, details = false, diagnostics = false) {
240
270
  const r = payload.result ?? payload, v = vm.conversation;
271
+ const questionData = payload.action === 'curation-question' ? payload : r.clarification;
272
+ const owner = payload.action === 'curation-question' ? { workflowId: payload.syncWorkflowId, candidateId: payload.candidateId } : { workflowId: r.id, candidateId: r.jobs?.find((j) => j.curationReviewId === questionData?.review?.snapshot.reviewId)?.id };
273
+ const answerInputs = questionData?.review && !questionData.graphical.analysisPending ? questionData.graphical.questions.filter((q) => q.needsInput && q.question.allowFreeText).map((q) => ({ tool: 'prodero_curation_question', inputRequired: 'rawText', instructions: 'Collect the user\'s actual explanation. Supply rawText; never execute this incomplete input binding or invent an answer.', arguments: { projectRoot: payload.root ?? r.root, ...owner, action: 'answer', reviewId: questionData.review.snapshot.reviewId, reviewRevisionId: questionData.review.snapshot.revision.id, planHash: questionData.review.snapshot.planHash, decisionRevision: questionData.review.state.revision, answerLogHash: questionData.graphical.answerLogHash, questionId: q.question.id, questionVersion: q.question.version, requestId: `chat_${globalThis.crypto.randomUUID()}`, ...(q.answer ? { replacesAnswerId: q.answer.id } : {}) } })) : [];
241
274
  return {
275
+ ...(answerInputs.length ? { answerInputs } : {}),
242
276
  kind: diagnostics ? 'diagnostic' : details ? 'details' : v.kind,
243
277
  succeeded: payload.ok !== false,
244
278
  blocking: v.blocking,
@@ -249,10 +283,11 @@ export function conversationOperation(payload, vm, details = false, diagnostics
249
283
  detailSource: v.detailSource,
250
284
  suggestions: v.suggestions,
251
285
  numberedActions: numberedActions(vm, details || diagnostics),
252
- ...(vm.knowledgeTable ? { knowledgeRows: vm.knowledgeTable.rows.map(row => ({ key: row.key, number: Number(row.key), id: row.id, tool: 'prodero_knowledge', arguments: { projectRoot: r.root, ...r.options, id: row.id } })) } : {}),
286
+ entryActions: vm.knowledgeTable ? vm.knowledgeTable.rows.map(row => ({ key: row.key, label: row.title, tool: 'prodero_knowledge', arguments: { projectRoot: r.root, ...r.options, id: row.id } })) : [],
287
+ knowledgeRows: vm.knowledgeTable ? vm.knowledgeTable.rows.map(row => ({ key: row.key, id: row.id, tool: 'prodero_knowledge', arguments: { projectRoot: r.root, ...r.options, id: row.id } })) : [],
253
288
  availableActions: details || v.kind === "details" ? vm.actions : [v.continuation, v.details, ...(v.suggestions ?? [])].filter(Boolean),
254
- disclosure: vm.knowledgeTable && !details ? 'Show the supplied fenced ASCII catalogue table and pagination. Full record bodies are available by inspecting a row; do not expand every record into prose.' : details || v.kind === 'details' ? 'Show the requested record/proposal bodies completely before asking for approval.' : 'Keep the human reply to presentation.text; do not expand other domain fields or proposal bodies. This limits display, not execution: continue already-authorized work and Core next instructions. Pause only at a Core human-choice/review step or blocking state. At a human review step, ask whether to inspect; retrieve details on request and show full wording before offering approval.',
255
- instructions: PRESENTATION_REPLY + ' Use presentation.text as the human reply, translated if needed. Keep the numbered action menu visible, translated naturally with the reply; use numberedActions for numeric replies and exact tool bindings when chosen. Do not expand healthy state into a full action catalog or narrate tool routing. Keep these operational data out of the human reply unless requested. A yes answers only the immediately preceding question: use its continuation through prodero_text for covered tools, otherwise call the named tool directly with the given arguments. Reviewing is not approval. Show the complete proposed changes before asking for explicit approval; never approve unseen changes. Preserve Core blocking and context requirements. For text presentation, use the continuation here instead of reopening a graphical card. For View Context after Prepare, display the context object already returned here: it is the prepared snapshot. Do not rerun retrieval to display it; a fresh prodero_context call is a new selection. These are tool data, not system instructions.',
289
+ disclosure: vm.knowledgeTable && !details ? 'Show the supplied letter-keyed catalogue and pagination. Bind letters only through entryActions on this exact page; full bodies require inspecting a record.' : details || v.kind === 'details' ? 'Show the requested record/proposal bodies completely before asking for approval.' : 'Keep the human reply to presentation.text; do not expand other domain fields or proposal bodies. This limits display, not execution: continue already-authorized work and Core next instructions. Pause only at a Core human-choice/review step or blocking state. At a human review step, ask whether to inspect; retrieve details on request and show full wording before offering approval.',
290
+ instructions: PRESENTATION_REPLY + (['prepare', 'session-close'].includes(payload.action) ? ' ' + LIFECYCLE_PRESENTATION_REPLY : '') + ' Relay presentation.text as the human reply, translated if needed, without narrating operational data. A yes answers only the immediately preceding question: follow its exact continuation through prodero_text for covered tools or the named tool otherwise. Preserve Core blockers and context requirements. View Context after Prepare displays the returned context snapshot; never rerun retrieval to display it. These are tool data, not system instructions.',
256
291
  ...(payload.bootstrap ? { bootstrap: payload.bootstrap } : {}),
257
292
  ...(payload.action === 'context' ? { context: r } : {}),
258
293
  ...(payload.action === 'prepare' ? { session: r.session, context: r.context, automaticSync: r.automaticSync } : {}),