create-snipara 1.3.37 → 1.3.38
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 +109 -63
- 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.38",
|
|
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",
|
|
@@ -288,7 +288,8 @@ function parseInitOptionsFromArgv(args) {
|
|
|
288
288
|
return parsed;
|
|
289
289
|
}
|
|
290
290
|
async function runInit(options) {
|
|
291
|
-
const
|
|
291
|
+
const normalizedOptions = normalizeInitOptions(options);
|
|
292
|
+
const answers = await gatherConfiguration(normalizedOptions);
|
|
292
293
|
if (answers.installMcp || answers.installRuntime || answers.installOrchestrator) {
|
|
293
294
|
await installPythonPackages(answers, false);
|
|
294
295
|
}
|
|
@@ -308,9 +309,9 @@ async function runInit(options) {
|
|
|
308
309
|
}
|
|
309
310
|
await updateEnvFiles(answers);
|
|
310
311
|
await createCompanionConfig(answers);
|
|
311
|
-
await syncRemoteAutomationClient(answers, { enabled: !
|
|
312
|
+
await syncRemoteAutomationClient(answers, { enabled: !normalizedOptions.skipTest });
|
|
312
313
|
await createCompanionPack(answers);
|
|
313
|
-
const validationReport = await runValidation(answers, !
|
|
314
|
+
const validationReport = await runValidation(answers, !normalizedOptions.skipTest);
|
|
314
315
|
writeDoctorReport(validationReport);
|
|
315
316
|
printSuccess(answers, validationReport);
|
|
316
317
|
if (answers.configureGitHub && answers.githubRepo) {
|
|
@@ -1137,31 +1138,10 @@ async function createClaudeCodeConfig(answers) {
|
|
|
1137
1138
|
if (answers.profile === "runtime-only") {
|
|
1138
1139
|
return;
|
|
1139
1140
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
console.log(chalk.
|
|
1143
|
-
|
|
1144
|
-
fs.mkdirSync(claudeDir, { recursive: true });
|
|
1145
|
-
let settings = {};
|
|
1146
|
-
if (fs.existsSync(settingsPath)) {
|
|
1147
|
-
settings = asObjectRecord(JSON.parse(fs.readFileSync(settingsPath, "utf-8")));
|
|
1148
|
-
}
|
|
1149
|
-
const mcpServers = asObjectRecord(settings.mcpServers);
|
|
1150
|
-
settings.mcpServers = {
|
|
1151
|
-
...mcpServers,
|
|
1152
|
-
snipara: buildHostedMcpServer(answers)
|
|
1153
|
-
};
|
|
1154
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", { mode: 384 });
|
|
1155
|
-
try {
|
|
1156
|
-
fs.chmodSync(settingsPath, 384);
|
|
1157
|
-
} catch {
|
|
1158
|
-
}
|
|
1159
|
-
console.log(chalk.green(" \u2713 Updated ~/.claude/settings.json with Snipara MCP"));
|
|
1160
|
-
} catch (error) {
|
|
1161
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
1162
|
-
console.log(chalk.yellow(` \u26A0\uFE0F Could not update Claude Code MCP config: ${detail}`));
|
|
1163
|
-
console.log(chalk.dim(" Run create-snipara doctor --client claude after manual setup."));
|
|
1164
|
-
}
|
|
1141
|
+
console.log(chalk.bold("\n\u{1F916} Claude Code MCP\n"));
|
|
1142
|
+
console.log(chalk.green(" \u2713 Project .mcp.json contains the Snipara Hosted MCP server"));
|
|
1143
|
+
console.log(chalk.dim(" Claude Code loads project-scoped MCP servers from .mcp.json."));
|
|
1144
|
+
console.log(chalk.dim(` Verify with: claude mcp list && claude mcp test snipara`));
|
|
1165
1145
|
}
|
|
1166
1146
|
async function createProjectBinding(answers) {
|
|
1167
1147
|
if (answers.profile === "runtime-only") {
|
|
@@ -1469,14 +1449,8 @@ async function createClaudeDesktopConfig(answers) {
|
|
|
1469
1449
|
}
|
|
1470
1450
|
async function installClaudeCodeHooks(answers) {
|
|
1471
1451
|
console.log(chalk.bold("\n\u{1FA9D} Installing Claude Code Hooks\n"));
|
|
1472
|
-
const claudeDir = path.join(
|
|
1452
|
+
const claudeDir = path.join(process.cwd(), ".claude");
|
|
1473
1453
|
const hooksDir = path.join(claudeDir, "hooks");
|
|
1474
|
-
if (!fs.existsSync(claudeDir)) {
|
|
1475
|
-
console.log(
|
|
1476
|
-
chalk.yellow(" \u26A0\uFE0F ~/.claude not found. Install Claude Code first, then rerun repair.\n")
|
|
1477
|
-
);
|
|
1478
|
-
return;
|
|
1479
|
-
}
|
|
1480
1454
|
if (!fs.existsSync(hooksDir)) {
|
|
1481
1455
|
fs.mkdirSync(hooksDir, { recursive: true });
|
|
1482
1456
|
}
|
|
@@ -1513,41 +1487,88 @@ async function installClaudeCodeHooks(answers) {
|
|
|
1513
1487
|
settings = {};
|
|
1514
1488
|
}
|
|
1515
1489
|
}
|
|
1516
|
-
settings.hooks = settings.hooks
|
|
1490
|
+
settings.hooks = asObjectRecord(settings.hooks);
|
|
1517
1491
|
const hooksConfig = settings.hooks;
|
|
1518
1492
|
const hookEntries = [
|
|
1519
1493
|
{
|
|
1520
1494
|
event: "PreToolUse",
|
|
1521
|
-
|
|
1522
|
-
|
|
1495
|
+
entry: {
|
|
1496
|
+
matcher: "Bash|Read|Grep|Glob|Edit|MultiEdit|Write",
|
|
1497
|
+
hooks: [
|
|
1498
|
+
{
|
|
1499
|
+
type: "command",
|
|
1500
|
+
command: createClaudeProjectHookCommand("snipara-stuck-guard.sh"),
|
|
1501
|
+
timeout: 10
|
|
1502
|
+
}
|
|
1503
|
+
]
|
|
1504
|
+
}
|
|
1523
1505
|
},
|
|
1524
1506
|
{
|
|
1525
|
-
event: "
|
|
1526
|
-
|
|
1527
|
-
|
|
1507
|
+
event: "SessionStart",
|
|
1508
|
+
entry: {
|
|
1509
|
+
matcher: "startup|resume|compact|clear",
|
|
1510
|
+
hooks: [
|
|
1511
|
+
{
|
|
1512
|
+
type: "command",
|
|
1513
|
+
command: createClaudeProjectHookCommand("snipara-startup.sh"),
|
|
1514
|
+
timeout: 30
|
|
1515
|
+
}
|
|
1516
|
+
]
|
|
1517
|
+
}
|
|
1528
1518
|
},
|
|
1529
1519
|
{
|
|
1530
1520
|
event: "PostToolUse",
|
|
1531
|
-
|
|
1532
|
-
|
|
1521
|
+
entry: {
|
|
1522
|
+
matcher: "Bash|Read|Grep|Glob|Edit|MultiEdit|Write",
|
|
1523
|
+
hooks: [
|
|
1524
|
+
{
|
|
1525
|
+
type: "command",
|
|
1526
|
+
command: createClaudeProjectHookCommand("snipara-session.sh"),
|
|
1527
|
+
timeout: 10
|
|
1528
|
+
}
|
|
1529
|
+
]
|
|
1530
|
+
}
|
|
1533
1531
|
},
|
|
1534
1532
|
{
|
|
1535
1533
|
event: "PreCompact",
|
|
1536
|
-
|
|
1534
|
+
entry: {
|
|
1535
|
+
matcher: "manual|auto",
|
|
1536
|
+
hooks: [
|
|
1537
|
+
{
|
|
1538
|
+
type: "command",
|
|
1539
|
+
command: createClaudeProjectHookCommand("snipara-compact.sh"),
|
|
1540
|
+
timeout: 30
|
|
1541
|
+
}
|
|
1542
|
+
]
|
|
1543
|
+
}
|
|
1537
1544
|
}
|
|
1538
1545
|
];
|
|
1539
|
-
for (const entry of hookEntries) {
|
|
1540
|
-
const eventHooks = hooksConfig[
|
|
1541
|
-
const
|
|
1542
|
-
|
|
1543
|
-
);
|
|
1546
|
+
for (const { event, entry } of hookEntries) {
|
|
1547
|
+
const eventHooks = Array.isArray(hooksConfig[event]) ? hooksConfig[event] : [];
|
|
1548
|
+
const expectedCommand = entry.hooks[0].command;
|
|
1549
|
+
const exists = eventHooks.some((hook) => hookConfigEntryHasCommand(hook, expectedCommand));
|
|
1544
1550
|
if (!exists) {
|
|
1545
1551
|
eventHooks.push(entry);
|
|
1546
|
-
hooksConfig[
|
|
1552
|
+
hooksConfig[event] = eventHooks;
|
|
1547
1553
|
}
|
|
1548
1554
|
}
|
|
1549
1555
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1550
|
-
console.log(chalk.green(" \u2713 Updated
|
|
1556
|
+
console.log(chalk.green(" \u2713 Updated .claude/settings.json hooks"));
|
|
1557
|
+
}
|
|
1558
|
+
function createClaudeProjectHookCommand(scriptName) {
|
|
1559
|
+
return `bash -lc 'cd "\${CLAUDE_PROJECT_DIR:-$PWD}" && ./.claude/hooks/${scriptName}'`;
|
|
1560
|
+
}
|
|
1561
|
+
function hookConfigEntryHasCommand(entry, expectedCommand) {
|
|
1562
|
+
if (!entry || typeof entry !== "object") {
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
const hooks = entry.hooks;
|
|
1566
|
+
if (!Array.isArray(hooks)) {
|
|
1567
|
+
return false;
|
|
1568
|
+
}
|
|
1569
|
+
return hooks.some(
|
|
1570
|
+
(hook) => hook && typeof hook === "object" && hook.command === expectedCommand
|
|
1571
|
+
);
|
|
1551
1572
|
}
|
|
1552
1573
|
function createStuckGuardHook(answers) {
|
|
1553
1574
|
return `#!/usr/bin/env bash
|
|
@@ -1978,7 +1999,7 @@ function getClientPackFilename(client) {
|
|
|
1978
1999
|
}
|
|
1979
2000
|
function getClientConfigArtifacts(client) {
|
|
1980
2001
|
if (client === "claude-code") {
|
|
1981
|
-
return ["AGENTS.md", "CLAUDE.md", "
|
|
2002
|
+
return ["AGENTS.md", "CLAUDE.md", ".mcp.json MCP server", ".claude/settings.json hooks"];
|
|
1982
2003
|
}
|
|
1983
2004
|
if (client === "cursor") {
|
|
1984
2005
|
return [".mcp.json", ".cursor/rules/snipara.mdc", "dashboard automation bundle when installed"];
|
|
@@ -2237,11 +2258,15 @@ function getClaudeClientChecks(answers, detection) {
|
|
|
2237
2258
|
const checks = [];
|
|
2238
2259
|
const claudePaths = getClaudeConfigPaths();
|
|
2239
2260
|
const projectConfig = readJsonFile(path.join(process.cwd(), ".mcp.json"));
|
|
2261
|
+
const projectSettings = readJsonFile(path.join(process.cwd(), ".claude", "settings.json"));
|
|
2240
2262
|
const codeSettings = readJsonFile(claudePaths.codeSettingsPath);
|
|
2241
2263
|
const desktopConfig = readJsonFile(claudePaths.desktopConfigPath);
|
|
2242
2264
|
const projectHasSnipara = containsSniparaMcpReference(projectConfig);
|
|
2265
|
+
const projectSettingsHasSnipara = containsSniparaMcpReference(projectSettings);
|
|
2243
2266
|
const codeSettingsHasSnipara = containsSniparaMcpReference(codeSettings);
|
|
2267
|
+
const hasLegacyCodeSettingsMcp = projectSettingsHasSnipara || codeSettingsHasSnipara;
|
|
2244
2268
|
const desktopHasSnipara = containsSniparaMcpReference(desktopConfig);
|
|
2269
|
+
const projectHooksConfigured = containsClaudeHooksReference(projectSettings);
|
|
2245
2270
|
checks.push({
|
|
2246
2271
|
name: "claude_project_config",
|
|
2247
2272
|
status: projectHasSnipara ? "pass" : "warn",
|
|
@@ -2250,8 +2275,13 @@ function getClaudeClientChecks(answers, detection) {
|
|
|
2250
2275
|
if (answers.aiClient === "claude-code") {
|
|
2251
2276
|
checks.push({
|
|
2252
2277
|
name: "claude_code_settings",
|
|
2253
|
-
status:
|
|
2254
|
-
detail:
|
|
2278
|
+
status: hasLegacyCodeSettingsMcp ? "warn" : "pass",
|
|
2279
|
+
detail: hasLegacyCodeSettingsMcp ? "Legacy Claude settings contain an MCP server entry; Claude Code project MCP should live in .mcp.json or be managed by claude mcp add --scope project" : "Claude settings are not being used as the MCP destination; .claude/settings.json is hooks-only"
|
|
2280
|
+
});
|
|
2281
|
+
checks.push({
|
|
2282
|
+
name: "claude_code_hooks",
|
|
2283
|
+
status: !answers.installHooks || projectHooksConfigured ? "pass" : "warn",
|
|
2284
|
+
detail: answers.installHooks ? projectHooksConfigured ? "Project .claude/settings.json contains Snipara hook commands" : "Project .claude/settings.json hooks missing; run create-snipara repair --with-hooks" : "Claude hooks not requested"
|
|
2255
2285
|
});
|
|
2256
2286
|
}
|
|
2257
2287
|
if (answers.aiClient === "claude-desktop") {
|
|
@@ -2298,6 +2328,15 @@ function containsSniparaMcpReference(value) {
|
|
|
2298
2328
|
}
|
|
2299
2329
|
return false;
|
|
2300
2330
|
}
|
|
2331
|
+
function containsClaudeHooksReference(value) {
|
|
2332
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2333
|
+
return false;
|
|
2334
|
+
}
|
|
2335
|
+
const hooks = value.hooks;
|
|
2336
|
+
return Boolean(
|
|
2337
|
+
hooks && typeof hooks === "object" && JSON.stringify(hooks).includes(COMPANION_COMMAND_NAME)
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2301
2340
|
function getCreateSniparaPackageFreshnessCheck() {
|
|
2302
2341
|
if (!commandExists("npm")) {
|
|
2303
2342
|
return {
|
|
@@ -2417,8 +2456,8 @@ async function runValidation(answers, includeHostedCheck, options = {}) {
|
|
|
2417
2456
|
});
|
|
2418
2457
|
checks.push({
|
|
2419
2458
|
name: "hooks",
|
|
2420
|
-
status: answers.installHooks && targetClient === "claude-code" ? detection.files.
|
|
2421
|
-
detail: answers.installHooks && targetClient === "claude-code" ? detection.files.
|
|
2459
|
+
status: answers.installHooks && targetClient === "claude-code" ? detection.files.claudeProjectSettings ? "pass" : "warn" : "pass",
|
|
2460
|
+
detail: answers.installHooks && targetClient === "claude-code" ? detection.files.claudeProjectSettings ? "Project .claude/settings.json hooks present" : "Project .claude/settings.json hooks missing" : "Hooks not requested"
|
|
2422
2461
|
});
|
|
2423
2462
|
if (targetClient === "claude-code" || targetClient === "claude-desktop") {
|
|
2424
2463
|
checks.push(...getClaudeClientChecks(effectiveAnswers, detection));
|
|
@@ -2679,7 +2718,7 @@ function printSuccess(answers, report) {
|
|
|
2679
2718
|
}
|
|
2680
2719
|
if (answers.aiClient === "claude-code" && answers.profile !== "runtime-only") {
|
|
2681
2720
|
console.log(
|
|
2682
|
-
chalk.green(" \u2713
|
|
2721
|
+
chalk.green(" \u2713 Claude Code") + chalk.dim(" - .mcp.json MCP config, .claude/settings.json hooks when enabled")
|
|
2683
2722
|
);
|
|
2684
2723
|
}
|
|
2685
2724
|
console.log(chalk.green(" \u2713 .snipara/companion") + chalk.dim(" - Local pack + doctor report"));
|
|
@@ -2799,6 +2838,8 @@ function detectEnvironment() {
|
|
|
2799
2838
|
sniparaProject: fs.existsSync(path.join(process.cwd(), ".snipara", "project")),
|
|
2800
2839
|
claudeDir: fs.existsSync(path.join(homedir(), ".claude")),
|
|
2801
2840
|
claudeSettings: fs.existsSync(claudePaths.codeSettingsPath),
|
|
2841
|
+
claudeProjectDir: fs.existsSync(path.join(process.cwd(), ".claude")),
|
|
2842
|
+
claudeProjectSettings: fs.existsSync(path.join(process.cwd(), ".claude", "settings.json")),
|
|
2802
2843
|
claudeDesktopConfig: fs.existsSync(claudePaths.desktopConfigPath),
|
|
2803
2844
|
cursorDir: fs.existsSync(path.join(homedir(), ".cursor")),
|
|
2804
2845
|
sniparaCompanionPack: fs.existsSync(path.join(process.cwd(), ".snipara", "companion")),
|
|
@@ -3024,7 +3065,8 @@ function detectRepoProjectSlug() {
|
|
|
3024
3065
|
try {
|
|
3025
3066
|
const remote = execSync("git remote get-url origin", {
|
|
3026
3067
|
encoding: "utf-8",
|
|
3027
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3068
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3069
|
+
timeout: 500
|
|
3028
3070
|
}).trim();
|
|
3029
3071
|
const match = remote.match(/[/:]([^/]+?)(?:\.git)?$/);
|
|
3030
3072
|
if (match) {
|
|
@@ -3041,7 +3083,8 @@ function detectGitRepository() {
|
|
|
3041
3083
|
try {
|
|
3042
3084
|
const remoteUrl = execSync("git remote get-url origin", {
|
|
3043
3085
|
encoding: "utf-8",
|
|
3044
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3086
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3087
|
+
timeout: 500
|
|
3045
3088
|
}).trim();
|
|
3046
3089
|
const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/) || remoteUrl.match(/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
3047
3090
|
if (!match) {
|
|
@@ -3065,7 +3108,8 @@ function detectGitBranch() {
|
|
|
3065
3108
|
try {
|
|
3066
3109
|
const branch = execSync("git branch --show-current", {
|
|
3067
3110
|
encoding: "utf-8",
|
|
3068
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3111
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3112
|
+
timeout: 500
|
|
3069
3113
|
}).trim();
|
|
3070
3114
|
if (branch) {
|
|
3071
3115
|
return branch;
|
|
@@ -3075,7 +3119,8 @@ function detectGitBranch() {
|
|
|
3075
3119
|
try {
|
|
3076
3120
|
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
3077
3121
|
encoding: "utf-8",
|
|
3078
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
3122
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3123
|
+
timeout: 500
|
|
3079
3124
|
}).trim();
|
|
3080
3125
|
return branch && branch !== "HEAD" ? branch : "main";
|
|
3081
3126
|
} catch {
|
|
@@ -3085,7 +3130,8 @@ function detectGitBranch() {
|
|
|
3085
3130
|
function isGitRepo() {
|
|
3086
3131
|
try {
|
|
3087
3132
|
execSync("git rev-parse --is-inside-work-tree", {
|
|
3088
|
-
stdio: "ignore"
|
|
3133
|
+
stdio: "ignore",
|
|
3134
|
+
timeout: 500
|
|
3089
3135
|
});
|
|
3090
3136
|
return true;
|
|
3091
3137
|
} catch {
|
package/package.json
CHANGED