claude-all-config 3.8.3 → 3.9.0

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/.env.example CHANGED
@@ -50,3 +50,8 @@ TELEGRAM_API_HASH=
50
50
  # ANTHROPIC_API_KEY=
51
51
  # OPENAI_API_KEY=
52
52
  # GOOGLE_API_KEY=
53
+
54
+ # ─────────────────────────────────────────────────────────────
55
+ # GitHub token for ai-memory private repo sync
56
+ # ─────────────────────────────────────────────────────────────
57
+ GH_TOKEN=
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env bash
2
+ # ai-memory — Sync shared AI context across devices
3
+ # Fully automatic: pull at session start, push at session end.
4
+ # Usage:
5
+ # ai-memory pull — Load latest context (auto at session start)
6
+ # ai-memory push — Push updates (auto at session end)
7
+ # ai-memory context — Print context for AI injection
8
+ # ai-memory log "message" — Quick note to active-tasks
9
+
10
+ set -euo pipefail
11
+
12
+ MEMORY_DIR="${AI_MEMORY_DIR:-$HOME/ai-memory}"
13
+ REPO_URL="${AI_MEMORY_REPO:-https://github.com/zesbe/ai-memory.git}"
14
+
15
+ # Inject token for private repo
16
+ if [[ -n "${GH_TOKEN:-}" ]]; then
17
+ REPO_URL="https://x-access-token:${GH_TOKEN}@github.com/zesbe/ai-memory.git"
18
+ elif [[ -n "${GITHUB_TOKEN:-}" ]]; then
19
+ REPO_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/zesbe/ai-memory.git"
20
+ fi
21
+
22
+ # Load token from .env if not in env
23
+ if [[ -z "${GH_TOKEN:-}" && -z "${GITHUB_TOKEN:-}" ]]; then
24
+ for envfile in "$HOME/.claude/.env" "$HOME/.kiro/.env"; do
25
+ if [[ -f "$envfile" ]]; then
26
+ GH_TOKEN=$(grep -m1 "^GH_TOKEN=" "$envfile" 2>/dev/null | cut -d= -f2) || true
27
+ if [[ -n "${GH_TOKEN:-}" ]]; then
28
+ REPO_URL="https://x-access-token:${GH_TOKEN}@github.com/zesbe/ai-memory.git"
29
+ break
30
+ fi
31
+ fi
32
+ done
33
+ fi
34
+
35
+ ensure_repo() {
36
+ if [[ ! -d "$MEMORY_DIR/.git" ]]; then
37
+ git clone --depth 1 "$REPO_URL" "$MEMORY_DIR" 2>/dev/null || return 1
38
+ cd "$MEMORY_DIR" && git config user.email "ai@memory" && git config user.name "ai-memory"
39
+ fi
40
+ }
41
+
42
+ case "${1:-context}" in
43
+ pull)
44
+ ensure_repo || exit 0
45
+ cd "$MEMORY_DIR"
46
+ git pull --rebase --quiet 2>/dev/null || true
47
+ ;;
48
+ push)
49
+ ensure_repo || exit 0
50
+ cd "$MEMORY_DIR"
51
+ git pull --rebase --quiet 2>/dev/null || true
52
+ git add -A 2>/dev/null
53
+ if ! git diff --cached --quiet 2>/dev/null; then
54
+ git commit -m "auto: $(date +%Y-%m-%d_%H:%M) from $(hostname -s 2>/dev/null || echo unknown)" --quiet 2>/dev/null
55
+ git push --quiet 2>/dev/null || true
56
+ fi
57
+ ;;
58
+ context)
59
+ ensure_repo || exit 0
60
+ cd "$MEMORY_DIR"
61
+ git pull --rebase --quiet 2>/dev/null || true
62
+ echo "--- AI MEMORY CONTEXT ---"
63
+ cat "$MEMORY_DIR/CONTEXT.md" 2>/dev/null || true
64
+ echo ""
65
+ echo "--- ACTIVE TASKS ---"
66
+ cat "$MEMORY_DIR/active-tasks/current.md" 2>/dev/null || true
67
+ echo "--- END AI MEMORY ---"
68
+ ;;
69
+ log)
70
+ ensure_repo || exit 0
71
+ shift
72
+ echo "- $(date +%H:%M) $*" >> "$MEMORY_DIR/active-tasks/current.md"
73
+ cd "$MEMORY_DIR" && git add -A && git commit -m "log: $*" --quiet 2>/dev/null && git push --quiet 2>/dev/null || true
74
+ ;;
75
+ *)
76
+ echo "Usage: ai-memory [pull|push|context|log \"message\"]"
77
+ ;;
78
+ esac
@@ -0,0 +1,317 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Auto-Documentation Hook
4
+ Updates documentation when code changes: API specs, README, comments
5
+ """
6
+ import subprocess
7
+ import json
8
+ import sys
9
+ import os
10
+ import re
11
+ from datetime import datetime
12
+
13
+ def run_command(cmd):
14
+ """Run shell command and return output"""
15
+ try:
16
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
17
+ return result.stdout.strip(), result.stderr.strip(), result.returncode
18
+ except Exception as e:
19
+ return "", str(e), 1
20
+
21
+ def get_file_from_stdin():
22
+ """Get file path from Claude tool input"""
23
+ try:
24
+ # Read from stdin if available
25
+ if not sys.stdin.isatty():
26
+ input_data = json.load(sys.stdin)
27
+ if "tool_input" in input_data:
28
+ tool_input = input_data["tool_input"]
29
+ if "path" in tool_input:
30
+ return tool_input["path"]
31
+ elif "file_path" in tool_input:
32
+ return tool_input["file_path"]
33
+
34
+ # Fallback: get from environment variable
35
+ return os.environ.get("CLAUDE_TOOL_INPUT_FILE_PATH")
36
+ except:
37
+ return None
38
+
39
+ def is_api_file(file_path):
40
+ """Check if file contains API endpoints"""
41
+ if not file_path or not os.path.exists(file_path):
42
+ return False
43
+
44
+ api_patterns = [
45
+ r'@app\.route\(', # Flask
46
+ r'app\.(get|post|put|delete|patch)\(', # Express
47
+ r'router\.(get|post|put|delete|patch)\(', # Express Router
48
+ r'@(Get|Post|Put|Delete|Patch)\(', # NestJS/Spring
49
+ r'func.*http\.Handler', # Go HTTP
50
+ r'#\[actix_web::(get|post|put|delete|patch)\]', # Actix Web (Rust)
51
+ ]
52
+
53
+ try:
54
+ with open(file_path, 'r', encoding='utf-8') as f:
55
+ content = f.read()
56
+ return any(re.search(pattern, content, re.IGNORECASE) for pattern in api_patterns)
57
+ except:
58
+ return False
59
+
60
+ def is_component_file(file_path):
61
+ """Check if file is a UI component"""
62
+ if not file_path:
63
+ return False
64
+
65
+ # Check file extension and naming patterns
66
+ component_patterns = [
67
+ r'\.component\.(js|ts|jsx|tsx)$',
68
+ r'components/.*\.(js|ts|jsx|tsx)$',
69
+ r'\.vue$',
70
+ r'\.svelte$'
71
+ ]
72
+
73
+ return any(re.search(pattern, file_path, re.IGNORECASE) for pattern in component_patterns)
74
+
75
+ def extract_api_endpoints(file_path):
76
+ """Extract API endpoints from file"""
77
+ endpoints = []
78
+
79
+ try:
80
+ with open(file_path, 'r', encoding='utf-8') as f:
81
+ content = f.read()
82
+ lines = content.split('\n')
83
+
84
+ for i, line in enumerate(lines):
85
+ # Flask routes
86
+ flask_match = re.search(r'@app\.route\([\'"]([^\'"]+)[\'"].*methods=\[([^\]]+)\]', line)
87
+ if flask_match:
88
+ path = flask_match.group(1)
89
+ methods = flask_match.group(2).replace("'", "").replace('"', "")
90
+ endpoints.append(f"{methods} {path}")
91
+
92
+ # Express routes
93
+ express_match = re.search(r'app\.(get|post|put|delete|patch)\([\'"]([^\'"]+)[\'"]', line)
94
+ if express_match:
95
+ method = express_match.group(1).upper()
96
+ path = express_match.group(2)
97
+ endpoints.append(f"{method} {path}")
98
+
99
+ # Router routes
100
+ router_match = re.search(r'router\.(get|post|put|delete|patch)\([\'"]([^\'"]+)[\'"]', line)
101
+ if router_match:
102
+ method = router_match.group(1).upper()
103
+ path = router_match.group(2)
104
+ endpoints.append(f"{method} {path}")
105
+ except:
106
+ pass
107
+
108
+ return endpoints
109
+
110
+ def update_api_documentation(file_path):
111
+ """Update API documentation"""
112
+ results = []
113
+
114
+ endpoints = extract_api_endpoints(file_path)
115
+ if not endpoints:
116
+ return results
117
+
118
+ # Update OpenAPI spec if exists
119
+ openapi_files = ["openapi.yaml", "openapi.yml", "swagger.yaml", "swagger.yml", "api-spec.yaml"]
120
+ for spec_file in openapi_files:
121
+ if os.path.exists(spec_file):
122
+ results.append(f"📝 Found API spec: {spec_file} (manual update needed)")
123
+ break
124
+
125
+ # Update API documentation file
126
+ api_doc_file = "API.md"
127
+ if os.path.exists(api_doc_file):
128
+ try:
129
+ with open(api_doc_file, 'r') as f:
130
+ content = f.read()
131
+
132
+ # Add new endpoints section if not exists
133
+ if "## Endpoints" not in content:
134
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
135
+ new_section = f"\n\n## Endpoints\n\n*Last updated: {timestamp}*\n\n"
136
+ for endpoint in endpoints:
137
+ new_section += f"- `{endpoint}`\n"
138
+
139
+ with open(api_doc_file, 'a') as f:
140
+ f.write(new_section)
141
+
142
+ results.append(f"📝 Updated {api_doc_file} with {len(endpoints)} endpoints")
143
+ except:
144
+ pass
145
+ else:
146
+ # Create new API documentation
147
+ try:
148
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
149
+ with open(api_doc_file, 'w') as f:
150
+ f.write(f"# API Documentation\n\n*Generated: {timestamp}*\n\n## Endpoints\n\n")
151
+ for endpoint in endpoints:
152
+ f.write(f"- `{endpoint}`\n")
153
+
154
+ results.append(f"📝 Created {api_doc_file} with {len(endpoints)} endpoints")
155
+ except:
156
+ pass
157
+
158
+ return results
159
+
160
+ def update_component_documentation(file_path):
161
+ """Update component documentation"""
162
+ results = []
163
+
164
+ if not is_component_file(file_path):
165
+ return results
166
+
167
+ component_name = os.path.splitext(os.path.basename(file_path))[0]
168
+
169
+ # Check for Storybook
170
+ storybook_patterns = [".storybook", "stories", "*.stories.js", "*.stories.ts"]
171
+ has_storybook = any(os.path.exists(pattern) or len(glob.glob(pattern)) > 0 for pattern in storybook_patterns)
172
+
173
+ if has_storybook:
174
+ results.append(f"📚 Component {component_name} - Storybook available (manual story update recommended)")
175
+
176
+ # Update component README if exists
177
+ component_dir = os.path.dirname(file_path)
178
+ readme_file = os.path.join(component_dir, "README.md")
179
+
180
+ if os.path.exists(readme_file):
181
+ results.append(f"📝 Component README exists: {readme_file}")
182
+
183
+ return results
184
+
185
+ def add_function_comments(file_path):
186
+ """Add JSDoc/docstring comments to functions"""
187
+ results = []
188
+
189
+ if not file_path or not os.path.exists(file_path):
190
+ return results
191
+
192
+ try:
193
+ with open(file_path, 'r', encoding='utf-8') as f:
194
+ content = f.read()
195
+
196
+ # Count functions without comments
197
+ function_patterns = [
198
+ r'function\s+(\w+)\s*\(', # JavaScript functions
199
+ r'const\s+(\w+)\s*=\s*\(', # Arrow functions
200
+ r'def\s+(\w+)\s*\(', # Python functions
201
+ r'func\s+(\w+)\s*\(', # Go functions
202
+ ]
203
+
204
+ functions_found = 0
205
+ for pattern in function_patterns:
206
+ matches = re.findall(pattern, content)
207
+ functions_found += len(matches)
208
+
209
+ if functions_found > 0:
210
+ results.append(f"📋 Found {functions_found} functions in {os.path.basename(file_path)} (consider adding documentation)")
211
+ except:
212
+ pass
213
+
214
+ return results
215
+
216
+ def update_readme():
217
+ """Update README.md with project info"""
218
+ results = []
219
+
220
+ if not os.path.exists("README.md"):
221
+ return results
222
+
223
+ try:
224
+ with open("README.md", 'r') as f:
225
+ content = f.read()
226
+
227
+ # Check if README needs updates
228
+ needs_update = False
229
+
230
+ # Check for outdated "Last updated" timestamp
231
+ last_updated_match = re.search(r'Last updated:?\s*([^\n]+)', content, re.IGNORECASE)
232
+ if last_updated_match:
233
+ # Update timestamp
234
+ current_time = datetime.now().strftime("%Y-%m-%d")
235
+ new_content = re.sub(
236
+ r'Last updated:?\s*[^\n]+',
237
+ f'Last updated: {current_time}',
238
+ content,
239
+ flags=re.IGNORECASE
240
+ )
241
+
242
+ if new_content != content:
243
+ with open("README.md", 'w') as f:
244
+ f.write(new_content)
245
+ results.append("📝 Updated README.md timestamp")
246
+
247
+ # Check for missing sections
248
+ missing_sections = []
249
+ required_sections = ["Installation", "Usage", "API", "Contributing"]
250
+
251
+ for section in required_sections:
252
+ if f"## {section}" not in content and f"# {section}" not in content:
253
+ missing_sections.append(section)
254
+
255
+ if missing_sections:
256
+ results.append(f"📝 README.md missing sections: {', '.join(missing_sections)}")
257
+
258
+ except:
259
+ pass
260
+
261
+ return results
262
+
263
+ def generate_changelog_entry(file_path):
264
+ """Generate changelog entry for significant changes"""
265
+ results = []
266
+
267
+ if not os.path.exists("CHANGELOG.md"):
268
+ return results
269
+
270
+ # Get recent git commits for this file
271
+ stdout, _, code = run_command(f"git log --oneline -3 -- {file_path} 2>/dev/null")
272
+ if code == 0 and stdout:
273
+ commits = stdout.strip().split('\n')
274
+ if commits and commits[0]:
275
+ latest_commit = commits[0]
276
+ results.append(f"📝 Latest change to {os.path.basename(file_path)}: {latest_commit}")
277
+
278
+ return results
279
+
280
+ def main():
281
+ try:
282
+ file_path = get_file_from_stdin()
283
+
284
+ if not file_path:
285
+ sys.exit(0) # No file to process
286
+
287
+ results = []
288
+
289
+ # API documentation
290
+ if is_api_file(file_path):
291
+ results.extend(update_api_documentation(file_path))
292
+
293
+ # Component documentation
294
+ if is_component_file(file_path):
295
+ results.extend(update_component_documentation(file_path))
296
+
297
+ # Function comments
298
+ results.extend(add_function_comments(file_path))
299
+
300
+ # README updates
301
+ results.extend(update_readme())
302
+
303
+ # Changelog
304
+ results.extend(generate_changelog_entry(file_path))
305
+
306
+ # Output results
307
+ if results:
308
+ print("📚 AUTO-DOCUMENTATION UPDATES:")
309
+ for result in results:
310
+ print(f" {result}")
311
+
312
+ except Exception as e:
313
+ print(f"❌ Auto-documentation error: {e}")
314
+ sys.exit(0) # Don't block Claude
315
+
316
+ if __name__ == "__main__":
317
+ main()
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Deployment Pipeline Hook
4
+ Auto-deploy when conditions are met: tests pass, on main branch, etc.
5
+ """
6
+ import subprocess
7
+ import json
8
+ import sys
9
+ import os
10
+ from datetime import datetime
11
+
12
+ def run_command(cmd):
13
+ """Run shell command and return output"""
14
+ try:
15
+ result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
16
+ return result.stdout.strip(), result.stderr.strip(), result.returncode
17
+ except Exception as e:
18
+ return "", str(e), 1
19
+
20
+ def get_current_branch():
21
+ """Get current git branch"""
22
+ stdout, _, code = run_command("git branch --show-current")
23
+ return stdout if code == 0 else None
24
+
25
+ def has_package_json():
26
+ """Check if package.json exists"""
27
+ return os.path.exists("package.json")
28
+
29
+ def has_dockerfile():
30
+ """Check if Dockerfile exists"""
31
+ return os.path.exists("Dockerfile")
32
+
33
+ def has_docker_compose():
34
+ """Check if docker-compose.yml exists"""
35
+ return os.path.exists("docker-compose.yml") or os.path.exists("docker-compose.yaml")
36
+
37
+ def run_tests():
38
+ """Run tests based on project type"""
39
+ if has_package_json():
40
+ # Node.js project
41
+ stdout, stderr, code = run_command("npm test 2>/dev/null || npm run test 2>/dev/null")
42
+ return code == 0, stdout, stderr
43
+
44
+ # Python project
45
+ if os.path.exists("pytest.ini") or os.path.exists("pyproject.toml"):
46
+ stdout, stderr, code = run_command("python -m pytest 2>/dev/null")
47
+ return code == 0, stdout, stderr
48
+
49
+ # Go project
50
+ if os.path.exists("go.mod"):
51
+ stdout, stderr, code = run_command("go test ./... 2>/dev/null")
52
+ return code == 0, stdout, stderr
53
+
54
+ # No tests found, assume OK
55
+ return True, "No tests configured", ""
56
+
57
+ def build_and_deploy():
58
+ """Build and deploy based on project configuration"""
59
+ results = []
60
+
61
+ # Docker build
62
+ if has_dockerfile():
63
+ print("🐳 Building Docker image...")
64
+ app_name = os.path.basename(os.getcwd())
65
+ stdout, stderr, code = run_command(f"docker build -t {app_name}:latest .")
66
+
67
+ if code == 0:
68
+ results.append("✅ Docker build successful")
69
+
70
+ # Try to restart if docker-compose exists
71
+ if has_docker_compose():
72
+ stdout, stderr, code = run_command("docker-compose up -d --no-deps")
73
+ if code == 0:
74
+ results.append("✅ Service restarted via docker-compose")
75
+ else:
76
+ results.append(f"⚠️ Docker-compose restart failed: {stderr}")
77
+ else:
78
+ results.append(f"❌ Docker build failed: {stderr}")
79
+ return False, results
80
+
81
+ # Node.js build
82
+ elif has_package_json():
83
+ print("📦 Building Node.js project...")
84
+ stdout, stderr, code = run_command("npm run build 2>/dev/null")
85
+
86
+ if code == 0:
87
+ results.append("✅ Node.js build successful")
88
+
89
+ # Try to restart PM2 process
90
+ app_name = os.path.basename(os.getcwd())
91
+ stdout, stderr, code = run_command(f"pm2 restart {app_name} 2>/dev/null")
92
+ if code == 0:
93
+ results.append("✅ PM2 process restarted")
94
+ else:
95
+ results.append(f"⚠️ Build failed: {stderr}")
96
+
97
+ return True, results
98
+
99
+ def send_notification(message):
100
+ """Send deployment notification"""
101
+ # Try Telegram notification if configured
102
+ if os.path.exists(os.path.expanduser("~/.kiro/hooks/telegram-alerts.py")):
103
+ run_command(f'python ~/.kiro/hooks/telegram-alerts.py "🚀 {message}"')
104
+
105
+ print(f"📢 {message}")
106
+
107
+ def should_deploy():
108
+ """Check if we should deploy"""
109
+ # Only deploy on main/master branch
110
+ branch = get_current_branch()
111
+ if branch not in ['main', 'master']:
112
+ print(f"⏭️ Skipping deploy - not on main branch (current: {branch})")
113
+ return False
114
+
115
+ # Check if there are uncommitted changes
116
+ stdout, _, code = run_command("git status --porcelain")
117
+ if code == 0 and stdout.strip():
118
+ print("⏭️ Skipping deploy - uncommitted changes detected")
119
+ return False
120
+
121
+ return True
122
+
123
+ def main():
124
+ try:
125
+ # Check if we're in a git repo
126
+ stdout, _, code = run_command("git rev-parse --git-dir")
127
+ if code != 0:
128
+ sys.exit(0) # Not a git repo, skip
129
+
130
+ # Check deployment conditions
131
+ if not should_deploy():
132
+ sys.exit(0)
133
+
134
+ print("🔍 Running pre-deployment checks...")
135
+
136
+ # Run tests
137
+ tests_passed, test_output, test_error = run_tests()
138
+
139
+ if not tests_passed:
140
+ print(f"❌ Tests failed - aborting deployment")
141
+ print(f"Error: {test_error}")
142
+ sys.exit(0)
143
+
144
+ print("✅ All tests passed")
145
+
146
+ # Build and deploy
147
+ print("🚀 Starting deployment...")
148
+ deploy_success, deploy_results = build_and_deploy()
149
+
150
+ if deploy_success:
151
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
152
+ app_name = os.path.basename(os.getcwd())
153
+
154
+ # Log deployment
155
+ with open(".deployment.log", "a") as f:
156
+ f.write(f"{timestamp} - Deployment successful\n")
157
+
158
+ # Send notification
159
+ send_notification(f"Deployment successful: {app_name} @ {timestamp}")
160
+
161
+ # Print results
162
+ for result in deploy_results:
163
+ print(result)
164
+ else:
165
+ print("❌ Deployment failed")
166
+ for result in deploy_results:
167
+ print(result)
168
+
169
+ except Exception as e:
170
+ print(f"❌ Deployment pipeline error: {e}")
171
+ sys.exit(0) # Don't block Claude
172
+
173
+ if __name__ == "__main__":
174
+ main()