@zokizuan/satori-cli 0.4.0 → 0.4.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 ham-zax
3
+ Copyright (c) 2026 Hamza (@ham-zax)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -5,14 +5,16 @@ Installer and shell client for Satori MCP. Use this package to configure support
5
5
  ## Quick Start
6
6
 
7
7
  ```bash
8
- npx -y @zokizuan/satori-cli@0.4.0 install --client all
9
- npx -y @zokizuan/satori-cli@0.4.0 doctor
8
+ npx -y @zokizuan/satori-cli@0.4.2 install --client all
9
+ npx -y @zokizuan/satori-cli@0.4.2 doctor
10
10
  ```
11
11
 
12
12
  Supported clients are `codex`, `claude`, `opencode`, and `all`.
13
13
 
14
14
  The installer performs package resolution once, stores the MCP server under `~/.satori/mcp-runtime/`, writes a stable launcher at `~/.satori/bin/satori-mcp.js`, and writes client-specific config that starts the launcher directly with Node. Resident MCP startup should not run `npx` or require a custom long startup timeout.
15
15
 
16
+ Treat `~/.satori/` as installer-owned state. The public setup path is the installer command above, not manual copying of runtime cache paths into each harness.
17
+
16
18
  The installer only manages Satori-owned config and the first-party workflow skill:
17
19
 
18
20
  - `satori`
@@ -20,15 +22,44 @@ The installer only manages Satori-owned config and the first-party workflow skil
20
22
  ## Commands
21
23
 
22
24
  ```bash
23
- npx -y @zokizuan/satori-cli@0.4.0 install --client codex
24
- npx -y @zokizuan/satori-cli@0.4.0 install --client claude
25
- npx -y @zokizuan/satori-cli@0.4.0 install --client opencode
26
- npx -y @zokizuan/satori-cli@0.4.0 install --client all --dry-run
27
- npx -y @zokizuan/satori-cli@0.4.0 uninstall --client codex
25
+ npx -y @zokizuan/satori-cli@0.4.2 install --client codex
26
+ npx -y @zokizuan/satori-cli@0.4.2 install --client claude
27
+ npx -y @zokizuan/satori-cli@0.4.2 install --client opencode
28
+ npx -y @zokizuan/satori-cli@0.4.2 install --client all --dry-run
29
+ npx -y @zokizuan/satori-cli@0.4.2 uninstall --client codex
28
30
  ```
29
31
 
30
32
  `doctor` checks Node, package visibility, provider env, and Milvus env without starting an MCP client.
31
33
 
34
+ Typical first run:
35
+
36
+ ```bash
37
+ npx -y @zokizuan/satori-cli@0.4.2 install --client all
38
+ npx -y @zokizuan/satori-cli@0.4.2 doctor
39
+ # restart your MCP client
40
+ ```
41
+
42
+ The installer writes launcher config only. Runtime provider settings are read when the MCP client starts.
43
+
44
+ Supported client installs expose the Satori runtime variable names in the client config:
45
+
46
+ - Codex writes active `env_vars` forwarding plus an optional commented `[mcp_servers.satori.env]` template in `~/.codex/config.toml`.
47
+ - Claude Code writes `mcpServers.satori.env` in `~/.claude.json` with `${VAR:-}` pass-through values.
48
+ - OpenCode writes `mcp.satori.environment` in `~/.config/opencode/opencode.json` with `{env:VAR}` pass-through values.
49
+
50
+ If you want a client to store literal values, replace the generated pass-through value for that client. In Codex, uncomment or add this table outside the installer-managed launcher block so reinstalls keep your edits:
51
+
52
+ ```toml
53
+ [mcp_servers.satori.env]
54
+ EMBEDDING_PROVIDER = "VoyageAI"
55
+ EMBEDDING_MODEL = "voyage-4-large"
56
+ EMBEDDING_OUTPUT_DIMENSION = "1024"
57
+ VOYAGEAI_API_KEY = "pa-..."
58
+ VOYAGEAI_RERANKER_MODEL = "rerank-2.5"
59
+ MILVUS_ADDRESS = "https://your-zilliz-endpoint"
60
+ MILVUS_TOKEN = "your-zilliz-token"
61
+ ```
62
+
32
63
  ## Direct Tool Calls
33
64
 
34
65
  ```bash
package/dist/doctor.js CHANGED
@@ -7,6 +7,32 @@ function parseNodeMajor(version) {
7
7
  function selectedProvider(env) {
8
8
  return env.EMBEDDING_PROVIDER || "VoyageAI";
9
9
  }
10
+ function defaultModelForProvider(provider) {
11
+ switch (provider) {
12
+ case "OpenAI":
13
+ return "text-embedding-3-small";
14
+ case "VoyageAI":
15
+ return "voyage-4-large";
16
+ case "Gemini":
17
+ return "gemini-embedding-001";
18
+ case "Ollama":
19
+ return "nomic-embed-text";
20
+ default:
21
+ return "voyage-4-large";
22
+ }
23
+ }
24
+ function selectedModel(env, provider) {
25
+ if (provider === "Ollama") {
26
+ return env.OLLAMA_MODEL || env.EMBEDDING_MODEL || defaultModelForProvider(provider);
27
+ }
28
+ return env.EMBEDDING_MODEL || defaultModelForProvider(provider);
29
+ }
30
+ function selectedDimension(env, provider) {
31
+ if (env.EMBEDDING_OUTPUT_DIMENSION) {
32
+ return env.EMBEDDING_OUTPUT_DIMENSION;
33
+ }
34
+ return provider === "VoyageAI" ? "1024" : "provider default";
35
+ }
10
36
  function requiredEmbeddingEnv(provider) {
11
37
  switch (provider) {
12
38
  case "OpenAI":
@@ -62,17 +88,25 @@ export function runDoctor(options = {}) {
62
88
  nextSteps.push("Verify npm can access @zokizuan/satori-mcp from this machine.");
63
89
  }
64
90
  const provider = selectedProvider(env);
91
+ addCheck(checks, "embedding_provider", "ok", `Embedding provider: ${provider}.`);
92
+ addCheck(checks, "embedding_model", "ok", `Embedding model: ${selectedModel(env, provider)}.`);
93
+ addCheck(checks, "embedding_dimension", "ok", `Embedding output dimension: ${selectedDimension(env, provider)}.`);
65
94
  const requiredKey = requiredEmbeddingEnv(provider);
66
95
  if (requiredKey && !env[requiredKey]) {
67
96
  addCheck(checks, "embedding_provider_env", "error", `${provider} requires ${requiredKey}.`);
68
- nextSteps.push(`Set ${requiredKey}.`);
97
+ if (provider === "VoyageAI") {
98
+ nextSteps.push("Set VOYAGEAI_API_KEY from the Voyage AI dashboard API keys page.");
99
+ }
100
+ else {
101
+ nextSteps.push(`Set ${requiredKey}.`);
102
+ }
69
103
  }
70
104
  else {
71
105
  addCheck(checks, "embedding_provider_env", "ok", requiredKey ? `${requiredKey} is present.` : `${provider} does not require an API key.`);
72
106
  }
73
107
  if (!env.MILVUS_ADDRESS) {
74
108
  addCheck(checks, "milvus_address", "error", "MILVUS_ADDRESS is required for index/search/clear operations.");
75
- nextSteps.push("Set MILVUS_ADDRESS.");
109
+ nextSteps.push("Set MILVUS_ADDRESS to a Zilliz Cloud public endpoint or local Milvus address such as localhost:19530.");
76
110
  }
77
111
  else {
78
112
  addCheck(checks, "milvus_address", "ok", "MILVUS_ADDRESS is present.");
@@ -83,6 +117,9 @@ export function runDoctor(options = {}) {
83
117
  else {
84
118
  addCheck(checks, "milvus_token", "ok", "MILVUS_TOKEN is not set; local/unauthenticated Milvus endpoints are supported.");
85
119
  }
120
+ if (nextSteps.length > 0) {
121
+ nextSteps.push("Restart your MCP client after changing Satori environment variables.");
122
+ }
86
123
  return {
87
124
  status: overallStatus(checks),
88
125
  checks,
package/dist/install.js CHANGED
@@ -8,12 +8,47 @@ import { CliError } from "./errors.js";
8
8
  import { resolveManagedPackageSpecifier } from "./managed-package.js";
9
9
  const MANAGED_BLOCK_START = "# >>> satori-cli managed satori start >>>";
10
10
  const MANAGED_BLOCK_END = "# <<< satori-cli managed satori end <<<";
11
+ const CODEX_ENV_TEMPLATE_START = "# >>> satori-cli optional satori env template >>>";
12
+ const CODEX_ENV_TEMPLATE_END = "# <<< satori-cli optional satori env template <<<";
11
13
  const INSTRUCTIONS_BLOCK_START = "<!-- satori-mcp:start -->";
12
14
  const INSTRUCTIONS_BLOCK_END = "<!-- satori-mcp:end -->";
13
15
  const OWNED_SKILL_DIRS = ["satori"];
14
16
  const MANAGED_RUNTIME_DIR = "mcp-runtime";
15
17
  const MANAGED_BIN_DIR = "bin";
16
18
  const MANAGED_LAUNCHER_FILE = "satori-mcp.js";
19
+ const SATORI_RUNTIME_ENV_VARS = [
20
+ "EMBEDDING_PROVIDER",
21
+ "EMBEDDING_MODEL",
22
+ "EMBEDDING_OUTPUT_DIMENSION",
23
+ "OPENAI_API_KEY",
24
+ "OPENAI_BASE_URL",
25
+ "VOYAGEAI_API_KEY",
26
+ "VOYAGEAI_RERANKER_MODEL",
27
+ "GEMINI_API_KEY",
28
+ "GEMINI_BASE_URL",
29
+ "OLLAMA_HOST",
30
+ "OLLAMA_MODEL",
31
+ "MILVUS_ADDRESS",
32
+ "MILVUS_TOKEN",
33
+ "READ_FILE_MAX_LINES",
34
+ "MCP_ENABLE_WATCHER",
35
+ "MCP_WATCH_DEBOUNCE_MS",
36
+ ];
37
+ const CODEX_ENV_TEMPLATE_LINES = [
38
+ CODEX_ENV_TEMPLATE_START,
39
+ "# Optional direct Codex env values. Uncomment/fill these if you prefer",
40
+ "# ~/.codex/config.toml to store Satori runtime settings directly.",
41
+ "# This template is outside the launcher block so reinstall keeps edits.",
42
+ "# [mcp_servers.satori.env]",
43
+ "# EMBEDDING_PROVIDER = \"VoyageAI\"",
44
+ "# EMBEDDING_MODEL = \"voyage-4-large\"",
45
+ "# EMBEDDING_OUTPUT_DIMENSION = \"1024\"",
46
+ "# VOYAGEAI_API_KEY = \"pa-...\"",
47
+ "# VOYAGEAI_RERANKER_MODEL = \"rerank-2.5\"",
48
+ "# MILVUS_ADDRESS = \"https://your-zilliz-endpoint\"",
49
+ "# MILVUS_TOKEN = \"your-zilliz-token\"",
50
+ CODEX_ENV_TEMPLATE_END,
51
+ ];
17
52
  const OPENCODE_INSTRUCTIONS = `# Satori MCP
18
53
 
19
54
  This project uses Satori MCP for semantic code search, deterministic navigation, and index lifecycle management.
@@ -56,7 +91,7 @@ function resolveClientTargets(homeDir) {
56
91
  },
57
92
  {
58
93
  client: "claude",
59
- configPath: path.join(homeDir, ".claude", "settings.json"),
94
+ configPath: path.join(homeDir, ".claude.json"),
60
95
  companion: {
61
96
  kind: "skills",
62
97
  path: path.join(homeDir, ".claude", "skills"),
@@ -103,6 +138,21 @@ function toTomlString(value) {
103
138
  function buildTomlArray(values) {
104
139
  return `[${values.map(toTomlString).join(", ")}]`;
105
140
  }
141
+ function runtimeEnvMap(valueForName) {
142
+ return Object.fromEntries(SATORI_RUNTIME_ENV_VARS.map((name) => [name, valueForName(name)]));
143
+ }
144
+ function objectValue(value) {
145
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
146
+ return undefined;
147
+ }
148
+ return value;
149
+ }
150
+ function mergeRuntimeEnv(existing, defaults) {
151
+ return {
152
+ ...defaults,
153
+ ...(objectValue(existing) ?? {}),
154
+ };
155
+ }
106
156
  function packageNameFromSpecifier(packageSpecifier) {
107
157
  if (packageSpecifier.startsWith("@")) {
108
158
  const versionMarker = packageSpecifier.indexOf("@", 1);
@@ -258,10 +308,25 @@ function buildCodexManagedBlock(runtimeCommand) {
258
308
  "[mcp_servers.satori]",
259
309
  `command = ${toTomlString(runtimeCommand.command)}`,
260
310
  `args = ${buildTomlArray(runtimeCommand.args)}`,
311
+ "# Satori reads provider/vector settings from environment at MCP startup.",
312
+ "# env_vars forwards these names from Codex's parent environment when set.",
313
+ `env_vars = ${buildTomlArray([...SATORI_RUNTIME_ENV_VARS])}`,
261
314
  MANAGED_BLOCK_END,
262
315
  "",
263
316
  ].join("\n");
264
317
  }
318
+ function buildCodexEnvTemplateBlock() {
319
+ return `${CODEX_ENV_TEMPLATE_LINES.join("\n")}\n`;
320
+ }
321
+ function codexHasSatoriEnvTable(content) {
322
+ return /^\s*\[mcp_servers\.satori\.env\]\s*$/m.test(content);
323
+ }
324
+ function ensureCodexEnvTemplate(content) {
325
+ if (content.includes(CODEX_ENV_TEMPLATE_START) || codexHasSatoriEnvTable(content)) {
326
+ return content;
327
+ }
328
+ return `${normalizeTrailingNewline(content)}\n${buildCodexEnvTemplateBlock()}`;
329
+ }
265
330
  function codexHasUnmanagedSatoriSection(content) {
266
331
  if (!content.includes("[mcp_servers.satori]")) {
267
332
  return false;
@@ -284,6 +349,7 @@ function prepareCodexInstall(filePath, runtimeCommand) {
284
349
  else {
285
350
  next = `${normalizeTrailingNewline(current)}\n${managedBlock}`;
286
351
  }
352
+ next = ensureCodexEnvTemplate(next);
287
353
  return {
288
354
  changed: next !== current,
289
355
  apply: () => {
@@ -337,10 +403,12 @@ function parseJsonObject(filePath) {
337
403
  }
338
404
  return parsed;
339
405
  }
340
- function buildClaudeServerConfig(runtimeCommand) {
406
+ function buildClaudeServerConfig(runtimeCommand, existing) {
341
407
  return {
408
+ type: "stdio",
342
409
  command: runtimeCommand.command,
343
410
  args: runtimeCommand.args,
411
+ env: mergeRuntimeEnv(existing?.env, runtimeEnvMap((name) => `\${${name}:-}`)),
344
412
  };
345
413
  }
346
414
  function isManagedLauncherPath(value) {
@@ -366,7 +434,8 @@ function isManagedClaudeEntry(value) {
366
434
  function prepareClaudeInstall(filePath, runtimeCommand) {
367
435
  const currentObject = parseJsonObject(filePath);
368
436
  const currentSerialized = JSON.stringify(currentObject);
369
- const desiredServer = buildClaudeServerConfig(runtimeCommand);
437
+ const existingSatori = objectValue(currentObject.mcpServers?.satori);
438
+ const desiredServer = buildClaudeServerConfig(runtimeCommand, existingSatori);
370
439
  const mcpServersValue = currentObject.mcpServers;
371
440
  let mcpServers;
372
441
  if (mcpServersValue === undefined) {
@@ -378,8 +447,7 @@ function prepareClaudeInstall(filePath, runtimeCommand) {
378
447
  else {
379
448
  throw new CliError("E_USAGE", `Expected mcpServers to be an object in ${filePath}.`, 2);
380
449
  }
381
- const existingSatori = mcpServers.satori;
382
- if (existingSatori !== undefined && !isManagedClaudeEntry(existingSatori)) {
450
+ if (mcpServers.satori !== undefined && !isManagedClaudeEntry(mcpServers.satori)) {
383
451
  throw new CliError("E_USAGE", `Refusing to overwrite unmanaged Satori config in ${filePath}. Remove mcpServers.satori manually or align it to the managed Satori form first.`, 2);
384
452
  }
385
453
  mcpServers.satori = {
@@ -439,11 +507,12 @@ function parseJsoncObject(filePath, content) {
439
507
  }
440
508
  return parsed;
441
509
  }
442
- function buildOpenCodeServerConfig(runtimeCommand) {
510
+ function buildOpenCodeServerConfig(runtimeCommand, existing) {
443
511
  return {
444
512
  enabled: true,
445
513
  type: "local",
446
514
  command: [runtimeCommand.command, ...runtimeCommand.args],
515
+ environment: mergeRuntimeEnv(existing?.environment, runtimeEnvMap((name) => `{env:${name}}`)),
447
516
  };
448
517
  }
449
518
  function isManagedOpenCodeEntry(value) {
@@ -488,7 +557,7 @@ function prepareOpenCodeInstall(filePath, runtimeCommand) {
488
557
  if (existingSatori !== undefined && !isManagedOpenCodeEntry(existingSatori)) {
489
558
  throw new CliError("E_USAGE", `Refusing to overwrite unmanaged Satori config in ${filePath}. Remove mcp.satori manually or align it to the managed Satori form first.`, 2);
490
559
  }
491
- return mutateJsonc(filePath, current, ["mcp", "satori"], buildOpenCodeServerConfig(runtimeCommand));
560
+ return mutateJsonc(filePath, current, ["mcp", "satori"], buildOpenCodeServerConfig(runtimeCommand, objectValue(existingSatori)));
492
561
  }
493
562
  function prepareOpenCodeUninstall(filePath) {
494
563
  const current = readTextIfExists(filePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zokizuan/satori-cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Shell CLI for Satori MCP installation and skill-based workflows",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,7 +11,7 @@
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/sdk": "^1.29.0",
13
13
  "jsonc-parser": "^3.3.1",
14
- "@zokizuan/satori-mcp": "4.11.0"
14
+ "@zokizuan/satori-mcp": "4.11.2"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^20.0.0",