@zokizuan/satori-cli 0.2.0 → 0.3.1
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 +9 -5
- package/assets/skills/satori-indexing/SKILL.md +3 -1
- package/assets/skills/satori-navigation/SKILL.md +9 -6
- package/assets/skills/satori-search/SKILL.md +6 -3
- package/dist/args.d.ts +2 -0
- package/dist/args.js +10 -1
- package/dist/doctor.d.ts +20 -0
- package/dist/doctor.js +92 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +15 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,10 +14,11 @@ Shell CLI for Satori installation, skill packaging, and direct tool invocation w
|
|
|
14
14
|
## Install / Uninstall
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npx -y @zokizuan/satori-cli@0.
|
|
18
|
-
npx -y @zokizuan/satori-cli@0.
|
|
19
|
-
npx -y @zokizuan/satori-cli@0.
|
|
20
|
-
npx -y @zokizuan/satori-cli@0.
|
|
17
|
+
npx -y @zokizuan/satori-cli@0.3.1 install --client codex
|
|
18
|
+
npx -y @zokizuan/satori-cli@0.3.1 install --client claude
|
|
19
|
+
npx -y @zokizuan/satori-cli@0.3.1 install --client all --dry-run
|
|
20
|
+
npx -y @zokizuan/satori-cli@0.3.1 uninstall --client codex
|
|
21
|
+
npx -y @zokizuan/satori-cli@0.3.1 doctor
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
Managed install writes MCP config that launches:
|
|
@@ -25,13 +26,14 @@ Managed install writes MCP config that launches:
|
|
|
25
26
|
```toml
|
|
26
27
|
[mcp_servers.satori]
|
|
27
28
|
command = "npx"
|
|
28
|
-
args = ["-y", "@zokizuan/satori-mcp@4.
|
|
29
|
+
args = ["-y", "@zokizuan/satori-mcp@4.9.1"]
|
|
29
30
|
startup_timeout_ms = 180000
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
## Commands
|
|
33
34
|
|
|
34
35
|
```bash
|
|
36
|
+
satori-cli doctor
|
|
35
37
|
satori-cli tools list
|
|
36
38
|
satori-cli tool call <toolName> --args-json '{"path":"/abs/repo","query":"auth"}'
|
|
37
39
|
satori-cli tool call <toolName> --args-file ./args.json
|
|
@@ -41,6 +43,8 @@ satori-cli <toolName> [schema-subset flags]
|
|
|
41
43
|
|
|
42
44
|
Global flags (`--startup-timeout-ms`, `--call-timeout-ms`, `--format`, `--debug`) must appear before the command token.
|
|
43
45
|
|
|
46
|
+
`doctor` checks local setup without starting an MCP client: Node version, npm package visibility, provider env, and Milvus env.
|
|
47
|
+
|
|
44
48
|
## Development
|
|
45
49
|
|
|
46
50
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: satori-indexing
|
|
3
|
-
description:
|
|
3
|
+
description: Use when Satori codebases are not indexed, stale, blocked, still indexing, or need lifecycle recovery.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Satori Indexing
|
|
@@ -27,10 +27,12 @@ Use only:
|
|
|
27
27
|
- Never call `manage_index(action="clear")` unless the user explicitly requests destructive reset.
|
|
28
28
|
- Treat ignore-only churn as a `sync` problem first.
|
|
29
29
|
- Respect blocked and indexing states instead of forcing retries blindly.
|
|
30
|
+
- Use `status` and `list_codebases` freely; provider credentials are only needed for provider-backed lifecycle actions.
|
|
30
31
|
|
|
31
32
|
## Status Handling
|
|
32
33
|
|
|
33
34
|
- `requires_reindex`: run `manage_index(action="reindex")`.
|
|
34
35
|
- `not_ready` with indexing reason: check status and wait for terminal completion.
|
|
35
36
|
- `not_indexed`: create the index.
|
|
37
|
+
- `MISSING_PROVIDER_CONFIG`: run `satori-cli doctor` when available, then set missing provider or Milvus env before retrying create/reindex/sync/search.
|
|
36
38
|
- Ignore-rule noise mitigation: update `.satoriignore`, wait debounce, and run `sync` for immediate convergence.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: satori-navigation
|
|
3
|
-
description:
|
|
3
|
+
description: Use when search has returned candidate code and exact spans, symbol reads, or call relationships are needed.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Satori Navigation
|
|
@@ -14,12 +14,14 @@ Use only:
|
|
|
14
14
|
2. `call_graph`
|
|
15
15
|
3. `read_file`
|
|
16
16
|
|
|
17
|
+
For lifecycle remediation (`requires_reindex`, `not_indexed`, indexing waits), switch to `satori-indexing`.
|
|
18
|
+
|
|
17
19
|
## Workflow
|
|
18
20
|
|
|
19
21
|
1. Use grouped `search_codebase` results as the starting point.
|
|
20
|
-
2.
|
|
21
|
-
3. If `callGraphHint.supported=
|
|
22
|
-
4.
|
|
22
|
+
2. Use `file_outline(resolveMode="exact", symbolIdExact|symbolLabelExact)` to lock the symbol span when exact identity is available.
|
|
23
|
+
3. If `callGraphHint.supported=true`, call `call_graph(path=..., symbolRef=..., direction="both", depth=1)`.
|
|
24
|
+
4. If `callGraphHint.supported=false`, execute `navigationFallback.readSpan.args` exactly.
|
|
23
25
|
5. Use `read_file(path=..., open_symbol=...)` or deterministic line spans for the final read.
|
|
24
26
|
|
|
25
27
|
## Rules
|
|
@@ -28,9 +30,10 @@ Use only:
|
|
|
28
30
|
- `open_symbol` must resolve deterministically. Do not guess on ambiguity.
|
|
29
31
|
- `read_file(mode="annotated")` is preferred when outline metadata is useful.
|
|
30
32
|
- Follow continuation hints when plain reads are truncated.
|
|
33
|
+
- Other tools do not run search freshness; remediate stale index state before trusting navigation.
|
|
31
34
|
|
|
32
35
|
## Remediation
|
|
33
36
|
|
|
34
|
-
- `requires_reindex`: reindex before retrying navigation.
|
|
35
|
-
- `not_ready`:
|
|
37
|
+
- `requires_reindex`: switch to `satori-indexing` and reindex before retrying navigation.
|
|
38
|
+
- `not_ready` or `not_indexed`: switch to `satori-indexing`; wait or create before retrying navigation.
|
|
36
39
|
- `unsupported`: fall back to deterministic `read_file` spans when supplied by `navigationFallback`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: satori-search
|
|
3
|
-
description:
|
|
3
|
+
description: Use when finding code by behavior, concept, or symbol before opening files or falling back to grep.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Satori Search
|
|
@@ -16,16 +16,17 @@ Use only:
|
|
|
16
16
|
|
|
17
17
|
## Workflow
|
|
18
18
|
|
|
19
|
-
1. Check readiness with `manage_index(action="status", path=...)
|
|
19
|
+
1. Check readiness with `manage_index(action="status", path=...)` when index state is unknown.
|
|
20
20
|
2. If not indexed, use `manage_index(action="create", path=...)`.
|
|
21
21
|
3. If `requires_reindex` appears, stop and use `manage_index(action="reindex", path=...)`, then retry.
|
|
22
|
-
4. Search with `search_codebase(path=..., query=..., scope="runtime", resultMode="grouped", groupBy="symbol", rankingMode="auto_changed_first")`.
|
|
22
|
+
4. Search the user-requested path with `search_codebase(path=..., query=..., scope="runtime", resultMode="grouped", groupBy="symbol", rankingMode="auto_changed_first")`.
|
|
23
23
|
|
|
24
24
|
## Search Rules
|
|
25
25
|
|
|
26
26
|
- Start with natural-language intent, not filenames.
|
|
27
27
|
- Default to `scope="runtime"`.
|
|
28
28
|
- Use operators only when needed: `lang:`, `path:`, `-path:`, `must:`, `exclude:`.
|
|
29
|
+
- Pass the user's requested path; if Satori resolves an indexed parent, follow returned `navigationFallback` exactly.
|
|
29
30
|
- Treat warnings as usable-but-degraded results, not fatal errors.
|
|
30
31
|
- Use `debug=true` only when ranking or filter explanations are required.
|
|
31
32
|
|
|
@@ -33,4 +34,6 @@ Use only:
|
|
|
33
34
|
|
|
34
35
|
- `requires_reindex`: run `manage_index(action="reindex")`, not `sync`.
|
|
35
36
|
- `not_ready` with indexing reason: wait or check `manage_index(action="status")`.
|
|
37
|
+
- `not_indexed`: run `manage_index(action="create")` on the repository root or requested indexed root.
|
|
38
|
+
- `MISSING_PROVIDER_CONFIG` is active only when it appears as the tool response `code` or `reason`. If it appears inside `search_codebase` results, it may just be matched code content.
|
|
36
39
|
- Noise mitigation hint: update `.satoriignore`, wait debounce, rerun search, and use `manage_index(action="sync")` only for immediate convergence.
|
package/dist/args.d.ts
CHANGED
package/dist/args.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { CliError } from "./errors.js";
|
|
3
|
-
const RESERVED_SUBCOMMANDS = new Set(["tools", "tool", "help", "version", "install", "uninstall"]);
|
|
3
|
+
const RESERVED_SUBCOMMANDS = new Set(["tools", "tool", "help", "version", "doctor", "install", "uninstall"]);
|
|
4
4
|
const PRIMITIVE_TYPES = new Set(["string", "number", "integer", "boolean"]);
|
|
5
5
|
function parsePositiveInteger(value, flagName) {
|
|
6
6
|
const parsed = Number.parseInt(value, 10);
|
|
@@ -145,6 +145,15 @@ export function parseCliArgs(argv) {
|
|
|
145
145
|
command: { kind: "version" }
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
|
+
if (rest[0] === "doctor") {
|
|
149
|
+
if (rest.length !== 1) {
|
|
150
|
+
throw new CliError("E_USAGE", `Unknown arguments for doctor: ${rest.slice(1).join(" ")}`, 2);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
globals,
|
|
154
|
+
command: { kind: "doctor" }
|
|
155
|
+
};
|
|
156
|
+
}
|
|
148
157
|
if (rest[0] === "install") {
|
|
149
158
|
return {
|
|
150
159
|
globals,
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
type CheckStatus = "ok" | "warning" | "error";
|
|
3
|
+
export interface DoctorCheck {
|
|
4
|
+
name: string;
|
|
5
|
+
status: CheckStatus;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DoctorResult {
|
|
9
|
+
status: CheckStatus;
|
|
10
|
+
checks: DoctorCheck[];
|
|
11
|
+
nextSteps: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface DoctorOptions {
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
nodeVersion?: string;
|
|
16
|
+
execFileSyncImpl?: typeof execFileSync;
|
|
17
|
+
}
|
|
18
|
+
export declare function runDoctor(options?: DoctorOptions): DoctorResult;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=doctor.d.ts.map
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { readManagedPackageJson, resolveManagedPackageSpecifier } from "./managed-package.js";
|
|
3
|
+
function parseNodeMajor(version) {
|
|
4
|
+
const match = version.match(/^v?(\d+)/);
|
|
5
|
+
return match ? Number(match[1]) : 0;
|
|
6
|
+
}
|
|
7
|
+
function selectedProvider(env) {
|
|
8
|
+
return env.EMBEDDING_PROVIDER || "VoyageAI";
|
|
9
|
+
}
|
|
10
|
+
function requiredEmbeddingEnv(provider) {
|
|
11
|
+
switch (provider) {
|
|
12
|
+
case "OpenAI":
|
|
13
|
+
return "OPENAI_API_KEY";
|
|
14
|
+
case "VoyageAI":
|
|
15
|
+
return "VOYAGEAI_API_KEY";
|
|
16
|
+
case "Gemini":
|
|
17
|
+
return "GEMINI_API_KEY";
|
|
18
|
+
case "Ollama":
|
|
19
|
+
return null;
|
|
20
|
+
default:
|
|
21
|
+
return "VOYAGEAI_API_KEY";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function addCheck(checks, name, status, message) {
|
|
25
|
+
checks.push({ name, status, message });
|
|
26
|
+
}
|
|
27
|
+
function overallStatus(checks) {
|
|
28
|
+
if (checks.some((check) => check.status === "error")) {
|
|
29
|
+
return "error";
|
|
30
|
+
}
|
|
31
|
+
if (checks.some((check) => check.status === "warning")) {
|
|
32
|
+
return "warning";
|
|
33
|
+
}
|
|
34
|
+
return "ok";
|
|
35
|
+
}
|
|
36
|
+
export function runDoctor(options = {}) {
|
|
37
|
+
const env = options.env || process.env;
|
|
38
|
+
const nodeVersion = options.nodeVersion || process.version;
|
|
39
|
+
const execImpl = options.execFileSyncImpl || execFileSync;
|
|
40
|
+
const checks = [];
|
|
41
|
+
const nextSteps = [];
|
|
42
|
+
const nodeMajor = parseNodeMajor(nodeVersion);
|
|
43
|
+
if (nodeMajor >= 20) {
|
|
44
|
+
addCheck(checks, "node_version", "ok", `Node ${nodeVersion} satisfies >=20.`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
addCheck(checks, "node_version", "error", `Node ${nodeVersion} is unsupported. Install Node.js 20 or newer.`);
|
|
48
|
+
nextSteps.push("Install Node.js 20 or newer.");
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const specifier = resolveManagedPackageSpecifier();
|
|
52
|
+
const pkg = readManagedPackageJson();
|
|
53
|
+
execImpl("npm", ["view", `${pkg.name}@${pkg.version}`, "version", "--json"], {
|
|
54
|
+
encoding: "utf8",
|
|
55
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
56
|
+
});
|
|
57
|
+
addCheck(checks, "npm_package_access", "ok", `${specifier} is visible to npm.`);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
61
|
+
addCheck(checks, "npm_package_access", "warning", `Could not verify npm package access: ${message}`);
|
|
62
|
+
nextSteps.push("Verify npm can access @zokizuan/satori-mcp from this machine.");
|
|
63
|
+
}
|
|
64
|
+
const provider = selectedProvider(env);
|
|
65
|
+
const requiredKey = requiredEmbeddingEnv(provider);
|
|
66
|
+
if (requiredKey && !env[requiredKey]) {
|
|
67
|
+
addCheck(checks, "embedding_provider_env", "error", `${provider} requires ${requiredKey}.`);
|
|
68
|
+
nextSteps.push(`Set ${requiredKey}.`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
addCheck(checks, "embedding_provider_env", "ok", requiredKey ? `${requiredKey} is present.` : `${provider} does not require an API key.`);
|
|
72
|
+
}
|
|
73
|
+
if (!env.MILVUS_ADDRESS) {
|
|
74
|
+
addCheck(checks, "milvus_address", "error", "MILVUS_ADDRESS is required for index/search/clear operations.");
|
|
75
|
+
nextSteps.push("Set MILVUS_ADDRESS.");
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
addCheck(checks, "milvus_address", "ok", "MILVUS_ADDRESS is present.");
|
|
79
|
+
}
|
|
80
|
+
if (env.MILVUS_TOKEN) {
|
|
81
|
+
addCheck(checks, "milvus_token", "ok", "MILVUS_TOKEN is present.");
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
addCheck(checks, "milvus_token", "ok", "MILVUS_TOKEN is not set; local/unauthenticated Milvus endpoints are supported.");
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
status: overallStatus(checks),
|
|
88
|
+
checks,
|
|
89
|
+
nextSteps: [...new Set(nextSteps)],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=doctor.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import type { DoctorResult } from "./doctor.js";
|
|
2
3
|
interface RunCliOptions {
|
|
3
4
|
writeStdout?: (text: string) => void;
|
|
4
5
|
writeStderr?: (text: string) => void;
|
|
@@ -11,6 +12,9 @@ interface RunCliOptions {
|
|
|
11
12
|
callTimeoutMs?: number;
|
|
12
13
|
cwd?: string;
|
|
13
14
|
installabilityVerifier?: () => string | Promise<string>;
|
|
15
|
+
doctorRunner?: (options: {
|
|
16
|
+
env: NodeJS.ProcessEnv;
|
|
17
|
+
}) => DoctorResult;
|
|
14
18
|
connectSession?: (options: {
|
|
15
19
|
command: string;
|
|
16
20
|
args: string[];
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { emitError, emitJson, inferManageStatusState, parseStructuredEnvelope }
|
|
|
9
9
|
import { executeInstallCommand } from "./install.js";
|
|
10
10
|
import { verifyManagedPackageInstallability } from "./package-installability.js";
|
|
11
11
|
import { resolveServerEntryPath } from "./resolve-server-entry.js";
|
|
12
|
+
import { runDoctor } from "./doctor.js";
|
|
12
13
|
const MANAGE_INDEX_MIN_POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
13
14
|
function firstText(result) {
|
|
14
15
|
const content = result?.content;
|
|
@@ -46,6 +47,7 @@ function buildHelpPayload() {
|
|
|
46
47
|
commands: [
|
|
47
48
|
"install [--client all|codex|claude] [--dry-run]",
|
|
48
49
|
"uninstall [--client all|codex|claude] [--dry-run]",
|
|
50
|
+
"doctor",
|
|
49
51
|
"tools list",
|
|
50
52
|
"tool call <toolName> --args-json '<json>'",
|
|
51
53
|
"tool call <toolName> --args-file <path>",
|
|
@@ -195,6 +197,19 @@ export async function runCli(argv, options = {}) {
|
|
|
195
197
|
}
|
|
196
198
|
return 0;
|
|
197
199
|
}
|
|
200
|
+
if (parsed.command.kind === "doctor") {
|
|
201
|
+
const result = (options.doctorRunner || ((doctorOptions) => runDoctor({ env: doctorOptions.env })))({
|
|
202
|
+
env: effectiveEnv,
|
|
203
|
+
});
|
|
204
|
+
emitJson(writers, result);
|
|
205
|
+
if (parsed.globals.format === "text") {
|
|
206
|
+
writers.writeStderr(`satori-cli doctor status=${result.status}.\n`);
|
|
207
|
+
for (const step of result.nextSteps) {
|
|
208
|
+
writers.writeStderr(`next: ${step}\n`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return result.status === "error" ? 1 : 0;
|
|
212
|
+
}
|
|
198
213
|
if (parsed.command.kind === "install" || parsed.command.kind === "uninstall") {
|
|
199
214
|
if (parsed.command.kind === "install") {
|
|
200
215
|
await (options.installabilityVerifier || verifyManagedPackageInstallability)();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zokizuan/satori-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Shell CLI for Satori MCP installation and skill-based workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
13
|
-
"@zokizuan/satori-mcp": "4.
|
|
13
|
+
"@zokizuan/satori-mcp": "4.9.1"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^20.0.0",
|