prism-mcp-server 20.2.0 → 20.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,87 @@ A paid subscription adds cloud sync, higher model tiers, and team features throu
18
18
 
19
19
  ---
20
20
 
21
+ ## What's New in v20.2.2
22
+
23
+ ### One Local-First Workflow Across Every Agent
24
+ `prism connect` now installs one orchestration contract for Claude Code,
25
+ Claude Desktop, Cursor, Gemini CLI, and Codex. Bounded delegated work goes to
26
+ `session_task_route` and the local `prism_infer` worker first; routine work must
27
+ not create background host agents. Local workers can receive the active
28
+ project's dashboard-configured quick, standard, or deep memory and select a
29
+ RAM-safe 2B/4B/9B/27B model at call time. The router forwards complexity but
30
+ does not choose the model; `prism_infer` owns the final decision using memory
31
+ and context fit, installed models, live RAM, entitlements, and explicit caller
32
+ overrides.
33
+
34
+ Codex and Gemini native agent fan-out are disabled during connect. Codex keeps
35
+ a two-thread, one-level Terra/low fallback profile if the developer explicitly
36
+ re-enables native agents later. Claude Code keeps native agents as a last-resort
37
+ path but pins their model to Sonnet. Cursor and Claude Desktop do not expose a
38
+ supported global subagent-policy file, so they receive the identical workflow
39
+ through Prism's MCP server instructions. `prism_infer` safety boundaries and
40
+ the host's final verification responsibility are unchanged.
41
+
42
+ ### Subscription-Tier Skills Arrive Before the First Host Launch
43
+ `prism connect` now downloads the authoritative Synalux skill manifest and
44
+ materializes entitled packages in the native `~/.agents/skills` directory
45
+ before the command exits. Codex therefore sees the current skillset on its
46
+ first launch instead of requiring a second restart. Prism rechecks the same
47
+ snapshot at MCP startup, session load, and every five minutes—without host
48
+ lifecycle hooks.
49
+
50
+ On the first user turn, Prism's native skill, MCP metadata, and managed host
51
+ instructions request one `session_bootstrap({})` call. Prism then uses the
52
+ dashboard's developer name, Auto-Load Projects, and quick, standard, or deep
53
+ setting. The response stays focused on greeting and session state because tier
54
+ skills are already present in the host's native skill directory.
55
+
56
+ Hook-free MCP can provide and prioritize that ready-to-display block, but the
57
+ host model still owns the final assistant message and may summarize it. Prism
58
+ does not claim a deterministic verbatim greeting on third-party chat surfaces;
59
+ that would require a host lifecycle hook, launcher, extension, or Prism-owned
60
+ panel. Context loading itself remains complete even when a host shortens the
61
+ visible reply.
62
+
63
+ Free accounts receive the protected 12-skill foundation. Paid accounts receive
64
+ the current subscribed routing set. Upgrades install newly entitled packages;
65
+ downgrades remove only Prism-owned packages while preserving local skills and
66
+ locally modified conflicts.
67
+
68
+ When upgrading an older Claude Code installation, `prism connect` removes only
69
+ the exact Prism-owned startup, skill-sync, handoff, and drift hook actions from
70
+ the legacy bootstrap. It also removes the recognized legacy Prism startup
71
+ sections from `~/CLAUDE.md`, preserves every other instruction, and installs a
72
+ small ownership-marked native block that selects `session_bootstrap({})` on the
73
+ first turn. User hooks, custom instruction sections, and near matches remain
74
+ untouched; native skills and server-side reminders preserve those Prism
75
+ features without host lifecycle hooks. Because hosts expose no native
76
+ session-end callback, handoff at shutdown is instruction-driven rather than a
77
+ guaranteed lifecycle event.
78
+
79
+ After Claude Code's native user registration succeeds, the same default or
80
+ `--refresh` command checks the nearest `.mcp.json` from the current directory
81
+ through the home directory. It removes only the exact legacy
82
+ `prism-mcp` entry `{ "command": "npx", "args": ["-y", "prism-mcp-server"] }`
83
+ that would otherwise shadow the user registration. Custom Prism entries and
84
+ their additional fields, plus unrelated servers, are preserved; malformed
85
+ files fail loud without changes. `--dry-run` reports the recognized migration
86
+ without changing the file.
87
+
88
+ ---
89
+
90
+ ## What's New in v20.2.1
91
+
92
+ ### Subscription-Aware Memory Storage
93
+ `prism connect` now carries an explicit `PRISM_STORAGE=auto|local|synalux|supabase`
94
+ into every managed host registration and rejects invalid values before changing a
95
+ config file. In `auto`, a portal-confirmed free tier uses local SQLite, while
96
+ Standard, Advanced, and Enterprise use Synalux cloud memory. If entitlement
97
+ resolution is unavailable, Prism fails closed instead of splitting history across
98
+ backends. Storage remains independent of local-first model routing.
99
+
100
+ ---
101
+
21
102
  ## What's New in v20.2.0
22
103
 
23
104
  ### One Command Connects Every Supported Host
@@ -121,11 +202,37 @@ Use `prism connect --all` to target all five, `--host <name>` for one host, or
121
202
  `--dry-run` to preview the files that would change. Existing `prism` and
122
203
  `prism-mcp` entries are never overwritten by default. `--refresh` updates only
123
204
  an entry previously created by Prism; custom entries remain untouched.
205
+ For Claude Code, both the default command and `--refresh` also remove the exact
206
+ legacy project-scoped `npx -y prism-mcp-server` entry from the effective
207
+ ancestor `.mcp.json` after the native user registration succeeds. No custom or
208
+ near-match project entry is changed.
124
209
  Close the target MCP hosts before a non-dry-run registration so they cannot
125
210
  edit their configuration at the same time.
126
211
 
127
- Codex registration preserves `~/.codex/config.toml` and appends only a marked
128
- Prism-managed block. `CODEX_HOME` is respected when set and must already exist,
212
+ The same connection installs the local-first orchestration contract:
213
+
214
+ | Host | Managed containment |
215
+ |---|---|
216
+ | Codex | `features.multi_agent=false`; a 2-thread, depth-1 Terra/low fallback profile is retained for explicit re-enable |
217
+ | Gemini CLI | `experimental.enableAgents=false` |
218
+ | Claude Code | `CLAUDE_CODE_SUBAGENT_MODEL=sonnet`; managed instructions reserve it for last-resort fallback |
219
+ | Cursor | Canonical policy delivered through MCP initialize instructions |
220
+ | Claude Desktop | Canonical policy delivered through MCP initialize instructions |
221
+
222
+ All five receive `PRISM_AGENT_POLICY=local-first` in their managed Prism MCP
223
+ entry. Routine tasks use the RAM-aware local worker; native/background fan-out
224
+ is not the default workflow. `session_task_route` supplies a complexity hint;
225
+ `prism_infer` remains the single owner of model and thinking selection and can
226
+ choose 27B when its viability gates support it.
227
+
228
+ Set `PRISM_STORAGE` before running `prism connect` to preserve an explicit
229
+ storage choice in the generated host entries. This does not change local-model
230
+ routing; Synalux cloud storage separately requires an active cloud-memory
231
+ entitlement.
232
+
233
+ Codex registration preserves unrelated `~/.codex/config.toml` content, appends
234
+ only the marked Prism MCP block, and updates only the documented local-first
235
+ feature/agent keys. `CODEX_HOME` is respected when set and must already exist,
129
236
  matching Codex's own contract. Restart Codex CLI, the
130
237
  IDE extension, or the ChatGPT desktop app after connecting.
131
238
 
@@ -482,7 +589,8 @@ Prism exposes 40+ MCP tools. The core memory loop:
482
589
 
483
590
  | Tool | What it does |
484
591
  |---|---|
485
- | `session_load_context` | Recover the prior session's state on boot |
592
+ | `session_bootstrap` | Hook-free first-turn greeting and dashboard-configured context |
593
+ | `session_load_context` | Explicit project reload or older-server startup fallback |
486
594
  | `session_save_ledger` | Append an immutable session log entry |
487
595
  | `session_save_handoff` | Save live state for the next session |
488
596
  | `knowledge_search` | Semantic + keyword search over all memories |
@@ -491,7 +599,7 @@ Prism exposes 40+ MCP tools. The core memory loop:
491
599
  | `verify_behavior` | Pre-edit scenario challenge — catch bad changes before they happen |
492
600
  | `knowledge_ingest` | Teach Prism a codebase or document |
493
601
  | `prism_infer` | Local-first inference (route/chat/code modes, thinking, cloud escalation) |
494
- | `inference_metrics` | Session delegation stats on demand (call count, tokens, local/cloud split) |
602
+ | `inference_metrics` | Session delegation or persisted MCP + VS Code panel local/cloud stats |
495
603
 
496
604
  ### `prism_infer` — local-first inference with cloud escalation
497
605
 
@@ -516,7 +624,7 @@ Full TypeScript signatures live in [`src/tools/`](src/tools/); architecture in [
516
624
 
517
625
  ### `inference_metrics` — see your local-model usage on demand
518
626
 
519
- Call `inference_metrics` anytime mid-session to see how many `prism_infer` calls ran locally vs cloud, with actual token counts:
627
+ Call `inference_metrics` anytime mid-session to see how many `prism_infer` calls ran locally vs cloud. Use `period: "all"` to atomically import the Synalux VS Code panel spool and include its local-serve rate in the persisted totals:
520
628
 
521
629
  ```
522
630
  📊 Inference Metrics — local-model delegation (this session):
@@ -530,28 +638,42 @@ Call `inference_metrics` anytime mid-session to see how many `prism_infer` calls
530
638
 
531
639
  The same block also appears automatically in `session_save_ledger` and `session_save_handoff` responses at session end.
532
640
 
533
- **Note:** This tracks `prism_infer` delegation only not your host model's (Claude's) own token spend. For that, use Claude Code's `/cost` command.
641
+ **Note:** The default session view tracks this MCP process's `prism_infer` delegation. The all-time view combines persisted MCP calls with Synalux VS Code panel inference. Neither view includes your host model's (Claude's) own token spend; use Claude Code's `/cost` command for that.
534
642
 
535
- ### Local-model delegation (opt-in)
643
+ ### Local-model delegation (default)
536
644
 
537
- By default, your AI agent (Claude, Cursor, etc.) handles everything itself. You can optionally enable delegation so the agent offloads cheap, verifiable sub-tasks to local Ollama models at $0:
645
+ Prism routes qualifying bounded work—bulk classification, field extraction,
646
+ mechanical formatting, test generation, and similar tasks—to local Ollama
647
+ models before any host-native subagent. The agent checks `gate_outcome`,
648
+ verifies the result, and continues in the current host thread when the local
649
+ worker is unavailable, refused, or degraded.
538
650
 
539
- ```bash
540
- # Enable via Prism config
541
- prism config set delegation_enabled true
651
+ Pass project memory when the subtask depends on prior work:
652
+
653
+ ```json
654
+ {
655
+ "prompt": "Generate the bounded regression-test cases.",
656
+ "project": "prism-mcp",
657
+ "context_depth": "standard",
658
+ "conversation_id": "<from session_bootstrap>",
659
+ "mode": "code",
660
+ "cloud_fallback": false,
661
+ "escalation": "report"
662
+ }
542
663
  ```
543
664
 
544
- When enabled, the agent's task router may delegate qualifying work — bulk classification, field extraction, mechanical formatting — to `prism_infer` instead of using cloud tokens. The agent always verifies the result and redoes it itself if quality is degraded.
665
+ Omit `context_depth` to use the dashboard setting. Turn off the dashboard Task
666
+ Router toggle or set `PRISM_TASK_ROUTER_ENABLED=false` for an explicit opt-out.
545
667
 
546
668
  **Guardrails:**
547
- - **Off by default** — enforced in code, not just convention
669
+ - **Local by default** — an explicit operator opt-out is preserved
548
670
  - **Never delegates:** code/text that ships to the user, security/safety logic, planning/reasoning, anything where a silent quality drop isn't obvious
549
671
  - **Always verifies:** checks `quality_gate_failed` and `used_cloud` before trusting local output
550
672
 
551
673
  <details>
552
674
  <summary>How Prism survives context compaction</summary>
553
675
 
554
- The LLM context window is treated as ephemeral scratch space; durable state lives in the persistent store (SQLite locally, the portal in the cloud). Every session begins with a mandatory `session_load_context` call, so the agent is oriented before it writes a response. When a project exceeds a threshold (default 50 entries), `session_compact_ledger` summarizes old entries into a rollup, soft-archives the originals, and links them in the graph. See [`docs/COMPACTION.md`](docs/COMPACTION.md)
676
+ The LLM context window is treated as ephemeral scratch space; durable state lives in the persistent store (SQLite locally, the portal in the cloud). Every session begins with a mandatory no-argument `session_bootstrap` call, so Prism applies the dashboard's project and quick/standard/deep setting before the agent writes a response. When a project exceeds a threshold (default 50 entries), `session_compact_ledger` summarizes old entries into a rollup, soft-archives the originals, and links them in the graph. See [`docs/COMPACTION.md`](docs/COMPACTION.md)
555
677
  </details>
556
678
 
557
679
  ---
@@ -692,7 +814,7 @@ Routing is automatic: `9b → 4b → cloud fallback` on desktop/server, `2b →
692
814
  | `PRISM_FORCE_LOCAL` | Force local SQLite regardless of credentials | `false` |
693
815
  | `TELEMETRY_WRITE_TOKEN` | Portal analytics token (optional — metrics display works without it) | -- |
694
816
 
695
- With no variables set, Prism runs fully local. Set `PRISM_SYNALUX_API_KEY` (and leave `PRISM_STORAGE=auto`) to use the cloud backend.
817
+ With no variables set, Prism runs fully local. With an active cloud-memory subscription, set `PRISM_SYNALUX_API_KEY` (and leave `PRISM_STORAGE=auto`) to use the Synalux backend; a portal-confirmed free tier remains on local SQLite.
696
818
 
697
819
  ---
698
820
 
package/dist/cli.js CHANGED
@@ -7,13 +7,81 @@ import { getStorage, closeStorage } from './storage/index.js';
7
7
  import { getSetting } from './storage/configStorage.js';
8
8
  import { PRISM_USER_ID, SERVER_CONFIG } from './config.js';
9
9
  import { getCurrentGitState } from './utils/git.js';
10
- import { sessionLoadContextHandler, sessionSaveLedgerHandler, sessionSaveHandoffHandler } from './tools/ledgerHandlers.js';
11
- import { connectHosts, normalizeHostName } from './connect.js';
10
+ import { sessionBootstrapHandler, sessionLoadContextHandler, sessionSaveLedgerHandler, sessionSaveHandoffHandler, } from './tools/ledgerHandlers.js';
11
+ import { configureClaudeAgentPolicy, configureClaudeNativeStartup, configureCodexAgentPolicy, configureCodexNativeStartup, configureGeminiAgentPolicy, configureGeminiNativeStartup, connectHosts, migrateLegacyClaudeHooks, migrateLegacyClaudeInstructions, migrateLegacyClaudeManagedStartup, migrateLegacyClaudeProjectMcp, normalizeHostName, } from './connect.js';
12
12
  const program = new Command();
13
+ /** Build the stable `prism load --json` envelope from depth-specific context. */
14
+ export function buildLoadJsonOutput(project, data, level, metadata) {
15
+ let history = [];
16
+ let historyLimit = 0;
17
+ if (level === 'standard') {
18
+ history = Array.isArray(data.recent_sessions) ? data.recent_sessions : [];
19
+ historyLimit = 5;
20
+ }
21
+ else if (level === 'deep') {
22
+ // Canonical deep context uses session_history. Fall back only when that
23
+ // field is absent so older portal responses remain consumable.
24
+ history = Array.isArray(data.session_history)
25
+ ? data.session_history
26
+ : Array.isArray(data.recent_sessions)
27
+ ? data.recent_sessions
28
+ : [];
29
+ historyLimit = 50;
30
+ }
31
+ return {
32
+ agent_name: metadata.agentName || null,
33
+ handoff: [{
34
+ project,
35
+ role: metadata.effectiveRole || data.role || 'global',
36
+ last_summary: data.last_summary || null,
37
+ pending_todo: data.pending_todo || null,
38
+ active_decisions: data.active_decisions || null,
39
+ keywords: data.keywords || null,
40
+ key_context: data.key_context || null,
41
+ active_branch: data.active_branch || null,
42
+ version: data.version ?? null,
43
+ updated_at: data.updated_at || null,
44
+ }],
45
+ // Keep the established key for callers while sourcing the canonical field
46
+ // for the requested depth.
47
+ recent_ledger: history.slice(0, historyLimit).map((entry) => ({
48
+ summary: entry?.summary || null,
49
+ decisions: entry?.decisions || null,
50
+ keywords: entry?.keywords || null,
51
+ created_at: entry?.session_date || entry?.created_at || null,
52
+ })),
53
+ git_hash: metadata.gitHash,
54
+ git_branch: metadata.gitBranch,
55
+ pkg_version: metadata.packageVersion,
56
+ };
57
+ }
13
58
  program
14
59
  .name('prism')
15
60
  .description('Prism — The Mind Palace for AI Agents')
16
61
  .version(SERVER_CONFIG.version);
62
+ /** Print the canonical hook-free first-turn display used by the MCP tool. */
63
+ export async function runBootstrapCommand() {
64
+ try {
65
+ const result = await sessionBootstrapHandler({});
66
+ const output = result.content?.map((part) => part?.text).filter(Boolean).join('\n') || '';
67
+ if (!output)
68
+ throw new Error('session_bootstrap returned no display content');
69
+ console.log(output);
70
+ if (result.isError)
71
+ process.exitCode = 1;
72
+ }
73
+ catch (err) {
74
+ console.error(`Bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
75
+ process.exitCode = 1;
76
+ }
77
+ finally {
78
+ await closeStorage().catch(() => { });
79
+ }
80
+ }
81
+ program
82
+ .command('bootstrap')
83
+ .description('Print the canonical dashboard-configured first-turn Prism greeting')
84
+ .action(runBootstrapCommand);
17
85
  // ─── prism connect ────────────────────────────────────────────
18
86
  // Registers this installed package with supported MCP hosts. The
19
87
  // merge is additive: an existing `prism` or `prism-mcp` entry is
@@ -25,7 +93,7 @@ program
25
93
  .option('--all', 'Target all supported hosts instead of auto-detecting installed hosts')
26
94
  .option('--dry-run', 'Preview configuration changes without writing files')
27
95
  .option('--refresh', 'Refresh only entries previously created by Prism; custom entries stay untouched')
28
- .action((options) => {
96
+ .action(async (options) => {
29
97
  try {
30
98
  if (!options.dryRun) {
31
99
  console.log('Close target MCP hosts before registration so they cannot edit configuration concurrently.');
@@ -63,11 +131,146 @@ program
63
131
  process.exitCode = 1;
64
132
  }
65
133
  }
134
+ const connectedClaude = summary.results.some((result) => result.host === 'claude-code' && result.status !== 'error' && result.startupCompatible);
135
+ const connectedGemini = summary.results.some((result) => result.host === 'gemini' && result.status !== 'error' && result.startupCompatible);
136
+ const connectedCodex = summary.results.some((result) => result.host === 'codex' && result.status !== 'error' && result.startupCompatible);
66
137
  if (options.dryRun) {
138
+ if (connectedClaude) {
139
+ const projectMcpMigration = migrateLegacyClaudeProjectMcp(undefined, undefined, true);
140
+ const hookMigration = migrateLegacyClaudeHooks(undefined, true);
141
+ const instructionMigration = migrateLegacyClaudeInstructions(undefined, true);
142
+ const managedMigration = migrateLegacyClaudeManagedStartup(undefined, true);
143
+ const nativeStartup = configureClaudeNativeStartup(undefined, true);
144
+ const agentPolicy = configureClaudeAgentPolicy(undefined, true);
145
+ if (projectMcpMigration.status === 'would-remove') {
146
+ console.log(`• Claude Code: would remove exact legacy project Prism registration (${projectMcpMigration.path})`);
147
+ }
148
+ if (hookMigration.status === 'would-remove') {
149
+ console.log(`• Claude Code: would remove ${hookMigration.removed} legacy Prism hook action(s) (${hookMigration.path})`);
150
+ }
151
+ if (instructionMigration.status === 'would-remove') {
152
+ console.log(`• Claude Code: would remove ${instructionMigration.removed} legacy Prism startup section(s) (${instructionMigration.path})`);
153
+ }
154
+ if (managedMigration.status === 'would-remove') {
155
+ console.log(`• Claude Code: would remove legacy managed startup block (${managedMigration.path})`);
156
+ }
157
+ if (nativeStartup.status === 'would-install' || nativeStartup.status === 'would-refresh') {
158
+ console.log(`• Claude Code: would ${nativeStartup.status === 'would-install' ? 'install' : 'refresh'} native startup instructions (${nativeStartup.path})`);
159
+ }
160
+ if (agentPolicy.status === 'would-install' || agentPolicy.status === 'would-refresh') {
161
+ console.log(`• Claude Code: would ${agentPolicy.status === 'would-install' ? 'install' : 'refresh'} local-first fallback policy (${agentPolicy.path})`);
162
+ }
163
+ }
164
+ if (connectedGemini) {
165
+ const nativeStartup = configureGeminiNativeStartup(undefined, true);
166
+ const agentPolicy = configureGeminiAgentPolicy(undefined, true);
167
+ if (nativeStartup.status === 'would-install' || nativeStartup.status === 'would-refresh') {
168
+ console.log(`• Gemini CLI: would ${nativeStartup.status === 'would-install' ? 'install' : 'refresh'} native startup instructions (${nativeStartup.path})`);
169
+ }
170
+ if (agentPolicy.status === 'would-install' || agentPolicy.status === 'would-refresh') {
171
+ console.log(`• Gemini CLI: would ${agentPolicy.status === 'would-install' ? 'disable native agents for' : 'refresh'} local-first policy (${agentPolicy.path})`);
172
+ }
173
+ }
174
+ if (connectedCodex) {
175
+ const nativeStartup = configureCodexNativeStartup(undefined, true);
176
+ const agentPolicy = configureCodexAgentPolicy(undefined, true);
177
+ if (nativeStartup.status === 'would-install' || nativeStartup.status === 'would-refresh') {
178
+ console.log(`• Codex: would ${nativeStartup.status === 'would-install' ? 'install' : 'refresh'} native startup instructions (${nativeStartup.path})`);
179
+ }
180
+ if (agentPolicy.status === 'would-install' || agentPolicy.status === 'would-refresh') {
181
+ console.log(`• Codex: would ${agentPolicy.status === 'would-install' ? 'install' : 'refresh'} hard local-first agent limits (${agentPolicy.path})`);
182
+ }
183
+ }
67
184
  console.log('\nDry run complete — no files changed.');
68
185
  }
186
+ else if (summary.results.some((result) => result.status !== 'error' && result.startupCompatible)) {
187
+ // Codex discovers native skills before it starts MCP servers. Syncing
188
+ // only from server startup therefore makes a fresh install require a
189
+ // second host restart. Materialize the entitlement snapshot before
190
+ // this registration command exits so the first launch sees it.
191
+ const { triggerSkillManifestSync } = await import('./skillManifestSync.js');
192
+ const skillSync = await triggerSkillManifestSync();
193
+ if (skillSync.status === 'failed' || skillSync.status === 'partial') {
194
+ throw new Error(`Synalux skill synchronization failed: ${skillSync.error || skillSync.status}`);
195
+ }
196
+ if (skillSync.status !== 'disabled') {
197
+ const changed = skillSync.installed.length + skillSync.updated.length + skillSync.pruned.length;
198
+ console.log(`✓ Synalux skills: ${skillSync.tier || 'free'} tier (${changed} changed)`);
199
+ if (skillSync.conflicts.length > 0) {
200
+ console.error(`⚠ Preserved locally modified skill conflicts: ${skillSync.conflicts.join(', ')}`);
201
+ }
202
+ if (connectedClaude) {
203
+ // Validate every affected surface before mutating any file.
204
+ migrateLegacyClaudeHooks(undefined, true);
205
+ migrateLegacyClaudeInstructions(undefined, true);
206
+ migrateLegacyClaudeManagedStartup(undefined, true);
207
+ configureClaudeNativeStartup(undefined, true);
208
+ configureClaudeAgentPolicy(undefined, true);
209
+ // Install the canonical block first. Never remove the legacy block
210
+ // unless the canonical path is already current or commits safely.
211
+ const nativeStartup = configureClaudeNativeStartup();
212
+ const agentPolicy = configureClaudeAgentPolicy();
213
+ const projectMcpMigration = migrateLegacyClaudeProjectMcp();
214
+ const hookMigration = migrateLegacyClaudeHooks();
215
+ const instructionMigration = migrateLegacyClaudeInstructions();
216
+ const managedMigration = migrateLegacyClaudeManagedStartup();
217
+ if (projectMcpMigration.status === 'removed') {
218
+ console.log(`✓ Claude Code: removed exact legacy project Prism registration (${projectMcpMigration.path}); user-scope registration now owns Prism`);
219
+ }
220
+ if (hookMigration.status === 'removed') {
221
+ console.log(`✓ Claude Code: removed ${hookMigration.removed} legacy Prism hook action(s); native skills now own startup and sync`);
222
+ }
223
+ if (instructionMigration.status === 'removed') {
224
+ console.log(`✓ Claude Code: removed ${instructionMigration.removed} legacy Prism startup section(s); MCP bootstrap now owns first-turn context`);
225
+ }
226
+ if (managedMigration.status === 'removed') {
227
+ console.log('✓ Claude Code: removed legacy managed startup block from ~/CLAUDE.md');
228
+ }
229
+ if (nativeStartup.status === 'installed' || nativeStartup.status === 'refreshed') {
230
+ console.log(`✓ Claude Code: ${nativeStartup.status} hook-free native startup instructions`);
231
+ }
232
+ if (agentPolicy.status === 'installed' || agentPolicy.status === 'refreshed') {
233
+ console.log(`✓ Claude Code: ${agentPolicy.status} local-first policy with Sonnet fallback`);
234
+ }
235
+ }
236
+ if (connectedGemini) {
237
+ configureGeminiNativeStartup(undefined, true);
238
+ configureGeminiAgentPolicy(undefined, true);
239
+ const nativeStartup = configureGeminiNativeStartup();
240
+ const agentPolicy = configureGeminiAgentPolicy();
241
+ if (nativeStartup.status === 'installed' || nativeStartup.status === 'refreshed') {
242
+ console.log(`✓ Gemini CLI: ${nativeStartup.status} hook-free native startup instructions`);
243
+ }
244
+ if (agentPolicy.status === 'installed' || agentPolicy.status === 'refreshed') {
245
+ console.log(`✓ Gemini CLI: ${agentPolicy.status} local-first policy; native agents disabled`);
246
+ }
247
+ }
248
+ if (connectedCodex) {
249
+ configureCodexNativeStartup(undefined, true);
250
+ configureCodexAgentPolicy(undefined, true);
251
+ const nativeStartup = configureCodexNativeStartup();
252
+ const agentPolicy = configureCodexAgentPolicy();
253
+ if (nativeStartup.status === 'installed' || nativeStartup.status === 'refreshed') {
254
+ console.log(`✓ Codex: ${nativeStartup.status} hook-free native startup instructions`);
255
+ }
256
+ if (agentPolicy.status === 'installed' || agentPolicy.status === 'refreshed') {
257
+ console.log(`✓ Codex: ${agentPolicy.status} hard local-first agent limits`);
258
+ }
259
+ }
260
+ }
261
+ else if (connectedClaude) {
262
+ // The project-level npx registration is redundant once the
263
+ // user-level installed-server entry succeeds. Removing that exact
264
+ // legacy tuple is independent of native skill delivery; leave hooks
265
+ // and startup instructions untouched while synchronization is off.
266
+ const projectMcpMigration = migrateLegacyClaudeProjectMcp();
267
+ if (projectMcpMigration.status === 'removed') {
268
+ console.log(`✓ Claude Code: removed exact legacy project Prism registration (${projectMcpMigration.path}); user-scope registration now owns Prism`);
269
+ }
270
+ }
271
+ }
69
272
  if (!summary.usedApiKey) {
70
- console.log('PRISM_SYNALUX_API_KEY is not set; new registrations use local/free mode.');
273
+ console.log('PRISM_SYNALUX_API_KEY is not set; no Synalux subscription key was copied into new registrations.');
71
274
  }
72
275
  }
73
276
  catch (err) {
@@ -101,13 +304,13 @@ program
101
304
  program
102
305
  .command('load <project>')
103
306
  .description('Load session context for a project (same output as session_load_context MCP tool)')
104
- .option('-l, --level <level>', 'Context depth: quick, standard, deep', 'standard')
307
+ .option('-l, --level <level>', 'Context depth: quick, standard, deep (defaults to dashboard setting)')
105
308
  .option('-r, --role <role>', 'Role scope for context loading')
106
309
  .option('-s, --storage <backend>', 'Storage backend: auto (default, prefers cloud), local (SQLite), or supabase. Overrides PRISM_STORAGE env var.')
107
310
  .option('--json', 'Emit machine-readable JSON instead of formatted text')
108
311
  .action(async (project, options) => {
109
312
  try {
110
- const { level, role, storage, json: jsonOutput } = options;
313
+ const { level: requestedLevel, role, storage, json: jsonOutput } = options;
111
314
  // v9.2.2: --storage flag overrides PRISM_STORAGE env var to prevent
112
315
  // split-brain when CLI environment differs from MCP server config.
113
316
  if (storage) {
@@ -119,10 +322,12 @@ program
119
322
  process.env.PRISM_STORAGE = storage;
120
323
  }
121
324
  const validLevels = ['quick', 'standard', 'deep'];
122
- if (!validLevels.includes(level)) {
123
- console.error(`Error: Invalid level "${level}". Must be one of: ${validLevels.join(', ')}`);
325
+ if (requestedLevel && !validLevels.includes(requestedLevel)) {
326
+ console.error(`Error: Invalid level "${requestedLevel}". Must be one of: ${validLevels.join(', ')}`);
124
327
  process.exit(1);
125
328
  }
329
+ const configuredLevel = requestedLevel ?? await getSetting('default_context_depth', 'standard');
330
+ const level = validLevels.includes(configuredLevel) ? configuredLevel : 'standard';
126
331
  if (jsonOutput) {
127
332
  // ── JSON mode: structured output for programmatic consumption ──
128
333
  const storageBackend = await getStorage();
@@ -136,30 +341,13 @@ program
136
341
  }
137
342
  const d = data;
138
343
  const gitState = getCurrentGitState();
139
- const output = {
140
- agent_name: agentName || null,
141
- handoff: [{
142
- project,
143
- role: effectiveRole || d.role || 'global',
144
- last_summary: d.last_summary || null,
145
- pending_todo: d.pending_todo || null,
146
- active_decisions: d.active_decisions || null,
147
- keywords: d.keywords || null,
148
- key_context: d.key_context || null,
149
- active_branch: d.active_branch || null,
150
- version: d.version ?? null,
151
- updated_at: d.updated_at || null,
152
- }],
153
- recent_ledger: (d.recent_sessions || []).map((s) => ({
154
- summary: s.summary || null,
155
- decisions: s.decisions || null,
156
- keywords: s.keywords || null,
157
- created_at: s.session_date || s.created_at || null,
158
- })),
159
- git_hash: gitState.commitSha ? gitState.commitSha.substring(0, 7) : null,
160
- git_branch: gitState.branch || null,
161
- pkg_version: SERVER_CONFIG.version,
162
- };
344
+ const output = buildLoadJsonOutput(project, d, level, {
345
+ agentName,
346
+ effectiveRole,
347
+ gitHash: gitState.commitSha ? gitState.commitSha.substring(0, 7) : null,
348
+ gitBranch: gitState.branch || null,
349
+ packageVersion: SERVER_CONFIG.version,
350
+ });
163
351
  console.log(JSON.stringify(output, null, 2));
164
352
  }
165
353
  else {
@@ -167,7 +355,7 @@ program
167
355
  // Delegates to the real handler so all enrichments (morning briefing,
168
356
  // reality drift, SDM recall, visual memory, skill injection,
169
357
  // behavioral warnings, etc.) are included automatically.
170
- const result = await sessionLoadContextHandler({ project, level, role });
358
+ const result = await sessionLoadContextHandler({ project, ...(requestedLevel ? { level: requestedLevel } : {}), role });
171
359
  // Surface handler-level errors (e.g. invalid args, storage failures)
172
360
  if (result.isError) {
173
361
  console.error(result.content[0]?.text || 'Unknown error loading context');
@@ -646,4 +834,4 @@ program
646
834
  if (fail > 0)
647
835
  process.exit(1);
648
836
  });
649
- program.parse(process.argv);
837
+ await program.parseAsync(process.argv);
package/dist/config.js CHANGED
@@ -81,7 +81,8 @@ export const GOOGLE_SEARCH_CX = process.env.GOOGLE_SEARCH_CX;
81
81
  //
82
82
  // Auto-resolution (PRISM_STORAGE=auto, the default) picks in this order:
83
83
  // 1. PRISM_FORCE_LOCAL=true → "local" (override everything)
84
- // 2. SYNALUX_API_KEY + PRISM_SYNALUX_BASE_URL set → "synalux"
84
+ // 2. Synalux credentials + portal-confirmed cloud-memory entitlement → "synalux"
85
+ // (portal-confirmed free → "local"; unverifiable entitlement → fail closed)
85
86
  // 3. SUPABASE_URL + SUPABASE_KEY set → "supabase" (legacy)
86
87
  // 4. else → "local"
87
88
  export const PRISM_STORAGE = process.env.PRISM_STORAGE || "auto";
@@ -124,7 +125,9 @@ export const SUPABASE_CONFIGURED = !!SUPABASE_URL &&
124
125
  // becomes a thin HTTP client of the synalux portal. This is the paid-tier
125
126
  // default. Synalux portal owns project validation, tier gating, audit logs,
126
127
  // and hivemind agent coordination.
127
- export const PRISM_SYNALUX_BASE_URL = sanitizeEnv(process.env.PRISM_SYNALUX_BASE_URL);
128
+ // PRISM_SYNALUX_BASE_URL is canonical. SYNALUX_BASE_URL remains a legacy
129
+ // compatibility alias so every portal client resolves the same origin.
130
+ export const PRISM_SYNALUX_BASE_URL = sanitizeEnv(process.env.PRISM_SYNALUX_BASE_URL || process.env.SYNALUX_BASE_URL);
128
131
  export const PRISM_SYNALUX_API_KEY = sanitizeEnv(process.env.PRISM_SYNALUX_API_KEY);
129
132
  export const SYNALUX_CONFIGURED = !!PRISM_SYNALUX_BASE_URL &&
130
133
  !!PRISM_SYNALUX_API_KEY &&
@@ -263,14 +266,18 @@ export const PRISM_ACTR_BUFFER_FLUSH_MS = parseInt(process.env.PRISM_ACTR_BUFFER
263
266
  export const PRISM_ACTR_ACCESS_LOG_RETENTION_DAYS = parseInt(process.env.PRISM_ACTR_ACCESS_LOG_RETENTION_DAYS || "90", 10);
264
267
  // ─── v7.1: Task Router Configuration ─────────────────────────
265
268
  // Deterministic heuristic-based routing for delegating coding tasks
266
- // between the host cloud model and the local claw-code-agent.
267
- // Set PRISM_TASK_ROUTER_ENABLED=true to unlock the session_task_route tool.
269
+ // between the host cloud model and Prism's local worker. Local-first is the
270
+ // default; set PRISM_TASK_ROUTER_ENABLED=false for an explicit operator opt-out.
268
271
  /** Master switch for the task router tool. */
269
- export const PRISM_TASK_ROUTER_ENABLED_ENV = process.env.PRISM_TASK_ROUTER_ENABLED === "true";
272
+ export const PRISM_TASK_ROUTER_ENABLED_ENV = process.env.PRISM_TASK_ROUTER_ENABLED !== "false";
270
273
  /** Confidence threshold below which routing defaults to the host model. (Default: 0.6) */
271
274
  export const PRISM_TASK_ROUTER_CONFIDENCE_THRESHOLD = parseFloat(process.env.PRISM_TASK_ROUTER_CONFIDENCE_THRESHOLD || "0.6");
272
- /** Maximum complexity score (1-10) that Claw can handle. Tasks above this → host. (Default: 4) */
273
- export const PRISM_TASK_ROUTER_MAX_CLAW_COMPLEXITY = parseInt(process.env.PRISM_TASK_ROUTER_MAX_CLAW_COMPLEXITY || "4", 10);
275
+ /**
276
+ * Operator ceiling for bounded local work (1-10). The router independently
277
+ * rejects architecture, security, tool-heavy, and other host-only work.
278
+ * Default 10 lets prism_infer select every entitled/RAM-safe local tier.
279
+ */
280
+ export const PRISM_TASK_ROUTER_MAX_CLAW_COMPLEXITY = parseInt(process.env.PRISM_TASK_ROUTER_MAX_CLAW_COMPLEXITY || "10", 10);
274
281
  // ─── v7.2: Verification Harness ──────────────────────────────
275
282
  /** Master switch for the v7.2.0 enhanced verification harness. */
276
283
  export const PRISM_VERIFICATION_HARNESS_ENABLED = process.env.PRISM_VERIFICATION_HARNESS_ENABLED === "true";