@musashishao/agent-kit 1.6.1 → 1.7.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.
Files changed (64) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +487 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +76 -0
  28. package/.agent/adr/ADR-TEMPLATE.md +57 -0
  29. package/.agent/adr/README.md +30 -0
  30. package/.agent/agents/backend-specialist.md +1 -1
  31. package/.agent/agents/devops-engineer.md +1 -1
  32. package/.agent/agents/performance-optimizer.md +1 -1
  33. package/.agent/agents/security-auditor.md +1 -1
  34. package/.agent/dashboard/index.html +169 -0
  35. package/.agent/rules/REFERENCE.md +14 -0
  36. package/.agent/skills/ai-incident-management/SKILL.md +517 -0
  37. package/.agent/skills/ai-security-guardrails/SKILL.md +405 -0
  38. package/.agent/skills/ai-security-guardrails/owasp-llm-top10.md +160 -0
  39. package/.agent/skills/ai-security-guardrails/scripts/prompt_injection_scanner.py +230 -0
  40. package/.agent/skills/compliance-for-ai/SKILL.md +411 -0
  41. package/.agent/skills/observability-patterns/SKILL.md +484 -0
  42. package/.agent/skills/observability-patterns/scripts/otel_validator.py +330 -0
  43. package/.agent/skills/opentelemetry-expert/SKILL.md +738 -0
  44. package/.agent/skills/opentelemetry-expert/scripts/trace_analyzer.py +351 -0
  45. package/.agent/skills/privacy-preserving-dev/SKILL.md +442 -0
  46. package/.agent/skills/privacy-preserving-dev/scripts/pii_scanner.py +285 -0
  47. package/.agent/workflows/autofix.md +4 -1
  48. package/.agent/workflows/brainstorm.md +1 -1
  49. package/.agent/workflows/context.md +3 -1
  50. package/.agent/workflows/create.md +1 -1
  51. package/.agent/workflows/dashboard.md +4 -1
  52. package/.agent/workflows/debug.md +1 -1
  53. package/.agent/workflows/deploy.md +1 -1
  54. package/.agent/workflows/enhance.md +1 -1
  55. package/.agent/workflows/next.md +4 -1
  56. package/.agent/workflows/orchestrate.md +1 -1
  57. package/.agent/workflows/plan.md +1 -1
  58. package/.agent/workflows/preview.md +1 -1
  59. package/.agent/workflows/quality.md +1 -1
  60. package/.agent/workflows/spec.md +1 -1
  61. package/.agent/workflows/status.md +1 -1
  62. package/.agent/workflows/test.md +1 -1
  63. package/.agent/workflows/ui-ux-pro-max.md +1 -1
  64. package/package.json +4 -1
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill: observability-patterns
4
+ Script: otel_validator.py
5
+ Purpose: Validate OpenTelemetry instrumentation in AI applications
6
+ Usage: python otel_validator.py <project_path> [--output json|summary]
7
+ Output: JSON with instrumentation findings
8
+
9
+ This script checks for:
10
+ 1. OpenTelemetry SDK setup
11
+ 2. LLM call instrumentation
12
+ 3. Proper span attributes for AI
13
+ 4. Context propagation patterns
14
+ """
15
+ import os
16
+ import sys
17
+ import re
18
+ import json
19
+ import argparse
20
+ from pathlib import Path
21
+ from typing import Dict, List, Any
22
+ from datetime import datetime
23
+
24
+ # Fix console encoding
25
+ try:
26
+ sys.stdout.reconfigure(encoding='utf-8', errors='replace')
27
+ sys.stderr.reconfigure(encoding='utf-8', errors='replace')
28
+ except AttributeError:
29
+ pass
30
+
31
+ # ============================================================================
32
+ # CONFIGURATION
33
+ # ============================================================================
34
+
35
+ SKIP_DIRS = {'node_modules', '.git', 'dist', 'build', '__pycache__', '.venv', 'venv', '.next'}
36
+ CODE_EXTENSIONS = {'.js', '.ts', '.jsx', '.tsx', '.py', '.go'}
37
+ CONFIG_EXTENSIONS = {'.json', '.yaml', '.yml', '.toml'}
38
+
39
+ # Patterns to check for good observability practices
40
+ GOOD_PATTERNS = [
41
+ # OpenTelemetry setup
42
+ (r"@opentelemetry|opentelemetry-sdk|from opentelemetry", "otel_sdk", "OpenTelemetry SDK imported"),
43
+ (r"getTracer|trace\.get_tracer", "tracer_init", "Tracer initialized"),
44
+ (r"getMeter|metrics\.get_meter", "meter_init", "Meter initialized"),
45
+
46
+ # Span creation
47
+ (r"startActiveSpan|start_as_current_span|start_span", "span_creation", "Spans being created"),
48
+ (r"span\.setAttributes?|span\.set_attribute", "span_attributes", "Span attributes set"),
49
+ (r"span\.recordException|record_exception", "error_recording", "Errors recorded in spans"),
50
+
51
+ # AI-specific attributes
52
+ (r"llm\.|model|tokens|completion", "ai_attributes", "AI-related attributes found"),
53
+ (r"agent\.step|agent\.tool", "agent_tracing", "Agent tracing attributes"),
54
+
55
+ # Metrics
56
+ (r"createCounter|create_counter|Counter\(", "counter_metric", "Counter metrics defined"),
57
+ (r"createHistogram|create_histogram|Histogram\(", "histogram_metric", "Histogram metrics defined"),
58
+
59
+ # Context propagation
60
+ (r"propagation\.inject|inject_context", "context_inject", "Context injection found"),
61
+ (r"propagation\.extract|extract_context", "context_extract", "Context extraction found"),
62
+
63
+ # Logging with trace context
64
+ (r"trace_id|span_id|traceId|spanId", "log_correlation", "Log-trace correlation"),
65
+ ]
66
+
67
+ # Patterns that indicate missing observability
68
+ MISSING_PATTERNS = [
69
+ # LLM calls without tracing
70
+ (r"openai\.|anthropic\.|llm\.|completion", "llm_call", "LLM call found"),
71
+ (r"fetch|axios|requests\.", "http_call", "HTTP call found"),
72
+ ]
73
+
74
+ # Check if LLM/HTTP calls are within spans
75
+ SPAN_CONTEXT_PATTERN = r"(startActiveSpan|start_as_current_span|with_span).*?(openai|anthropic|llm|fetch|axios|requests)"
76
+
77
+
78
+ # ============================================================================
79
+ # SCANNING FUNCTIONS
80
+ # ============================================================================
81
+
82
+ def check_package_deps(project_path: Path) -> Dict[str, Any]:
83
+ """Check package.json or requirements.txt for OTel dependencies."""
84
+ result = {
85
+ "has_otel": False,
86
+ "packages": [],
87
+ "missing": []
88
+ }
89
+
90
+ # Check package.json
91
+ pkg_json = project_path / "package.json"
92
+ if pkg_json.exists():
93
+ try:
94
+ with open(pkg_json) as f:
95
+ pkg = json.load(f)
96
+ all_deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
97
+
98
+ otel_packages = [k for k in all_deps if "opentelemetry" in k.lower()]
99
+ if otel_packages:
100
+ result["has_otel"] = True
101
+ result["packages"] = otel_packages
102
+
103
+ # Check for recommended packages
104
+ recommended = ["@opentelemetry/sdk-node", "@opentelemetry/api"]
105
+ result["missing"] = [r for r in recommended if r not in all_deps]
106
+ except Exception:
107
+ pass
108
+
109
+ # Check requirements.txt
110
+ req_txt = project_path / "requirements.txt"
111
+ if req_txt.exists():
112
+ try:
113
+ with open(req_txt) as f:
114
+ content = f.read().lower()
115
+ if "opentelemetry" in content:
116
+ result["has_otel"] = True
117
+ result["packages"].append("opentelemetry (Python)")
118
+ except Exception:
119
+ pass
120
+
121
+ return result
122
+
123
+
124
+ def scan_file_for_patterns(filepath: Path, patterns: List[tuple]) -> Dict[str, List[Dict]]:
125
+ """Scan file for specific patterns."""
126
+ findings = {}
127
+
128
+ try:
129
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
130
+ content = f.read()
131
+ lines = content.split('\n')
132
+
133
+ for pattern, key, description in patterns:
134
+ matches = list(re.finditer(pattern, content, re.IGNORECASE | re.DOTALL))
135
+ if matches:
136
+ if key not in findings:
137
+ findings[key] = []
138
+
139
+ for match in matches[:3]: # Limit to 3 per pattern
140
+ # Find line number
141
+ line_num = content[:match.start()].count('\n') + 1
142
+ findings[key].append({
143
+ "line": line_num,
144
+ "snippet": lines[line_num - 1].strip()[:80] if line_num <= len(lines) else "",
145
+ "description": description
146
+ })
147
+ except Exception:
148
+ pass
149
+
150
+ return findings
151
+
152
+
153
+ def analyze_instrumentation(project_path: str) -> Dict[str, Any]:
154
+ """Analyze project for observability instrumentation."""
155
+ project = Path(project_path)
156
+
157
+ results = {
158
+ "project": project_path,
159
+ "timestamp": datetime.now().isoformat(),
160
+ "dependencies": check_package_deps(project),
161
+ "instrumentation": {
162
+ "good_practices": {},
163
+ "potential_gaps": [],
164
+ "files_analyzed": 0
165
+ },
166
+ "recommendations": [],
167
+ "score": 0
168
+ }
169
+
170
+ all_good_findings = {}
171
+ llm_calls_without_traces = []
172
+
173
+ for root, dirs, files in os.walk(project):
174
+ dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
175
+
176
+ for file in files:
177
+ ext = Path(file).suffix.lower()
178
+ if ext not in CODE_EXTENSIONS:
179
+ continue
180
+
181
+ filepath = Path(root) / file
182
+ results["instrumentation"]["files_analyzed"] += 1
183
+
184
+ # Check for good patterns
185
+ file_findings = scan_file_for_patterns(filepath, GOOD_PATTERNS)
186
+ for key, findings in file_findings.items():
187
+ if key not in all_good_findings:
188
+ all_good_findings[key] = []
189
+ for f in findings:
190
+ f["file"] = str(filepath.relative_to(project))
191
+ all_good_findings[key].append(f)
192
+
193
+ # Check for potential gaps (LLM calls without spans)
194
+ try:
195
+ with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
196
+ content = f.read()
197
+ lines = content.split('\n')
198
+
199
+ # Find LLM/API calls
200
+ for pattern, key, desc in MISSING_PATTERNS:
201
+ for match in re.finditer(pattern, content, re.IGNORECASE):
202
+ line_num = content[:match.start()].count('\n') + 1
203
+
204
+ # Check if there's a span context nearby (within 10 lines)
205
+ context_start = max(0, line_num - 10)
206
+ context_end = min(len(lines), line_num + 5)
207
+ context = '\n'.join(lines[context_start:context_end])
208
+
209
+ has_span = bool(re.search(r"startActiveSpan|start_as_current_span|with_span|tracer\.", context, re.IGNORECASE))
210
+
211
+ if not has_span and key == "llm_call":
212
+ llm_calls_without_traces.append({
213
+ "file": str(filepath.relative_to(project)),
214
+ "line": line_num,
215
+ "snippet": lines[line_num - 1].strip()[:80]
216
+ })
217
+ except Exception:
218
+ pass
219
+
220
+ results["instrumentation"]["good_practices"] = all_good_findings
221
+ results["instrumentation"]["potential_gaps"] = llm_calls_without_traces[:10] # Limit output
222
+
223
+ # Calculate score
224
+ score = 0
225
+ max_score = 100
226
+
227
+ if results["dependencies"]["has_otel"]:
228
+ score += 20
229
+ if all_good_findings.get("tracer_init"):
230
+ score += 15
231
+ if all_good_findings.get("span_creation"):
232
+ score += 15
233
+ if all_good_findings.get("span_attributes"):
234
+ score += 10
235
+ if all_good_findings.get("ai_attributes"):
236
+ score += 10
237
+ if all_good_findings.get("counter_metric") or all_good_findings.get("histogram_metric"):
238
+ score += 10
239
+ if all_good_findings.get("error_recording"):
240
+ score += 10
241
+ if all_good_findings.get("log_correlation"):
242
+ score += 5
243
+ if all_good_findings.get("context_inject") or all_good_findings.get("context_extract"):
244
+ score += 5
245
+
246
+ # Deduct for gaps
247
+ if len(llm_calls_without_traces) > 0:
248
+ score -= min(20, len(llm_calls_without_traces) * 5)
249
+
250
+ results["score"] = max(0, score)
251
+
252
+ # Generate recommendations
253
+ if not results["dependencies"]["has_otel"]:
254
+ results["recommendations"].append("Install OpenTelemetry SDK: npm install @opentelemetry/sdk-node")
255
+ if not all_good_findings.get("tracer_init"):
256
+ results["recommendations"].append("Initialize a tracer: const tracer = trace.getTracer('service-name')")
257
+ if not all_good_findings.get("span_creation"):
258
+ results["recommendations"].append("Add spans to track operations: tracer.startActiveSpan('operation', ...)")
259
+ if not all_good_findings.get("ai_attributes"):
260
+ results["recommendations"].append("Add AI-specific attributes: llm.model, llm.tokens, agent.step")
261
+ if llm_calls_without_traces:
262
+ results["recommendations"].append(f"Wrap {len(llm_calls_without_traces)} LLM calls in spans for tracing")
263
+ if not all_good_findings.get("error_recording"):
264
+ results["recommendations"].append("Record exceptions in spans: span.recordException(error)")
265
+
266
+ # Determine status
267
+ if score >= 80:
268
+ results["status"] = "[OK] Good observability instrumentation"
269
+ elif score >= 50:
270
+ results["status"] = "[?] Partial instrumentation - improvements recommended"
271
+ elif score >= 20:
272
+ results["status"] = "[!] Basic instrumentation - significant improvements needed"
273
+ else:
274
+ results["status"] = "[!!] Missing observability - add OpenTelemetry instrumentation"
275
+
276
+ return results
277
+
278
+
279
+ # ============================================================================
280
+ # MAIN
281
+ # ============================================================================
282
+
283
+ def main():
284
+ parser = argparse.ArgumentParser(
285
+ description="Validate OpenTelemetry instrumentation in AI applications"
286
+ )
287
+ parser.add_argument("project_path", nargs="?", default=".", help="Project directory to scan")
288
+ parser.add_argument("--output", choices=["json", "summary"], default="json",
289
+ help="Output format")
290
+
291
+ args = parser.parse_args()
292
+
293
+ if not os.path.isdir(args.project_path):
294
+ print(json.dumps({"error": f"Directory not found: {args.project_path}"}))
295
+ sys.exit(1)
296
+
297
+ results = analyze_instrumentation(args.project_path)
298
+
299
+ if args.output == "summary":
300
+ print(f"\n{'='*60}")
301
+ print(f"Observability Validator: {results['project']}")
302
+ print(f"{'='*60}")
303
+ print(f"Status: {results['status']}")
304
+ print(f"Score: {results['score']}/100")
305
+ print(f"\nDependencies:")
306
+ print(f" OpenTelemetry installed: {'Yes' if results['dependencies']['has_otel'] else 'No'}")
307
+ if results['dependencies']['packages']:
308
+ print(f" Packages: {', '.join(results['dependencies']['packages'][:5])}")
309
+
310
+ print(f"\nInstrumentation Found:")
311
+ for key, findings in results['instrumentation']['good_practices'].items():
312
+ print(f" ✓ {key}: {len(findings)} occurrences")
313
+
314
+ if results['instrumentation']['potential_gaps']:
315
+ print(f"\nPotential Gaps:")
316
+ for gap in results['instrumentation']['potential_gaps'][:5]:
317
+ print(f" ⚠ {gap['file']}:{gap['line']} - LLM call without span")
318
+
319
+ if results['recommendations']:
320
+ print(f"\nRecommendations:")
321
+ for rec in results['recommendations']:
322
+ print(f" → {rec}")
323
+
324
+ print(f"{'='*60}\n")
325
+ else:
326
+ print(json.dumps(results, indent=2))
327
+
328
+
329
+ if __name__ == "__main__":
330
+ main()