@stackmemoryai/stackmemory 0.3.21 → 0.3.22

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,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Debug Railway build issues
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ console.log('šŸ” Railway Build Debugger');
15
+ console.log('========================\n');
16
+
17
+ // Check for server files
18
+ const serverDir = path.join(__dirname, '..', 'dist', 'servers', 'railway');
19
+ const srcDir = path.join(__dirname, '..', 'src', 'servers', 'railway');
20
+
21
+ console.log('šŸ“ Checking dist/servers/railway:');
22
+ if (fs.existsSync(serverDir)) {
23
+ const files = fs.readdirSync(serverDir);
24
+ files.forEach(file => {
25
+ const stats = fs.statSync(path.join(serverDir, file));
26
+ console.log(` - ${file} (${stats.size} bytes, modified: ${stats.mtime.toISOString()})`);
27
+
28
+ // Check for minimal server references
29
+ if (file === 'index.js') {
30
+ const content = fs.readFileSync(path.join(serverDir, file), 'utf-8');
31
+ if (content.includes('Minimal')) {
32
+ console.log(` āš ļø Contains "Minimal" references`);
33
+ }
34
+ if (content.includes('/auth/signup')) {
35
+ console.log(` āœ… Contains auth endpoints`);
36
+ }
37
+ }
38
+ });
39
+ } else {
40
+ console.log(' āŒ Directory does not exist');
41
+ }
42
+
43
+ console.log('\nšŸ“ Checking src/servers/railway:');
44
+ if (fs.existsSync(srcDir)) {
45
+ const files = fs.readdirSync(srcDir);
46
+ files.forEach(file => {
47
+ const stats = fs.statSync(path.join(srcDir, file));
48
+ console.log(` - ${file} (${stats.size} bytes)`);
49
+ });
50
+ } else {
51
+ console.log(' āŒ Directory does not exist');
52
+ }
53
+
54
+ // Check package.json scripts
55
+ console.log('\nšŸ“¦ Package.json start scripts:');
56
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
57
+ Object.entries(packageJson.scripts).forEach(([key, value]) => {
58
+ if (key.includes('start')) {
59
+ console.log(` ${key}: ${value}`);
60
+ }
61
+ });
62
+
63
+ // Check Dockerfile
64
+ console.log('\n🐳 Dockerfile CMD:');
65
+ const dockerfile = fs.readFileSync(path.join(__dirname, '..', 'Dockerfile'), 'utf-8');
66
+ const cmdMatch = dockerfile.match(/CMD\s+\[.*\]/g);
67
+ if (cmdMatch) {
68
+ cmdMatch.forEach(cmd => {
69
+ console.log(` ${cmd}`);
70
+ });
71
+ }
72
+
73
+ // Check Railway config
74
+ console.log('\nšŸš‚ Railway.json:');
75
+ const railwayConfig = path.join(__dirname, '..', 'railway.json');
76
+ if (fs.existsSync(railwayConfig)) {
77
+ const config = JSON.parse(fs.readFileSync(railwayConfig, 'utf-8'));
78
+ console.log(JSON.stringify(config, null, 2));
79
+ } else {
80
+ console.log(' āŒ railway.json not found');
81
+ }
82
+
83
+ console.log('\nšŸ’” Recommendations:');
84
+ console.log('1. Railway may be using a cached build layer');
85
+ console.log('2. Try changing the base image in Dockerfile to force rebuild');
86
+ console.log('3. Check Railway dashboard for any override settings');
87
+ console.log('4. Consider contacting Railway support about cache issues');
@@ -8,7 +8,7 @@ import 'dotenv/config';
8
8
  import fs from 'fs';
9
9
  import readline from 'readline';
10
10
 
11
- const API_KEY = process.env.LINEAR_API_KEY;
11
+ const API_KEY = process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
12
12
  if (!API_KEY) {
13
13
  console.error('āŒ LINEAR_API_KEY environment variable not set');
14
14
  console.log('Please set LINEAR_API_KEY in your .env file or export it in your shell');
@@ -83,7 +83,7 @@ async function deleteLinearTasks() {
83
83
  const response = await fetch('https://api.linear.app/graphql', {
84
84
  method: 'POST',
85
85
  headers: {
86
- 'Authorization': API_KEY,
86
+ 'Authorization': `Bearer ${API_KEY}`,
87
87
  'Content-Type': 'application/json'
88
88
  },
89
89
  body: JSON.stringify({
@@ -0,0 +1,96 @@
1
+ #!/bin/bash
2
+
3
+ # Install Code Execution and Pre-Tool-Use Hooks for StackMemory
4
+
5
+ set -e
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
9
+ CLAUDE_HOOKS_DIR="$HOME/.claude/hooks"
10
+
11
+ echo "šŸš€ Installing StackMemory Code Execution Hooks"
12
+ echo "============================================"
13
+
14
+ # Create hooks directory if it doesn't exist
15
+ if [ ! -d "$CLAUDE_HOOKS_DIR" ]; then
16
+ echo "Creating Claude hooks directory..."
17
+ mkdir -p "$CLAUDE_HOOKS_DIR"
18
+ fi
19
+
20
+ # Build the project first to ensure handlers are compiled
21
+ echo ""
22
+ echo "šŸ“¦ Building project..."
23
+ cd "$PROJECT_ROOT"
24
+ npm run build
25
+
26
+ # Install pre-tool-use hook
27
+ echo ""
28
+ echo "šŸ”’ Installing pre-tool-use hook..."
29
+ if [ -f "$PROJECT_ROOT/templates/claude-hooks/pre-tool-use" ]; then
30
+ # Backup existing hook if present
31
+ if [ -f "$CLAUDE_HOOKS_DIR/pre-tool-use" ]; then
32
+ echo " Backing up existing pre-tool-use hook..."
33
+ cp "$CLAUDE_HOOKS_DIR/pre-tool-use" "$CLAUDE_HOOKS_DIR/pre-tool-use.backup.$(date +%Y%m%d_%H%M%S)"
34
+ fi
35
+
36
+ # Copy new hook
37
+ cp "$PROJECT_ROOT/templates/claude-hooks/pre-tool-use" "$CLAUDE_HOOKS_DIR/"
38
+ chmod +x "$CLAUDE_HOOKS_DIR/pre-tool-use"
39
+ echo " āœ… pre-tool-use hook installed"
40
+ else
41
+ echo " āŒ pre-tool-use hook not found"
42
+ fi
43
+
44
+ # Create configuration file
45
+ echo ""
46
+ echo "āš™ļø Setting up configuration..."
47
+ STACKMEMORY_CONFIG_DIR="$HOME/.stackmemory"
48
+ mkdir -p "$STACKMEMORY_CONFIG_DIR"
49
+
50
+ # Create mode configuration
51
+ cat > "$STACKMEMORY_CONFIG_DIR/tool-mode.conf" << EOF
52
+ # StackMemory Tool Mode Configuration
53
+ # Options: permissive (default), restrictive, code_only
54
+
55
+ STACKMEMORY_TOOL_MODE=permissive
56
+ EOF
57
+
58
+ echo " āœ… Configuration created at $STACKMEMORY_CONFIG_DIR/tool-mode.conf"
59
+
60
+ # Test code execution handler
61
+ echo ""
62
+ echo "🧪 Testing code execution handler..."
63
+ node "$PROJECT_ROOT/scripts/test-code-execution.js" 2>/dev/null || {
64
+ echo " āš ļø Code execution test failed - handler may need dependencies"
65
+ echo " Run: node scripts/test-code-execution.js for details"
66
+ }
67
+
68
+ # Display usage information
69
+ echo ""
70
+ echo "šŸ“ Installation Complete!"
71
+ echo ""
72
+ echo "Usage:"
73
+ echo "------"
74
+ echo "1. Set tool mode (optional):"
75
+ echo " export STACKMEMORY_TOOL_MODE=permissive # Default - all tools allowed"
76
+ echo " export STACKMEMORY_TOOL_MODE=restrictive # Block dangerous tools"
77
+ echo " export STACKMEMORY_TOOL_MODE=code_only # Only code execution allowed"
78
+ echo ""
79
+ echo "2. Or edit: ~/.stackmemory/tool-mode.conf"
80
+ echo ""
81
+ echo "3. View tool usage logs:"
82
+ echo " tail -f ~/.stackmemory/tool-use.log"
83
+ echo ""
84
+ echo "4. Test code execution:"
85
+ echo " node $PROJECT_ROOT/scripts/test-code-execution.js"
86
+ echo ""
87
+ echo "Modes:"
88
+ echo "------"
89
+ echo "• permissive: All tools allowed, dangerous ones logged"
90
+ echo "• restrictive: Blocks Bash, Write, Edit, Delete, WebFetch"
91
+ echo "• code_only: Only Python/JavaScript execution (pure computation)"
92
+ echo ""
93
+ echo "The code_only mode creates a restricted environment similar to"
94
+ echo "execute_code_py, where Claude can only perform computations."
95
+ echo ""
96
+ echo "✨ Ready to use with Claude Code!"
@@ -17,7 +17,7 @@ async function queryLinear(query, variables = {}) {
17
17
  method: 'POST',
18
18
  headers: {
19
19
  'Content-Type': 'application/json',
20
- 'Authorization': process.env.LINEAR_API_KEY
20
+ 'Authorization': process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY
21
21
  },
22
22
  body: JSON.stringify({ query, variables })
23
23
  });
@@ -10,7 +10,7 @@ import path from 'path';
10
10
  import { fileURLToPath } from 'url';
11
11
 
12
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
- const API_KEY = process.env.LINEAR_OAUTH_TOKEN || process.env.LINEAR_API_KEY;
13
+ const API_KEY = process.env.LINEAR_OAUTH_TOKEN || process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
14
14
  if (!API_KEY) {
15
15
  console.error('āŒ LINEAR_OAUTH_TOKEN or LINEAR_API_KEY environment variable not set');
16
16
  console.log('Please set LINEAR_OAUTH_TOKEN or LINEAR_API_KEY in your .env file or export it in your shell');
@@ -16,14 +16,14 @@ dotenv.config({
16
16
  });
17
17
 
18
18
  // Debug: Check if key is loaded
19
- console.log(`API Key loaded: ${process.env.LINEAR_API_KEY ? 'Yes' : 'No'} (length: ${process.env.LINEAR_API_KEY?.length || 0})`);
19
+ console.log(`API Key loaded: ${(process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY) ? 'Yes' : 'No'} (length: ${(process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY)?.length || 0})`);
20
20
 
21
21
  async function queryLinear(query, variables = {}) {
22
22
  const response = await fetch('https://api.linear.app/graphql', {
23
23
  method: 'POST',
24
24
  headers: {
25
25
  'Content-Type': 'application/json',
26
- 'Authorization': process.env.LINEAR_API_KEY
26
+ 'Authorization': process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY
27
27
  },
28
28
  body: JSON.stringify({ query, variables })
29
29
  });
@@ -36,7 +36,7 @@ async function queryLinear(query, variables = {}) {
36
36
  }
37
37
 
38
38
  async function syncLinearTasks() {
39
- const apiKey = process.env.LINEAR_API_KEY;
39
+ const apiKey = process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
40
40
 
41
41
  if (!apiKey) {
42
42
  console.error('āŒ LINEAR_API_KEY not found in environment');
@@ -12,7 +12,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  dotenv.config({ path: path.join(__dirname, '..', '.env') });
13
13
 
14
14
  async function syncLinearTasks() {
15
- const apiKey = process.env.LINEAR_API_KEY;
15
+ const apiKey = process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
16
16
 
17
17
  if (!apiKey) {
18
18
  console.error('āŒ LINEAR_API_KEY not found in environment');
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test code execution MCP handler
5
+ */
6
+
7
+ import { CodeExecutionHandler } from '../dist/integrations/mcp/handlers/code-execution-handlers.js';
8
+
9
+ async function testPythonExecution() {
10
+ console.log('šŸ Testing Python Code Execution\n');
11
+
12
+ const handler = new CodeExecutionHandler();
13
+
14
+ // Test 1: Simple Python code
15
+ console.log('Test 1: Simple calculation');
16
+ const result1 = await handler.executeCode({
17
+ language: 'python',
18
+ code: `
19
+ import math
20
+
21
+ def calculate_circle_area(radius):
22
+ return math.pi * radius ** 2
23
+
24
+ # Test the function
25
+ radius = 5
26
+ area = calculate_circle_area(radius)
27
+ print(f"Circle with radius {radius} has area: {area:.2f}")
28
+
29
+ # Generate some data
30
+ data = [i**2 for i in range(10)]
31
+ print(f"Squares: {data}")
32
+ `,
33
+ });
34
+
35
+ console.log('Success:', result1.success);
36
+ console.log('Output:', result1.stdout);
37
+ if (result1.stderr) console.log('Errors:', result1.stderr);
38
+ console.log('---\n');
39
+
40
+ // Test 2: Code with warnings
41
+ console.log('Test 2: Code with security warnings');
42
+ const code2 = `
43
+ import os
44
+ import subprocess
45
+
46
+ # This should trigger warnings
47
+ print("Current directory:", os.getcwd())
48
+ `;
49
+
50
+ const validation = handler.validateCode(code2);
51
+ console.log('Validation result:', validation);
52
+
53
+ if (!validation.safe) {
54
+ console.log('Executing anyway with force flag...');
55
+ const result2 = await handler.executeCode({
56
+ language: 'python',
57
+ code: code2,
58
+ });
59
+ console.log('Output:', result2.stdout);
60
+ }
61
+ console.log('---\n');
62
+
63
+ // Test 3: JavaScript execution
64
+ console.log('Test 3: JavaScript code');
65
+ const result3 = await handler.executeCode({
66
+ language: 'javascript',
67
+ code: `
68
+ // Calculate fibonacci
69
+ function fibonacci(n) {
70
+ if (n <= 1) return n;
71
+ return fibonacci(n - 1) + fibonacci(n - 2);
72
+ }
73
+
74
+ for (let i = 0; i < 10; i++) {
75
+ console.log(\`fibonacci(\${i}) = \${fibonacci(i)}\`);
76
+ }
77
+
78
+ // Test async operation
79
+ setTimeout(() => {
80
+ console.log('Async operation completed');
81
+ }, 100);
82
+
83
+ // Wait a bit for async
84
+ new Promise(resolve => setTimeout(resolve, 200)).then(() => {
85
+ console.log('Promise resolved');
86
+ });
87
+ `,
88
+ });
89
+
90
+ console.log('Success:', result3.success);
91
+ console.log('Output:', result3.stdout);
92
+ console.log('---\n');
93
+
94
+ // Test 4: Long-running code with timeout
95
+ console.log('Test 4: Timeout test');
96
+ const result4 = await handler.executeCode({
97
+ language: 'python',
98
+ code: `
99
+ import time
100
+ print("Starting long operation...")
101
+ time.sleep(5) # This should timeout if timeout is < 5 seconds
102
+ print("This should not print")
103
+ `,
104
+ timeout: 2000, // 2 second timeout
105
+ });
106
+
107
+ console.log('Success:', result4.success);
108
+ console.log('Output:', result4.stdout);
109
+ console.log('Errors:', result4.stderr);
110
+ console.log('---\n');
111
+
112
+ // Test 5: Large output truncation
113
+ console.log('Test 5: Large output handling');
114
+ const result5 = await handler.executeCode({
115
+ language: 'python',
116
+ code: `
117
+ # Generate large output
118
+ for i in range(10000):
119
+ print(f"Line {i}: {'=' * 50}")
120
+ `,
121
+ });
122
+
123
+ console.log('Success:', result5.success);
124
+ console.log('Truncated:', result5.truncated);
125
+ if (result5.truncated) {
126
+ console.log('Output file:', result5.outputFile);
127
+ }
128
+ console.log('Output length:', result5.stdout.length);
129
+ console.log('---\n');
130
+
131
+ // Get sandbox status
132
+ const status = await handler.getSandboxStatus();
133
+ console.log('šŸ“Š Sandbox Status:', status);
134
+
135
+ // Clean sandbox
136
+ console.log('\n🧹 Cleaning sandbox...');
137
+ await handler.cleanSandbox();
138
+ const statusAfter = await handler.getSandboxStatus();
139
+ console.log('Sandbox after cleanup:', statusAfter);
140
+ }
141
+
142
+ // Run tests
143
+ testPythonExecution().catch(console.error);
@@ -6,7 +6,7 @@
6
6
 
7
7
  import 'dotenv/config';
8
8
 
9
- const API_KEY = process.env.LINEAR_OAUTH_TOKEN || process.env.LINEAR_API_KEY;
9
+ const API_KEY = process.env.LINEAR_OAUTH_TOKEN || process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
10
10
  if (!API_KEY) {
11
11
  console.error('āŒ LINEAR_OAUTH_TOKEN or LINEAR_API_KEY environment variable not set');
12
12
  console.log('Please set LINEAR_OAUTH_TOKEN or LINEAR_API_KEY in your .env file or export it in your shell');
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Validate Railway deployment and check all services
5
+ */
6
+
7
+ async function checkEndpoint(url, expectedKeys = []) {
8
+ try {
9
+ console.log(`\nšŸ“ Checking: ${url}`);
10
+ const response = await fetch(url);
11
+ const data = await response.json();
12
+
13
+ console.log(` Status: ${response.status}`);
14
+ console.log(` Response:`, JSON.stringify(data, null, 2));
15
+
16
+ // Check for expected keys
17
+ for (const key of expectedKeys) {
18
+ if (key in data) {
19
+ console.log(` āœ… Found key: ${key}`);
20
+ } else {
21
+ console.log(` āŒ Missing key: ${key}`);
22
+ }
23
+ }
24
+
25
+ return { success: response.ok, data };
26
+ } catch (error) {
27
+ console.log(` āŒ Error: ${error.message}`);
28
+ return { success: false, error: error.message };
29
+ }
30
+ }
31
+
32
+ async function testAuth(baseUrl) {
33
+ console.log('\nšŸ” Testing Authentication Endpoints:');
34
+
35
+ // Test signup
36
+ const signupData = {
37
+ email: `test${Date.now()}@example.com`,
38
+ password: 'TestPass123!',
39
+ name: 'Test User'
40
+ };
41
+
42
+ console.log('\nšŸ“ Testing POST /auth/signup');
43
+ try {
44
+ const response = await fetch(`${baseUrl}/auth/signup`, {
45
+ method: 'POST',
46
+ headers: { 'Content-Type': 'application/json' },
47
+ body: JSON.stringify(signupData)
48
+ });
49
+
50
+ const data = await response.json();
51
+ console.log(` Status: ${response.status}`);
52
+
53
+ if (response.ok) {
54
+ console.log(' āœ… Signup endpoint works!');
55
+ console.log(` Response:`, JSON.stringify(data, null, 2));
56
+ } else {
57
+ console.log(' āŒ Signup failed:', data.error || data.message);
58
+ }
59
+ } catch (error) {
60
+ console.log(' āŒ Signup endpoint not available:', error.message);
61
+ }
62
+
63
+ // Test login
64
+ console.log('\nšŸ”‘ Testing POST /auth/login');
65
+ try {
66
+ const response = await fetch(`${baseUrl}/auth/login`, {
67
+ method: 'POST',
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify({
70
+ email: signupData.email,
71
+ password: signupData.password
72
+ })
73
+ });
74
+
75
+ const data = await response.json();
76
+ console.log(` Status: ${response.status}`);
77
+
78
+ if (response.ok) {
79
+ console.log(' āœ… Login endpoint works!');
80
+ console.log(` Token received:`, data.token ? 'Yes' : 'No');
81
+ console.log(` API Key:`, data.apiKey ? 'Yes' : 'No');
82
+ } else {
83
+ console.log(' āŒ Login failed:', data.error || data.message);
84
+ }
85
+ } catch (error) {
86
+ console.log(' āŒ Login endpoint not available:', error.message);
87
+ }
88
+ }
89
+
90
+ async function main() {
91
+ const baseUrl = process.argv[2] || 'https://stackmemory-production.up.railway.app';
92
+
93
+ console.log('šŸš€ StackMemory Railway Deployment Validator');
94
+ console.log('==========================================');
95
+ console.log(`Base URL: ${baseUrl}`);
96
+ console.log(`Timestamp: ${new Date().toISOString()}`);
97
+
98
+ // Check root endpoint
99
+ const root = await checkEndpoint(baseUrl, ['message', 'version', 'endpoints']);
100
+
101
+ // Detect server type
102
+ if (root.data?.message?.includes('Minimal')) {
103
+ console.log('\nāš ļø WARNING: Minimal server is running!');
104
+ console.log(' The full server with auth endpoints is not deployed.');
105
+ } else if (root.data?.message?.includes('API Server')) {
106
+ console.log('\nāœ… Full API server is running!');
107
+ }
108
+
109
+ // Check health
110
+ await checkEndpoint(`${baseUrl}/health`, ['status']);
111
+
112
+ // Check database connections
113
+ const dbTest = await checkEndpoint(`${baseUrl}/test-db`, ['postgresql', 'redis']);
114
+
115
+ if (dbTest.data) {
116
+ console.log('\nšŸ“Š Database Status:');
117
+ if (dbTest.data.postgresql?.status === 'connected') {
118
+ console.log(' āœ… PostgreSQL: Connected');
119
+ } else {
120
+ console.log(' āŒ PostgreSQL:', dbTest.data.postgresql?.status || 'Not configured');
121
+ }
122
+
123
+ if (dbTest.data.redis?.status === 'connected') {
124
+ console.log(' āœ… Redis: Connected');
125
+ } else {
126
+ console.log(' āŒ Redis:', dbTest.data.redis?.status || 'Not configured');
127
+ }
128
+ }
129
+
130
+ // Test auth endpoints
131
+ await testAuth(baseUrl);
132
+
133
+ console.log('\n==========================================');
134
+ console.log('Validation complete!');
135
+ }
136
+
137
+ main().catch(console.error);
@@ -0,0 +1,59 @@
1
+ {
2
+ "pre-tool-use": {
3
+ "description": "Controls tool usage based on configured policies",
4
+ "version": "1.0.0",
5
+ "modes": {
6
+ "permissive": {
7
+ "description": "Allow all tools but log dangerous operations",
8
+ "default": true,
9
+ "restrictions": []
10
+ },
11
+ "restrictive": {
12
+ "description": "Block potentially dangerous tools",
13
+ "restrictions": ["Bash", "Write", "Edit", "Delete", "WebFetch"],
14
+ "allowed": ["TodoWrite", "TodoRead", "Read", "Grep", "LS"]
15
+ },
16
+ "code_only": {
17
+ "description": "Only allow code execution tools - pure computational environment",
18
+ "restrictions": ["*"],
19
+ "allowed": ["mcp__stackmemory__code.execute", "mcp__code-executor__execute_code", "TodoWrite", "TodoRead"]
20
+ }
21
+ },
22
+ "alwaysAllowed": [
23
+ "mcp__stackmemory__save_context",
24
+ "mcp__stackmemory__load_context",
25
+ "TodoWrite",
26
+ "TodoRead"
27
+ ],
28
+ "dangerousTools": [
29
+ "Bash",
30
+ "Write",
31
+ "Edit",
32
+ "Delete",
33
+ "WebFetch",
34
+ "MultiEdit"
35
+ ],
36
+ "codeExecutionTools": [
37
+ "mcp__code-executor__execute_code",
38
+ "mcp__stackmemory__code.execute"
39
+ ]
40
+ },
41
+ "installation": {
42
+ "commands": [
43
+ "# Install pre-tool-use hook",
44
+ "cp templates/claude-hooks/pre-tool-use ~/.claude/hooks/",
45
+ "chmod +x ~/.claude/hooks/pre-tool-use",
46
+ "",
47
+ "# Set mode (optional, defaults to permissive)",
48
+ "export STACKMEMORY_TOOL_MODE=permissive # or restrictive, code_only",
49
+ "",
50
+ "# View tool usage logs",
51
+ "tail -f ~/.stackmemory/tool-use.log"
52
+ ]
53
+ },
54
+ "usage": {
55
+ "permissive_mode": "Default mode - all tools allowed with logging",
56
+ "restrictive_mode": "Blocks dangerous tools like Bash, Write, Edit",
57
+ "code_only_mode": "Only code execution allowed - ideal for pure computation tasks"
58
+ }
59
+ }