create-snipara 1.3.25 → 1.3.26
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/dist/index.js +190 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { homedir } from "os";
|
|
|
13
13
|
// package.json
|
|
14
14
|
var package_default = {
|
|
15
15
|
name: "create-snipara",
|
|
16
|
-
version: "1.3.
|
|
16
|
+
version: "1.3.26",
|
|
17
17
|
description: "Onboard Snipara Hosted MCP Context + Memory with auth, companion workflows, and optional Snipara Sandbox",
|
|
18
18
|
main: "dist/index.js",
|
|
19
19
|
type: "module",
|
|
@@ -185,7 +185,7 @@ function buildProgram() {
|
|
|
185
185
|
printBanner();
|
|
186
186
|
await runRepair(options);
|
|
187
187
|
});
|
|
188
|
-
program.command("upgrade").description("Upgrade installed Snipara local components").option("--with-hooks", "Refresh hooks after upgrade").option("--with-runtime", "Upgrade Snipara Sandbox even if not detected").option("--with-mcp", "Upgrade snipara-mcp even if not detected").option("--with-companion", "Upgrade companion even if not detected").option("--with-orchestrator", "Upgrade snipara-orchestrator even if not detected").action(async (options) => {
|
|
188
|
+
program.command("upgrade").description("Upgrade installed Snipara local components").option("--slug <slug>", "Project slug override").option("--with-hooks", "Refresh hooks after upgrade").option("--with-runtime", "Upgrade Snipara Sandbox even if not detected").option("--with-mcp", "Upgrade snipara-mcp even if not detected").option("--with-companion", "Upgrade companion even if not detected").option("--with-orchestrator", "Upgrade snipara-orchestrator even if not detected").action(async (options) => {
|
|
189
189
|
printBanner();
|
|
190
190
|
await runUpgrade(options);
|
|
191
191
|
});
|
|
@@ -385,7 +385,7 @@ async function runPrintConfig(options) {
|
|
|
385
385
|
repository: githubRepo,
|
|
386
386
|
answerPacks: githubAnswerPacks
|
|
387
387
|
},
|
|
388
|
-
inferredSetup: answers
|
|
388
|
+
inferredSetup: redactSetupAnswers(answers)
|
|
389
389
|
},
|
|
390
390
|
null,
|
|
391
391
|
2
|
|
@@ -395,6 +395,7 @@ async function runPrintConfig(options) {
|
|
|
395
395
|
}
|
|
396
396
|
console.log(chalk.bold("\n\u{1F4E6} Current Setup\n"));
|
|
397
397
|
console.log(` Project slug: ${answers.projectSlug}`);
|
|
398
|
+
console.log(` Project slug source: ${detection.projectSlugSource || "not detected"}`);
|
|
398
399
|
console.log(` Project id: ${answers.projectId || "not-set"}`);
|
|
399
400
|
console.log(` Profile: ${answers.profile}`);
|
|
400
401
|
console.log(` AI client: ${answers.aiClient}`);
|
|
@@ -407,11 +408,21 @@ async function runPrintConfig(options) {
|
|
|
407
408
|
console.log(` Snipara Orchestrator: ${answers.installOrchestrator ? "enabled" : "disabled"}`);
|
|
408
409
|
console.log(` Hooks: ${answers.installHooks ? "enabled" : "disabled"}`);
|
|
409
410
|
console.log(` GitHub repo: ${githubRepo ? githubRepo.fullName : "not detected"}`);
|
|
411
|
+
if (detection.detectedRepoSlug && detection.detectedRepoSlug !== answers.projectSlug) {
|
|
412
|
+
console.log(` Git repo slug: ${detection.detectedRepoSlug}`);
|
|
413
|
+
}
|
|
410
414
|
console.log(` ${GITHUB_ANSWER_PACKS_LABEL}: ${githubAnswerPacks}`);
|
|
411
415
|
console.log(` Existing .mcp.json: ${detection.files.mcpConfig ? "yes" : "no"}`);
|
|
412
416
|
console.log(` Companion pack: ${detection.files.sniparaCompanionPack ? "yes" : "no"}`);
|
|
413
417
|
console.log("");
|
|
414
418
|
}
|
|
419
|
+
function redactSetupAnswers(answers) {
|
|
420
|
+
return {
|
|
421
|
+
...answers,
|
|
422
|
+
apiKey: answers.apiKey.includes("YOUR_") ? answers.apiKey : "[redacted]",
|
|
423
|
+
llmApiKey: answers.llmApiKey ? "[redacted]" : void 0
|
|
424
|
+
};
|
|
425
|
+
}
|
|
415
426
|
function printBanner() {
|
|
416
427
|
console.log(
|
|
417
428
|
chalk.cyan(`
|
|
@@ -431,10 +442,11 @@ function printBanner() {
|
|
|
431
442
|
}
|
|
432
443
|
async function gatherConfiguration(options) {
|
|
433
444
|
console.log(chalk.bold("\n\u{1F4CB} Project Configuration\n"));
|
|
434
|
-
let detectedSlug = detectProjectSlug();
|
|
435
|
-
let detectedProjectId = "";
|
|
436
445
|
const detectedClient = detectAiClient();
|
|
437
446
|
const existingConfig = getExistingConfig();
|
|
447
|
+
const projectSlugResolution = resolveProjectSlug(existingConfig);
|
|
448
|
+
let detectedSlug = projectSlugResolution.slug;
|
|
449
|
+
let detectedProjectId = "";
|
|
438
450
|
const detectedGitRepo = detectGitRepository();
|
|
439
451
|
const showAdvancedPrompts = Boolean(options.advanced);
|
|
440
452
|
if (options.github && !detectedGitRepo) {
|
|
@@ -446,7 +458,6 @@ async function gatherConfiguration(options) {
|
|
|
446
458
|
}
|
|
447
459
|
if (existingConfig) {
|
|
448
460
|
console.log(chalk.dim(" Found existing .mcp.json configuration"));
|
|
449
|
-
detectedSlug = existingConfig.slug || detectedSlug;
|
|
450
461
|
detectedProjectId = existingConfig.projectId || "";
|
|
451
462
|
}
|
|
452
463
|
const initialProfile = resolveRequestedProfile(options);
|
|
@@ -2230,6 +2241,16 @@ async function runValidation(answers, includeHostedCheck, options = {}) {
|
|
|
2230
2241
|
status: answers.projectSlug ? "pass" : "fail",
|
|
2231
2242
|
detail: answers.projectSlug || "Project slug missing"
|
|
2232
2243
|
});
|
|
2244
|
+
const configuredSlugSignals = collectConfiguredProjectSlugSignals(detection.existingConfig);
|
|
2245
|
+
const presentSlugSignals = configuredSlugSignals.filter((signal) => signal.slug);
|
|
2246
|
+
const uniqueConfiguredSlugs = new Set(presentSlugSignals.map((signal) => signal.slug));
|
|
2247
|
+
if (uniqueConfiguredSlugs.size > 1) {
|
|
2248
|
+
checks.push({
|
|
2249
|
+
name: "project_slug_consistency",
|
|
2250
|
+
status: "warn",
|
|
2251
|
+
detail: `Configured slug mismatch: ${presentSlugSignals.map((signal) => `${signal.source}=${signal.slug}`).join(", ")}`
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2233
2254
|
checks.push({
|
|
2234
2255
|
name: "target_client",
|
|
2235
2256
|
status: "pass",
|
|
@@ -2315,9 +2336,11 @@ function writeDoctorReport(report) {
|
|
|
2315
2336
|
function printDoctorReport(detection, report) {
|
|
2316
2337
|
const targetClient = report.checks.find((check) => check.name === "target_client")?.detail || detection.aiClient;
|
|
2317
2338
|
console.log(chalk.bold("\n\u{1FA7A} Snipara Doctor\n"));
|
|
2318
|
-
console.log(
|
|
2319
|
-
|
|
2320
|
-
)
|
|
2339
|
+
console.log(` Project slug: ${detection.projectSlug || "unknown"}`);
|
|
2340
|
+
console.log(` Project slug source: ${detection.projectSlugSource || "not detected"}`);
|
|
2341
|
+
if (detection.detectedRepoSlug && detection.detectedRepoSlug !== detection.projectSlug) {
|
|
2342
|
+
console.log(` Git repo slug: ${detection.detectedRepoSlug}`);
|
|
2343
|
+
}
|
|
2321
2344
|
console.log(` AI client: ${targetClient}`);
|
|
2322
2345
|
console.log(` In git repo: ${detection.inGitRepo ? "yes" : "no"}`);
|
|
2323
2346
|
console.log(` Profile: ${report.profile}`);
|
|
@@ -2629,10 +2652,13 @@ function getClaudeConfigPaths() {
|
|
|
2629
2652
|
}
|
|
2630
2653
|
function detectEnvironment() {
|
|
2631
2654
|
const existingConfig = getExistingConfig();
|
|
2655
|
+
const projectSlugResolution = resolveProjectSlug(existingConfig);
|
|
2632
2656
|
const claudePaths = getClaudeConfigPaths();
|
|
2633
2657
|
return {
|
|
2634
2658
|
cwd: process.cwd(),
|
|
2635
|
-
projectSlug:
|
|
2659
|
+
projectSlug: projectSlugResolution.slug,
|
|
2660
|
+
projectSlugSource: projectSlugResolution.source,
|
|
2661
|
+
detectedRepoSlug: detectRepoProjectSlug(),
|
|
2636
2662
|
aiClient: detectAiClient(),
|
|
2637
2663
|
inGitRepo: isGitRepo(),
|
|
2638
2664
|
files: {
|
|
@@ -2644,6 +2670,7 @@ function detectEnvironment() {
|
|
|
2644
2670
|
claudeMd: fs.existsSync(path.join(process.cwd(), "CLAUDE.md")),
|
|
2645
2671
|
codexConfig: fs.existsSync(path.join(process.cwd(), ".codex", "config.toml")),
|
|
2646
2672
|
cursorRule: fs.existsSync(path.join(process.cwd(), ".cursor", "rules", "snipara.mdc")),
|
|
2673
|
+
sniparaProject: fs.existsSync(path.join(process.cwd(), ".snipara", "project")),
|
|
2647
2674
|
claudeDir: fs.existsSync(path.join(homedir(), ".claude")),
|
|
2648
2675
|
claudeSettings: fs.existsSync(claudePaths.codeSettingsPath),
|
|
2649
2676
|
claudeDesktopConfig: fs.existsSync(claudePaths.desktopConfigPath),
|
|
@@ -2683,7 +2710,7 @@ function loadExistingAnswers(options) {
|
|
|
2683
2710
|
const apiKey = providedApiKey || envApiKey || "YOUR_API_KEY_HERE";
|
|
2684
2711
|
const apiKeyType = effectiveProfile === "runtime-only" ? "none" : providedApiKey || envApiKey ? "api" : "none";
|
|
2685
2712
|
return buildAnswersFromProfile({
|
|
2686
|
-
projectSlug: options.slug || detection.
|
|
2713
|
+
projectSlug: options.slug || detection.projectSlug || "my-project",
|
|
2687
2714
|
projectId: options.projectId || detection.existingConfig.projectId || "",
|
|
2688
2715
|
apiKey,
|
|
2689
2716
|
apiKeyType,
|
|
@@ -2714,17 +2741,157 @@ function resolveMaintenanceProfile(detection, explicitProfile) {
|
|
|
2714
2741
|
function readApiKeyFromEnvFiles() {
|
|
2715
2742
|
const candidates = [".env.local", ".env"];
|
|
2716
2743
|
for (const file of candidates) {
|
|
2717
|
-
const
|
|
2718
|
-
if (
|
|
2744
|
+
const value = readEnvValueFromFile(file, ["SNIPARA_API_KEY", "RLM_API_KEY"]);
|
|
2745
|
+
if (value) {
|
|
2746
|
+
return value;
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
return null;
|
|
2750
|
+
}
|
|
2751
|
+
function resolveProjectSlug(existingConfig = getExistingConfig()) {
|
|
2752
|
+
const configuredSignals = collectConfiguredProjectSlugSignals(existingConfig);
|
|
2753
|
+
const signals = [
|
|
2754
|
+
...configuredSignals,
|
|
2755
|
+
{ source: "git remote", slug: detectRepoProjectSlug() },
|
|
2756
|
+
{ source: "working directory", slug: detectDirectoryProjectSlug() }
|
|
2757
|
+
];
|
|
2758
|
+
const selected = signals.find((signal) => signal.slug);
|
|
2759
|
+
return {
|
|
2760
|
+
slug: selected?.slug || null,
|
|
2761
|
+
source: selected?.source || null,
|
|
2762
|
+
signals
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
function collectConfiguredProjectSlugSignals(existingConfig = getExistingConfig()) {
|
|
2766
|
+
return [
|
|
2767
|
+
{ source: ".mcp.json", slug: normalizeSlugCandidate(existingConfig?.slug) },
|
|
2768
|
+
{ source: ".snipara/project", slug: readProjectSlugFromProjectBinding() },
|
|
2769
|
+
{
|
|
2770
|
+
source: ".codex/config.toml",
|
|
2771
|
+
slug: readProjectSlugFromTextFile(path.join(process.cwd(), ".codex", "config.toml"))
|
|
2772
|
+
},
|
|
2773
|
+
{
|
|
2774
|
+
source: ".snipara/templates/codex-config.toml",
|
|
2775
|
+
slug: readProjectSlugFromTextFile(
|
|
2776
|
+
path.join(process.cwd(), ".snipara", "templates", "codex-config.toml")
|
|
2777
|
+
)
|
|
2778
|
+
},
|
|
2779
|
+
{ source: ".env.local", slug: readProjectSlugFromEnvFile(".env.local") },
|
|
2780
|
+
{ source: ".env", slug: readProjectSlugFromEnvFile(".env") }
|
|
2781
|
+
];
|
|
2782
|
+
}
|
|
2783
|
+
function normalizeSlugCandidate(value) {
|
|
2784
|
+
if (!value) {
|
|
2785
|
+
return null;
|
|
2786
|
+
}
|
|
2787
|
+
const trimmed = value.trim().replace(/^["']|["']$/g, "");
|
|
2788
|
+
if (!trimmed || /^your_/i.test(trimmed) || /^YOUR_/.test(trimmed) || trimmed.includes("<") || trimmed.includes(">")) {
|
|
2789
|
+
return null;
|
|
2790
|
+
}
|
|
2791
|
+
return trimmed;
|
|
2792
|
+
}
|
|
2793
|
+
function slugifyFallback(value) {
|
|
2794
|
+
const normalized = normalizeSlugCandidate(value);
|
|
2795
|
+
if (!normalized) {
|
|
2796
|
+
return null;
|
|
2797
|
+
}
|
|
2798
|
+
const slug = normalized.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2799
|
+
return slug || null;
|
|
2800
|
+
}
|
|
2801
|
+
function extractSlugFromMcpEndpoint(value) {
|
|
2802
|
+
const endpoint = normalizeSlugCandidate(value);
|
|
2803
|
+
if (!endpoint) {
|
|
2804
|
+
return null;
|
|
2805
|
+
}
|
|
2806
|
+
const match = endpoint.match(/\/mcp\/([^/?#\s"']+)/);
|
|
2807
|
+
return normalizeSlugCandidate(match?.[1] ? decodeURIComponent(match[1]) : null);
|
|
2808
|
+
}
|
|
2809
|
+
function extractSlugFromText(content) {
|
|
2810
|
+
const match = content.match(/\/mcp\/([^/?#\s"'`]+)/);
|
|
2811
|
+
return normalizeSlugCandidate(match?.[1] ? decodeURIComponent(match[1]) : null);
|
|
2812
|
+
}
|
|
2813
|
+
function readProjectSlugFromTextFile(filePath) {
|
|
2814
|
+
if (!fs.existsSync(filePath)) {
|
|
2815
|
+
return null;
|
|
2816
|
+
}
|
|
2817
|
+
try {
|
|
2818
|
+
return extractSlugFromText(fs.readFileSync(filePath, "utf-8"));
|
|
2819
|
+
} catch {
|
|
2820
|
+
return null;
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
function readProjectSlugFromProjectBinding() {
|
|
2824
|
+
const filePath = path.join(process.cwd(), ".snipara", "project");
|
|
2825
|
+
if (!fs.existsSync(filePath)) {
|
|
2826
|
+
return null;
|
|
2827
|
+
}
|
|
2828
|
+
try {
|
|
2829
|
+
const content = fs.readFileSync(filePath, "utf-8").trim();
|
|
2830
|
+
if (!content) {
|
|
2831
|
+
return null;
|
|
2832
|
+
}
|
|
2833
|
+
if (content.startsWith("{")) {
|
|
2834
|
+
const parsed = JSON.parse(content);
|
|
2835
|
+
return normalizeSlugCandidate(parsed.slug || parsed.projectSlug || parsed.project_slug);
|
|
2836
|
+
}
|
|
2837
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2838
|
+
const trimmed = line.trim();
|
|
2839
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
2840
|
+
continue;
|
|
2841
|
+
}
|
|
2842
|
+
const keyed = trimmed.match(/^(?:slug|project|projectSlug|project_slug)\s*[:=]\s*(.+)$/i);
|
|
2843
|
+
if (keyed?.[1]) {
|
|
2844
|
+
return normalizeSlugCandidate(stripInlineEnvComment(keyed[1]));
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
const firstValue = content.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !line.startsWith("#"));
|
|
2848
|
+
return normalizeSlugCandidate(firstValue);
|
|
2849
|
+
} catch {
|
|
2850
|
+
return null;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
function readProjectSlugFromEnvFile(file) {
|
|
2854
|
+
const explicitSlug = readEnvValueFromFile(file, ["SNIPARA_PROJECT_SLUG", "RLM_PROJECT_SLUG"]);
|
|
2855
|
+
if (explicitSlug) {
|
|
2856
|
+
return explicitSlug;
|
|
2857
|
+
}
|
|
2858
|
+
const legacySlug = readEnvValueFromFile(file, ["SNIPARA_PROJECT_ID", "RLM_PROJECT_ID"]);
|
|
2859
|
+
if (legacySlug && /^[a-z0-9][a-z0-9-]*$/i.test(legacySlug)) {
|
|
2860
|
+
return legacySlug;
|
|
2861
|
+
}
|
|
2862
|
+
return null;
|
|
2863
|
+
}
|
|
2864
|
+
function readEnvValueFromFile(file, names) {
|
|
2865
|
+
const filePath = path.join(process.cwd(), file);
|
|
2866
|
+
if (!fs.existsSync(filePath)) {
|
|
2867
|
+
return null;
|
|
2868
|
+
}
|
|
2869
|
+
try {
|
|
2719
2870
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
2720
|
-
const
|
|
2721
|
-
|
|
2722
|
-
|
|
2871
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2872
|
+
const match = line.trim().match(/^(?:export\s+)?([A-Z0-9_]+)\s*=\s*(.*)$/);
|
|
2873
|
+
if (!match || !names.includes(match[1])) {
|
|
2874
|
+
continue;
|
|
2875
|
+
}
|
|
2876
|
+
const value = normalizeSlugCandidate(stripInlineEnvComment(match[2]));
|
|
2877
|
+
if (value) {
|
|
2878
|
+
return value;
|
|
2879
|
+
}
|
|
2723
2880
|
}
|
|
2881
|
+
} catch {
|
|
2882
|
+
return null;
|
|
2724
2883
|
}
|
|
2725
2884
|
return null;
|
|
2726
2885
|
}
|
|
2727
|
-
function
|
|
2886
|
+
function stripInlineEnvComment(value) {
|
|
2887
|
+
const trimmed = value.trim();
|
|
2888
|
+
const quoted = trimmed.match(/^(['"])(.*)\1$/);
|
|
2889
|
+
if (quoted) {
|
|
2890
|
+
return quoted[2].trim();
|
|
2891
|
+
}
|
|
2892
|
+
return trimmed.replace(/\s+#.*$/, "").trim();
|
|
2893
|
+
}
|
|
2894
|
+
function detectRepoProjectSlug() {
|
|
2728
2895
|
try {
|
|
2729
2896
|
const remote = execSync("git remote get-url origin", {
|
|
2730
2897
|
encoding: "utf-8",
|
|
@@ -2732,11 +2899,14 @@ function detectProjectSlug() {
|
|
|
2732
2899
|
}).trim();
|
|
2733
2900
|
const match = remote.match(/[/:]([^/]+?)(?:\.git)?$/);
|
|
2734
2901
|
if (match) {
|
|
2735
|
-
return match[1]
|
|
2902
|
+
return slugifyFallback(match[1]);
|
|
2736
2903
|
}
|
|
2737
2904
|
} catch {
|
|
2738
2905
|
}
|
|
2739
|
-
return
|
|
2906
|
+
return null;
|
|
2907
|
+
}
|
|
2908
|
+
function detectDirectoryProjectSlug() {
|
|
2909
|
+
return slugifyFallback(path.basename(process.cwd()));
|
|
2740
2910
|
}
|
|
2741
2911
|
function detectGitRepository() {
|
|
2742
2912
|
try {
|
|
@@ -2854,9 +3024,8 @@ function getExistingConfig() {
|
|
|
2854
3024
|
const config = JSON.parse(fs.readFileSync(mcpConfigPath, "utf-8"));
|
|
2855
3025
|
const sniparaServer = config.mcpServers?.snipara;
|
|
2856
3026
|
const runtimeServer = config.mcpServers?.[SANDBOX_SERVER_NAME] || config.mcpServers?.[LEGACY_RUNTIME_SERVER_NAME];
|
|
2857
|
-
const match = sniparaServer?.url?.match(/\/mcp\/([^/]+)$/);
|
|
2858
3027
|
return {
|
|
2859
|
-
slug:
|
|
3028
|
+
slug: extractSlugFromMcpEndpoint(sniparaServer?.url) || void 0,
|
|
2860
3029
|
projectId: "",
|
|
2861
3030
|
hasSniparaServer: Boolean(sniparaServer),
|
|
2862
3031
|
hasRuntimeServer: Boolean(runtimeServer)
|
package/package.json
CHANGED