@zokizuan/satori-cli 0.1.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.
@@ -0,0 +1,333 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { CliError } from "./errors.js";
6
+ import { resolveManagedPackageSpecifier } from "./managed-package.js";
7
+ const MANAGED_BLOCK_START = "# >>> satori-cli managed satori start >>>";
8
+ const MANAGED_BLOCK_END = "# <<< satori-cli managed satori end <<<";
9
+ const OWNED_SKILL_DIRS = ["satori-search", "satori-navigation", "satori-indexing"];
10
+ const MANAGED_TIMEOUT_MS = 180000;
11
+ function resolveDefaultSkillAssetRoot() {
12
+ const currentFile = fileURLToPath(import.meta.url);
13
+ return path.resolve(path.dirname(currentFile), "..", "assets", "skills");
14
+ }
15
+ function resolveDefaultPackageSpecifier() {
16
+ try {
17
+ return resolveManagedPackageSpecifier();
18
+ }
19
+ catch {
20
+ // Fall through to hard failure below.
21
+ }
22
+ throw new CliError("E_USAGE", "Unable to resolve the installed Satori package version for CLI install.", 2);
23
+ }
24
+ function resolveClientTargets(homeDir) {
25
+ return [
26
+ {
27
+ client: "codex",
28
+ configPath: path.join(homeDir, ".codex", "config.toml"),
29
+ skillsPath: path.join(homeDir, ".codex", "skills"),
30
+ },
31
+ {
32
+ client: "claude",
33
+ configPath: path.join(homeDir, ".claude", "settings.json"),
34
+ skillsPath: path.join(homeDir, ".claude", "skills"),
35
+ },
36
+ ];
37
+ }
38
+ function selectTargets(homeDir, client) {
39
+ const targets = resolveClientTargets(homeDir);
40
+ if (client === "all") {
41
+ return targets;
42
+ }
43
+ return targets.filter((target) => target.client === client);
44
+ }
45
+ function ensureParentDir(filePath) {
46
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
47
+ }
48
+ function ensureDir(dirPath) {
49
+ fs.mkdirSync(dirPath, { recursive: true });
50
+ }
51
+ function readTextIfExists(filePath) {
52
+ if (!fs.existsSync(filePath)) {
53
+ return null;
54
+ }
55
+ return fs.readFileSync(filePath, "utf8");
56
+ }
57
+ function normalizeTrailingNewline(value) {
58
+ return value.endsWith("\n") ? value : `${value}\n`;
59
+ }
60
+ function escapeRegExp(value) {
61
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62
+ }
63
+ function buildCodexManagedBlock(packageSpecifier) {
64
+ return [
65
+ MANAGED_BLOCK_START,
66
+ "[mcp_servers.satori]",
67
+ 'command = "npx"',
68
+ `args = ["-y", "${packageSpecifier}"]`,
69
+ `startup_timeout_ms = ${MANAGED_TIMEOUT_MS}`,
70
+ MANAGED_BLOCK_END,
71
+ "",
72
+ ].join("\n");
73
+ }
74
+ function codexHasUnmanagedSatoriSection(content) {
75
+ if (!content.includes("[mcp_servers.satori]")) {
76
+ return false;
77
+ }
78
+ return !(content.includes(MANAGED_BLOCK_START) && content.includes(MANAGED_BLOCK_END));
79
+ }
80
+ function prepareCodexInstall(filePath, packageSpecifier) {
81
+ const current = readTextIfExists(filePath) ?? "";
82
+ if (codexHasUnmanagedSatoriSection(current)) {
83
+ throw new CliError("E_USAGE", `Refusing to overwrite unmanaged Satori config in ${filePath}. Remove [mcp_servers.satori] manually or convert it to the managed block first.`, 2);
84
+ }
85
+ const managedBlock = buildCodexManagedBlock(packageSpecifier);
86
+ let next = current;
87
+ if (current.includes(MANAGED_BLOCK_START) && current.includes(MANAGED_BLOCK_END)) {
88
+ next = current.replace(new RegExp(`${escapeRegExp(MANAGED_BLOCK_START)}[\\s\\S]*?${escapeRegExp(MANAGED_BLOCK_END)}\\n?`, "m"), managedBlock);
89
+ }
90
+ else if (current.trim().length === 0) {
91
+ next = managedBlock;
92
+ }
93
+ else {
94
+ next = `${normalizeTrailingNewline(current)}\n${managedBlock}`;
95
+ }
96
+ return {
97
+ changed: next !== current,
98
+ apply: () => {
99
+ if (next === current) {
100
+ return;
101
+ }
102
+ ensureParentDir(filePath);
103
+ fs.writeFileSync(filePath, next, "utf8");
104
+ },
105
+ };
106
+ }
107
+ function prepareCodexUninstall(filePath) {
108
+ const current = readTextIfExists(filePath);
109
+ if (!current) {
110
+ return { changed: false, apply: () => { } };
111
+ }
112
+ if (codexHasUnmanagedSatoriSection(current)) {
113
+ throw new CliError("E_USAGE", `Refusing to remove unmanaged Satori config in ${filePath}. Remove [mcp_servers.satori] manually instead.`, 2);
114
+ }
115
+ if (!current.includes(MANAGED_BLOCK_START) || !current.includes(MANAGED_BLOCK_END)) {
116
+ return { changed: false, apply: () => { } };
117
+ }
118
+ const next = current
119
+ .replace(new RegExp(`\\n?${escapeRegExp(MANAGED_BLOCK_START)}[\\s\\S]*?${escapeRegExp(MANAGED_BLOCK_END)}\\n?`, "m"), "\n")
120
+ .replace(/\n{3,}/g, "\n\n")
121
+ .replace(/^\n+/, "");
122
+ return {
123
+ changed: next !== current,
124
+ apply: () => {
125
+ if (next === current) {
126
+ return;
127
+ }
128
+ fs.writeFileSync(filePath, next, "utf8");
129
+ },
130
+ };
131
+ }
132
+ function parseJsonObject(filePath) {
133
+ const current = readTextIfExists(filePath);
134
+ if (!current) {
135
+ return {};
136
+ }
137
+ let parsed;
138
+ try {
139
+ parsed = JSON.parse(current);
140
+ }
141
+ catch (error) {
142
+ throw new CliError("E_USAGE", `Failed to parse JSON config at ${filePath}: ${error.message}`, 2);
143
+ }
144
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
145
+ throw new CliError("E_USAGE", `Expected top-level JSON object in ${filePath}.`, 2);
146
+ }
147
+ return parsed;
148
+ }
149
+ function buildClaudeServerConfig(packageSpecifier) {
150
+ return {
151
+ command: "npx",
152
+ args: ["-y", packageSpecifier],
153
+ timeout: MANAGED_TIMEOUT_MS,
154
+ };
155
+ }
156
+ function isManagedPackageSpecifier(value) {
157
+ return typeof value === "string" && /^@zokizuan\/satori-mcp@.+$/.test(value);
158
+ }
159
+ function isManagedClaudeEntry(value) {
160
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
161
+ return false;
162
+ }
163
+ const entry = value;
164
+ if (entry.command !== "npx") {
165
+ return false;
166
+ }
167
+ if (entry.timeout !== MANAGED_TIMEOUT_MS) {
168
+ return false;
169
+ }
170
+ if (!Array.isArray(entry.args)) {
171
+ return false;
172
+ }
173
+ if (entry.args.length === 2) {
174
+ return entry.args[0] === "-y" && isManagedPackageSpecifier(entry.args[1]);
175
+ }
176
+ if (entry.args.length === 4) {
177
+ return entry.args[0] === "-y"
178
+ && entry.args[1] === "--package"
179
+ && isManagedPackageSpecifier(entry.args[2])
180
+ && entry.args[3] === "satori";
181
+ }
182
+ return false;
183
+ }
184
+ function prepareClaudeInstall(filePath, packageSpecifier) {
185
+ const currentObject = parseJsonObject(filePath);
186
+ const currentSerialized = JSON.stringify(currentObject);
187
+ const desiredServer = buildClaudeServerConfig(packageSpecifier);
188
+ const mcpServersValue = currentObject.mcpServers;
189
+ let mcpServers;
190
+ if (mcpServersValue === undefined) {
191
+ mcpServers = {};
192
+ }
193
+ else if (mcpServersValue && typeof mcpServersValue === "object" && !Array.isArray(mcpServersValue)) {
194
+ mcpServers = { ...mcpServersValue };
195
+ }
196
+ else {
197
+ throw new CliError("E_USAGE", `Expected mcpServers to be an object in ${filePath}.`, 2);
198
+ }
199
+ const existingSatori = mcpServers.satori;
200
+ if (existingSatori !== undefined && !isManagedClaudeEntry(existingSatori)) {
201
+ throw new CliError("E_USAGE", `Refusing to overwrite unmanaged Satori config in ${filePath}. Remove mcpServers.satori manually or align it to the managed npx form first.`, 2);
202
+ }
203
+ mcpServers.satori = {
204
+ ...existingSatori,
205
+ ...desiredServer,
206
+ };
207
+ currentObject.mcpServers = mcpServers;
208
+ const next = `${JSON.stringify(currentObject, null, 2)}\n`;
209
+ return {
210
+ changed: JSON.stringify(currentObject) !== currentSerialized,
211
+ apply: () => {
212
+ if (JSON.stringify(currentObject) === currentSerialized) {
213
+ return;
214
+ }
215
+ ensureParentDir(filePath);
216
+ fs.writeFileSync(filePath, next, "utf8");
217
+ },
218
+ };
219
+ }
220
+ function prepareClaudeUninstall(filePath) {
221
+ const currentObject = parseJsonObject(filePath);
222
+ const mcpServersValue = currentObject.mcpServers;
223
+ if (!mcpServersValue || typeof mcpServersValue !== "object" || Array.isArray(mcpServersValue)) {
224
+ return { changed: false, apply: () => { } };
225
+ }
226
+ const mcpServers = { ...mcpServersValue };
227
+ if (!Object.prototype.hasOwnProperty.call(mcpServers, "satori")) {
228
+ return { changed: false, apply: () => { } };
229
+ }
230
+ if (!isManagedClaudeEntry(mcpServers.satori)) {
231
+ throw new CliError("E_USAGE", `Refusing to remove unmanaged Satori config in ${filePath}. Remove mcpServers.satori manually instead.`, 2);
232
+ }
233
+ delete mcpServers.satori;
234
+ if (Object.keys(mcpServers).length === 0) {
235
+ delete currentObject.mcpServers;
236
+ }
237
+ else {
238
+ currentObject.mcpServers = mcpServers;
239
+ }
240
+ const next = `${JSON.stringify(currentObject, null, 2)}\n`;
241
+ return {
242
+ changed: true,
243
+ apply: () => {
244
+ fs.writeFileSync(filePath, next, "utf8");
245
+ },
246
+ };
247
+ }
248
+ function prepareSkillInstall(skillsPath, skillAssetRoot) {
249
+ const writes = [];
250
+ let changed = false;
251
+ for (const skillDirName of OWNED_SKILL_DIRS) {
252
+ const sourceFile = path.join(skillAssetRoot, skillDirName, "SKILL.md");
253
+ if (!fs.existsSync(sourceFile)) {
254
+ throw new CliError("E_USAGE", `Missing packaged skill asset: ${sourceFile}`, 2);
255
+ }
256
+ const content = fs.readFileSync(sourceFile, "utf8");
257
+ const destinationDir = path.join(skillsPath, skillDirName);
258
+ const destinationFile = path.join(destinationDir, "SKILL.md");
259
+ if (readTextIfExists(destinationFile) !== content) {
260
+ changed = true;
261
+ writes.push({ destinationDir, destinationFile, content });
262
+ }
263
+ }
264
+ return {
265
+ changed,
266
+ apply: () => {
267
+ for (const write of writes) {
268
+ ensureDir(write.destinationDir);
269
+ fs.writeFileSync(write.destinationFile, write.content, "utf8");
270
+ }
271
+ },
272
+ };
273
+ }
274
+ function prepareSkillRemoval(skillsPath) {
275
+ const removals = OWNED_SKILL_DIRS
276
+ .map((skillDirName) => path.join(skillsPath, skillDirName))
277
+ .filter((destinationDir) => fs.existsSync(destinationDir));
278
+ return {
279
+ changed: removals.length > 0,
280
+ apply: () => {
281
+ for (const destinationDir of removals) {
282
+ fs.rmSync(destinationDir, { recursive: true, force: true });
283
+ }
284
+ },
285
+ };
286
+ }
287
+ function prepareMutation(target, command, packageSpecifier, skillAssetRoot) {
288
+ const configMutation = command.kind === "install"
289
+ ? target.client === "codex"
290
+ ? prepareCodexInstall(target.configPath, packageSpecifier)
291
+ : prepareClaudeInstall(target.configPath, packageSpecifier)
292
+ : target.client === "codex"
293
+ ? prepareCodexUninstall(target.configPath)
294
+ : prepareClaudeUninstall(target.configPath);
295
+ const skillsMutation = command.kind === "install"
296
+ ? prepareSkillInstall(target.skillsPath, skillAssetRoot)
297
+ : prepareSkillRemoval(target.skillsPath);
298
+ return {
299
+ target,
300
+ configChanged: configMutation.changed,
301
+ skillsChanged: skillsMutation.changed,
302
+ apply: () => {
303
+ configMutation.apply();
304
+ skillsMutation.apply();
305
+ },
306
+ };
307
+ }
308
+ export function executeInstallCommand(command, options = {}) {
309
+ const homeDir = options.homeDir ?? os.homedir();
310
+ const packageSpecifier = options.packageSpecifier ?? resolveDefaultPackageSpecifier();
311
+ const skillAssetRoot = options.skillAssetRoot ?? resolveDefaultSkillAssetRoot();
312
+ const prepared = selectTargets(homeDir, command.client).map((target) => (prepareMutation(target, command, packageSpecifier, skillAssetRoot)));
313
+ if (!command.dryRun) {
314
+ for (const mutation of prepared) {
315
+ mutation.apply();
316
+ }
317
+ }
318
+ return {
319
+ action: command.kind,
320
+ client: command.client,
321
+ dryRun: command.dryRun,
322
+ results: prepared.map((mutation) => ({
323
+ client: mutation.target.client,
324
+ configPath: mutation.target.configPath,
325
+ skillsPath: mutation.target.skillsPath,
326
+ configChanged: mutation.configChanged,
327
+ skillsChanged: mutation.skillsChanged,
328
+ status: mutation.configChanged || mutation.skillsChanged ? "updated" : "unchanged",
329
+ dryRun: command.dryRun,
330
+ })),
331
+ };
332
+ }
333
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1,11 @@
1
+ interface ManagedPackageJsonShape {
2
+ name?: unknown;
3
+ version?: unknown;
4
+ main?: unknown;
5
+ }
6
+ export declare function resolveManagedPackageJsonPath(): string;
7
+ export declare function resolveManagedPackageRoot(): string;
8
+ export declare function readManagedPackageJson(): Required<Pick<ManagedPackageJsonShape, "name" | "version">> & ManagedPackageJsonShape;
9
+ export declare function resolveManagedPackageSpecifier(): string;
10
+ export {};
11
+ //# sourceMappingURL=managed-package.d.ts.map
@@ -0,0 +1,39 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createRequire } from "node:module";
4
+ import { fileURLToPath } from "node:url";
5
+ import { CliError } from "./errors.js";
6
+ const MANAGED_PACKAGE_NAME = "@zokizuan/satori-mcp";
7
+ const require = createRequire(import.meta.url);
8
+ function fallbackManagedPackageJsonPath() {
9
+ const currentFile = fileURLToPath(import.meta.url);
10
+ return path.resolve(path.dirname(currentFile), "..", "..", "mcp", "package.json");
11
+ }
12
+ export function resolveManagedPackageJsonPath() {
13
+ try {
14
+ return require.resolve(`${MANAGED_PACKAGE_NAME}/package.json`);
15
+ }
16
+ catch {
17
+ const fallbackPath = fallbackManagedPackageJsonPath();
18
+ if (fs.existsSync(fallbackPath)) {
19
+ return fallbackPath;
20
+ }
21
+ throw new CliError("E_USAGE", `Unable to resolve installed package metadata for ${MANAGED_PACKAGE_NAME}. Install ${MANAGED_PACKAGE_NAME} or use a local dev server config instead.`, 2);
22
+ }
23
+ }
24
+ export function resolveManagedPackageRoot() {
25
+ return path.dirname(resolveManagedPackageJsonPath());
26
+ }
27
+ export function readManagedPackageJson() {
28
+ const packageJsonPath = resolveManagedPackageJsonPath();
29
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
30
+ if (typeof parsed.name !== "string" || typeof parsed.version !== "string") {
31
+ throw new CliError("E_USAGE", `Unable to read valid package metadata from ${packageJsonPath}.`, 2);
32
+ }
33
+ return parsed;
34
+ }
35
+ export function resolveManagedPackageSpecifier() {
36
+ const pkg = readManagedPackageJson();
37
+ return `${pkg.name}@${pkg.version}`;
38
+ }
39
+ //# sourceMappingURL=managed-package.js.map
@@ -0,0 +1,14 @@
1
+ import { execFileSync } from "node:child_process";
2
+ type ExecFileSyncLike = typeof execFileSync;
3
+ export interface PackageInstallabilityOptions {
4
+ packageJsonPath?: string;
5
+ execFileSyncImpl?: ExecFileSyncLike;
6
+ }
7
+ export interface ReleaseSmokeOptions extends PackageInstallabilityOptions {
8
+ packageRoot?: string;
9
+ tempDir?: string;
10
+ }
11
+ export declare function verifyManagedPackageInstallability(options?: PackageInstallabilityOptions): string;
12
+ export declare function runPublishedPackageReleaseSmoke(options?: ReleaseSmokeOptions): void;
13
+ export {};
14
+ //# sourceMappingURL=package-installability.d.ts.map
@@ -0,0 +1,123 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { execFileSync } from "node:child_process";
5
+ import { CliError } from "./errors.js";
6
+ import { resolveManagedPackageJsonPath } from "./managed-package.js";
7
+ function resolveDefaultPackageJsonPath() {
8
+ return resolveManagedPackageJsonPath();
9
+ }
10
+ function readPackageJson(packageJsonPath) {
11
+ return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
12
+ }
13
+ function looksLikeExactVersion(value) {
14
+ return /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(value);
15
+ }
16
+ function npmOutput(error) {
17
+ if (!(error instanceof Error)) {
18
+ return String(error);
19
+ }
20
+ const stdout = "stdout" in error && typeof error.stdout === "string"
21
+ ? error.stdout
22
+ : "";
23
+ const stderr = "stderr" in error && typeof error.stderr === "string"
24
+ ? error.stderr
25
+ : "";
26
+ return `${stdout}\n${stderr}\n${error.message}`.trim();
27
+ }
28
+ function resolveWorkspaceDependencyVersion(packageJsonPath, dependencyName) {
29
+ const packageRoot = path.dirname(packageJsonPath);
30
+ const repoRoot = path.resolve(packageRoot, "..", "..");
31
+ const packagesRoot = path.join(repoRoot, "packages");
32
+ if (!fs.existsSync(packagesRoot)) {
33
+ return null;
34
+ }
35
+ for (const entry of fs.readdirSync(packagesRoot, { withFileTypes: true })) {
36
+ if (!entry.isDirectory()) {
37
+ continue;
38
+ }
39
+ const candidatePath = path.join(packagesRoot, entry.name, "package.json");
40
+ if (!fs.existsSync(candidatePath)) {
41
+ continue;
42
+ }
43
+ const candidate = readPackageJson(candidatePath);
44
+ if (candidate.name === dependencyName && looksLikeExactVersion(candidate.version)) {
45
+ return candidate.version;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ function assertPublishedVersion(packageName, version, ownerPackageName, ownerPackageVersion, execImpl, relation) {
51
+ try {
52
+ execImpl("npm", ["view", `${packageName}@${version}`, "version", "--json"], {
53
+ encoding: "utf8",
54
+ stdio: ["ignore", "pipe", "pipe"],
55
+ });
56
+ }
57
+ catch (error) {
58
+ if (relation === "self") {
59
+ throw new CliError("E_USAGE", `Cannot install ${ownerPackageName}@${ownerPackageVersion} because that package version is not published on npm. Publish ${ownerPackageName}@${ownerPackageVersion} first or use a local dev server config instead.`, 2);
60
+ }
61
+ throw new CliError("E_USAGE", `Cannot install ${ownerPackageName}@${ownerPackageVersion} because required dependency ${packageName}@${version} is not published on npm. Publish ${packageName}@${version} first, then rerun satori-cli install.`, 2);
62
+ }
63
+ }
64
+ export function verifyManagedPackageInstallability(options = {}) {
65
+ const packageJsonPath = options.packageJsonPath ?? resolveDefaultPackageJsonPath();
66
+ const execImpl = options.execFileSyncImpl ?? execFileSync;
67
+ const pkg = readPackageJson(packageJsonPath);
68
+ const packageSpecifier = `${pkg.name}@${pkg.version}`;
69
+ assertPublishedVersion(pkg.name, pkg.version, pkg.name, pkg.version, execImpl, "self");
70
+ for (const [dependencyName, rawDependencyVersion] of Object.entries(pkg.dependencies ?? {})) {
71
+ const dependencyVersion = looksLikeExactVersion(rawDependencyVersion)
72
+ ? rawDependencyVersion
73
+ : rawDependencyVersion.startsWith("workspace:")
74
+ ? resolveWorkspaceDependencyVersion(packageJsonPath, dependencyName)
75
+ : null;
76
+ if (!dependencyVersion) {
77
+ continue;
78
+ }
79
+ assertPublishedVersion(dependencyName, dependencyVersion, pkg.name, pkg.version, execImpl, "dependency");
80
+ }
81
+ return packageSpecifier;
82
+ }
83
+ export function runPublishedPackageReleaseSmoke(options = {}) {
84
+ const packageJsonPath = options.packageJsonPath ?? resolveDefaultPackageJsonPath();
85
+ const packageRoot = options.packageRoot ?? path.dirname(packageJsonPath);
86
+ const tempDir = options.tempDir ?? os.tmpdir();
87
+ const execImpl = options.execFileSyncImpl ?? execFileSync;
88
+ verifyManagedPackageInstallability({ packageJsonPath, execFileSyncImpl: execImpl });
89
+ const smokePackDir = fs.mkdtempSync(path.join(tempDir, "satori-release-smoke-"));
90
+ const smokeExecDir = fs.mkdtempSync(path.join(tempDir, "satori-release-exec-"));
91
+ const beforeFiles = new Set(fs.readdirSync(smokePackDir));
92
+ execImpl("pnpm", ["pack", "--pack-destination", smokePackDir], {
93
+ cwd: packageRoot,
94
+ encoding: "utf8",
95
+ stdio: ["ignore", "pipe", "pipe"],
96
+ });
97
+ const tarballName = fs.readdirSync(smokePackDir).find((entry) => entry.endsWith(".tgz") && !beforeFiles.has(entry));
98
+ if (!tarballName) {
99
+ throw new CliError("E_USAGE", "Release smoke failed: pnpm pack did not produce a tarball.", 2);
100
+ }
101
+ const tarballPath = path.join(smokePackDir, tarballName);
102
+ try {
103
+ execImpl("npm", ["exec", "--yes", "--package", tarballPath, "--", "satori", "--help"], {
104
+ cwd: smokeExecDir,
105
+ encoding: "utf8",
106
+ env: {
107
+ ...process.env,
108
+ npm_config_package_lock: "false",
109
+ },
110
+ stdio: ["ignore", "pipe", "pipe"],
111
+ });
112
+ }
113
+ catch (error) {
114
+ const output = npmOutput(error);
115
+ const pkg = readPackageJson(packageJsonPath);
116
+ throw new CliError("E_USAGE", `Release smoke failed for ${pkg.name}@${pkg.version}. The packed tarball did not start via 'npm exec --yes --package <tarball> -- satori --help'. ${output}`, 2);
117
+ }
118
+ finally {
119
+ fs.rmSync(smokePackDir, { recursive: true, force: true });
120
+ fs.rmSync(smokeExecDir, { recursive: true, force: true });
121
+ }
122
+ }
123
+ //# sourceMappingURL=package-installability.js.map
@@ -0,0 +1,2 @@
1
+ export declare function resolveServerEntryPath(): string;
2
+ //# sourceMappingURL=resolve-server-entry.d.ts.map
@@ -0,0 +1,17 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { readManagedPackageJson, resolveManagedPackageRoot } from "./managed-package.js";
4
+ export function resolveServerEntryPath() {
5
+ const packageRoot = resolveManagedPackageRoot();
6
+ const pkg = readManagedPackageJson();
7
+ const jsEntry = path.resolve(packageRoot, typeof pkg.main === "string" ? pkg.main : "dist/index.js");
8
+ if (fs.existsSync(jsEntry)) {
9
+ return jsEntry;
10
+ }
11
+ const tsEntry = path.resolve(packageRoot, "src", "index.ts");
12
+ if (fs.existsSync(tsEntry)) {
13
+ return tsEntry;
14
+ }
15
+ return jsEntry;
16
+ }
17
+ //# sourceMappingURL=resolve-server-entry.js.map
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@zokizuan/satori-cli",
3
+ "version": "0.1.1",
4
+ "description": "Shell CLI for Satori MCP installation and skill-based workflows",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "satori-cli": "dist/index.js"
10
+ },
11
+ "dependencies": {
12
+ "@modelcontextprotocol/sdk": "^1.12.1",
13
+ "@zokizuan/satori-mcp": "4.4.1"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^20.0.0",
17
+ "tsx": "^4.19.4",
18
+ "typescript": "^5.0.0"
19
+ },
20
+ "files": [
21
+ "dist/**/*.js",
22
+ "dist/**/*.d.ts",
23
+ "assets/skills/**/*.md",
24
+ "README.md"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/ham-zax/satori.git",
29
+ "directory": "packages/cli"
30
+ },
31
+ "homepage": "https://github.com/ham-zax/satori",
32
+ "bugs": {
33
+ "url": "https://github.com/ham-zax/satori/issues"
34
+ },
35
+ "author": "Hamza (@ham-zax)",
36
+ "license": "MIT",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "build": "pnpm clean && tsc --build --force && pnpm fix:bin-perms",
42
+ "dev": "tsx --watch src/index.ts",
43
+ "clean": "rimraf dist",
44
+ "lint": "eslint src --ext .ts",
45
+ "lint:fix": "eslint src --ext .ts --fix",
46
+ "typecheck": "tsc --noEmit",
47
+ "start": "tsx src/index.ts",
48
+ "release:smoke": "tsx scripts/release-smoke.ts",
49
+ "fix:bin-perms": "node -e \"const fs=require('fs');try{fs.chmodSync('dist/index.js',0o755);}catch(e){console.error(e);process.exit(1);}\"",
50
+ "test": "pnpm --filter @zokizuan/satori-mcp build:runtime && node --import tsx --test --test-concurrency=1 src/**/*.test.ts"
51
+ }
52
+ }