[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F8BYQEN93SMNXEVA664XKA":3,"skill-same:01M6F8BYQEN93SMNXEVA664XKA":79},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":56,"commitSha":57,"skillDirectory":58,"forksCount":59,"repositoryHtmlUrl":60,"repositoryHomepageUrl":6,"ownerHtmlUrl":61,"ownerBlogUrl":62,"ownerDescription":63,"updatedAt":64,"install":65,"resources":78},"01M6F8BYQEN93SMNXEVA664XKA","openevidence-data-handling",null,"Implement HIPAA-compliant PHI data handling for OpenEvidence integrations.\nUse when implementing data protection, configuring retention policies,\nor ensuring compliance for clinical AI data flows.\nTrigger with phrases like \"openevidence phi\", \"openevidence data\",\n\"openevidence hipaa data\", \"clinical data handling\", \"patient data protection\".","Dicklesworthstone\u002Fpi_agent_rust","Dicklesworthstone","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F35050222?v=4","01M6CG26AR942CSVMN1M5E3DSE","Jeff Emanuel","01M6CG26EPP71PSHW1VDS71121",1991,"MIT","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fopenevidence-pack\u002Fskills\u002Fopenevidence-data-handling\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"data","openevidence data handling","protection","when","for","implementing","with","flows","retention","trigger","io","patient","clinical","implement","use","edit","write","read","integrations","phi","hipaa","data protection","compliance","2026-11-10 10:12:30",false,"---\nname: openevidence-data-handling\ndescription: |\n  Implement HIPAA-compliant PHI data handling for OpenEvidence integrations.\n  Use when implementing data protection, configuring retention policies,\n  or ensuring compliance for clinical AI data flows.\n  Trigger with phrases like \"openevidence phi\", \"openevidence data\",\n  \"openevidence hipaa data\", \"clinical data handling\", \"patient data protection\".\nallowed-tools: Read, Write, Edit\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\n---\n\n# OpenEvidence Data Handling\n\n## Overview\nImplement HIPAA-compliant Protected Health Information (PHI) handling for OpenEvidence clinical AI integrations.\n\n## Prerequisites\n- Signed BAA with OpenEvidence\n- Understanding of HIPAA regulations\n- Data classification policy\n- Encryption infrastructure\n\n## HIPAA Data Categories\n\n| Category | Examples | Handling |\n|----------|----------|----------|\n| PHI Identifiers | Name, DOB, SSN, MRN | Never send to OpenEvidence |\n| Clinical Data | Conditions, medications | May send de-identified |\n| Query Results | Answers, citations | Cache with encryption, audit access |\n| Audit Logs | User actions, timestamps | Retain 6 years, encrypt |\n\n## 18 HIPAA Identifiers (Never Send to OpenEvidence)\n\n1. Names\n2. Geographic data (smaller than state)\n3. Dates (except year) related to individual\n4. Phone numbers\n5. Fax numbers\n6. Email addresses\n7. Social Security numbers\n8. Medical record numbers\n9. Health plan beneficiary numbers\n10. Account numbers\n11. Certificate\u002Flicense numbers\n12. Vehicle identifiers and serial numbers\n13. Device identifiers and serial numbers\n14. Web URLs\n15. IP addresses\n16. Biometric identifiers\n17. Full-face photographs\n18. Any other unique identifying characteristic\n\n## Instructions\n\n### Step 1: PHI Detection and Removal\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fphi-detector.ts\n\n\u002F\u002F Patterns for common PHI\nconst PHI_PATTERNS = {\n  ssn: \u002F\\b\\d{3}[-\\s]?\\d{2}[-\\s]?\\d{4}\\b\u002Fg,\n  mrn: \u002F\\b(MRN|Medical Record)[\\s:#]*\\d{6,12}\\b\u002Fgi,\n  phone: \u002F\\b(\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b\u002Fg,\n  email: \u002F\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b\u002Fg,\n  dob: \u002F\\b(DOB|Date of Birth|Born)[\\s:]*\\d{1,2}[\\\u002F\\-]\\d{1,2}[\\\u002F\\-]\\d{2,4}\\b\u002Fgi,\n  date: \u002F\\b\\d{1,2}[\\\u002F\\-]\\d{1,2}[\\\u002F\\-]\\d{2,4}\\b\u002Fg,\n  name: \u002F\\b(Mr\\.|Mrs\\.|Ms\\.|Dr\\.|Patient)\\s+[A-Z][a-z]+\\s+[A-Z][a-z]+\\b\u002Fg,\n  address: \u002F\\b\\d{1,5}\\s+[A-Z][a-z]+\\s+(Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd)\\b\u002Fgi,\n};\n\nexport interface PHIDetectionResult {\n  containsPHI: boolean;\n  detectedPatterns: string[];\n  sanitizedText: string;\n}\n\nexport function detectPHI(text: string): PHIDetectionResult {\n  const detectedPatterns: string[] = [];\n  let sanitized = text;\n\n  for (const [pattern, regex] of Object.entries(PHI_PATTERNS)) {\n    if (regex.test(text)) {\n      detectedPatterns.push(pattern);\n      \u002F\u002F Reset regex lastIndex for global patterns\n      regex.lastIndex = 0;\n      sanitized = sanitized.replace(regex, `[${pattern.toUpperCase()}_REDACTED]`);\n    }\n  }\n\n  return {\n    containsPHI: detectedPatterns.length > 0,\n    detectedPatterns,\n    sanitizedText: sanitized,\n  };\n}\n\nexport function sanitizeForOpenEvidence(text: string): string {\n  const result = detectPHI(text);\n\n  if (result.containsPHI) {\n    console.warn('[PHI] Detected PHI in query, sanitizing:', result.detectedPatterns);\n  }\n\n  return result.sanitizedText;\n}\n```\n\n### Step 2: Patient Context De-identification\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fdeidentify.ts\n\ninterface IdentifiedPatientContext {\n  patientId?: string;\n  name?: string;\n  dateOfBirth?: Date;\n  age?: number;\n  sex?: 'male' | 'female' | 'other';\n  conditions?: string[];\n  medications?: string[];\n  allergies?: string[];\n}\n\ninterface DeidentifiedPatientContext {\n  ageRange?: string;\n  sex?: string;\n  conditionCategories?: string[];\n  medicationClasses?: string[];\n  hasAllergies?: boolean;\n}\n\n\u002F\u002F Age ranges for de-identification\nfunction getAgeRange(age?: number): string | undefined {\n  if (!age) return undefined;\n  if (age \u003C 1) return 'infant';\n  if (age \u003C 5) return 'toddler';\n  if (age \u003C 13) return 'child';\n  if (age \u003C 18) return 'adolescent';\n  if (age \u003C 30) return 'young-adult';\n  if (age \u003C 50) return 'adult';\n  if (age \u003C 65) return 'middle-aged';\n  if (age \u003C 80) return 'elderly';\n  return 'elderly-80+';\n}\n\n\u002F\u002F Condition category mapping\nconst CONDITION_CATEGORIES: Record\u003Cstring, string> = {\n  \u002F\u002F Cardiovascular\n  'hypertension': 'cardiovascular',\n  'coronary artery disease': 'cardiovascular',\n  'heart failure': 'cardiovascular',\n  'atrial fibrillation': 'cardiovascular',\n\n  \u002F\u002F Metabolic\n  'diabetes': 'metabolic',\n  'type 2 diabetes': 'metabolic',\n  'hyperlipidemia': 'metabolic',\n  'obesity': 'metabolic',\n\n  \u002F\u002F Respiratory\n  'asthma': 'respiratory',\n  'copd': 'respiratory',\n  'pneumonia': 'respiratory',\n\n  \u002F\u002F Mental Health\n  'depression': 'mental-health',\n  'anxiety': 'mental-health',\n\n  \u002F\u002F Add more as needed\n};\n\nfunction categorizeConditions(conditions?: string[]): string[] | undefined {\n  if (!conditions) return undefined;\n\n  const categories = new Set\u003Cstring>();\n  for (const condition of conditions) {\n    const category = CONDITION_CATEGORIES[condition.toLowerCase()];\n    if (category) {\n      categories.add(category);\n    } else {\n      categories.add('other');\n    }\n  }\n\n  return Array.from(categories);\n}\n\n\u002F\u002F Drug class mapping\nconst DRUG_CLASSES: Record\u003Cstring, string> = {\n  'metformin': 'antidiabetic',\n  'lisinopril': 'ace-inhibitor',\n  'atorvastatin': 'statin',\n  'amlodipine': 'calcium-channel-blocker',\n  'metoprolol': 'beta-blocker',\n  \u002F\u002F Add more as needed\n};\n\nfunction classifyMedications(medications?: string[]): string[] | undefined {\n  if (!medications) return undefined;\n\n  const classes = new Set\u003Cstring>();\n  for (const med of medications) {\n    const drugClass = DRUG_CLASSES[med.toLowerCase()];\n    if (drugClass) {\n      classes.add(drugClass);\n    } else {\n      classes.add('other-medication');\n    }\n  }\n\n  return Array.from(classes);\n}\n\nexport function deidentifyPatientContext(\n  context: IdentifiedPatientContext\n): DeidentifiedPatientContext {\n  return {\n    ageRange: context.age ? getAgeRange(context.age) : undefined,\n    sex: context.sex,\n    conditionCategories: categorizeConditions(context.conditions),\n    medicationClasses: classifyMedications(context.medications),\n    hasAllergies: context.allergies ? context.allergies.length > 0 : undefined,\n  };\n}\n```\n\n### Step 3: Encrypted Storage\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fencryption.ts\nimport crypto from 'crypto';\n\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 16;\nconst AUTH_TAG_LENGTH = 16;\n\nexport class EncryptionService {\n  private key: Buffer;\n\n  constructor(encryptionKey: string) {\n    \u002F\u002F Key should be 32 bytes for AES-256\n    this.key = crypto.scryptSync(encryptionKey, 'salt', 32);\n  }\n\n  encrypt(plaintext: string): string {\n    const iv = crypto.randomBytes(IV_LENGTH);\n    const cipher = crypto.createCipheriv(ALGORITHM, this.key, iv);\n\n    let encrypted = cipher.update(plaintext, 'utf8', 'hex');\n    encrypted += cipher.final('hex');\n\n    const authTag = cipher.getAuthTag();\n\n    \u002F\u002F Return IV + AuthTag + Encrypted data\n    return iv.toString('hex') + authTag.toString('hex') + encrypted;\n  }\n\n  decrypt(ciphertext: string): string {\n    const iv = Buffer.from(ciphertext.slice(0, IV_LENGTH * 2), 'hex');\n    const authTag = Buffer.from(\n      ciphertext.slice(IV_LENGTH * 2, IV_LENGTH * 2 + AUTH_TAG_LENGTH * 2),\n      'hex'\n    );\n    const encrypted = ciphertext.slice(IV_LENGTH * 2 + AUTH_TAG_LENGTH * 2);\n\n    const decipher = crypto.createDecipheriv(ALGORITHM, this.key, iv);\n    decipher.setAuthTag(authTag);\n\n    let decrypted = decipher.update(encrypted, 'hex', 'utf8');\n    decrypted += decipher.final('utf8');\n\n    return decrypted;\n  }\n}\n\n\u002F\u002F Usage for caching OpenEvidence responses\nexport class EncryptedCacheStore {\n  private encryption: EncryptionService;\n  private redis: Redis;\n\n  constructor(redis: Redis, encryptionKey: string) {\n    this.redis = redis;\n    this.encryption = new EncryptionService(encryptionKey);\n  }\n\n  async set(key: string, value: any, ttlSeconds: number): Promise\u003Cvoid> {\n    const encrypted = this.encryption.encrypt(JSON.stringify(value));\n    await this.redis.setex(key, ttlSeconds, encrypted);\n  }\n\n  async get\u003CT>(key: string): Promise\u003CT | null> {\n    const encrypted = await this.redis.get(key);\n    if (!encrypted) return null;\n\n    try {\n      const decrypted = this.encryption.decrypt(encrypted);\n      return JSON.parse(decrypted) as T;\n    } catch (error) {\n      console.error('Decryption failed, cache entry may be corrupted');\n      await this.redis.del(key);\n      return null;\n    }\n  }\n}\n```\n\n### Step 4: Data Retention Policies\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fretention.ts\n\ninterface RetentionPolicy {\n  dataType: string;\n  retentionDays: number;\n  archiveAfterDays?: number;\n  encryptionRequired: boolean;\n}\n\nconst HIPAA_RETENTION_POLICIES: RetentionPolicy[] = [\n  {\n    dataType: 'audit_logs',\n    retentionDays: 2190, \u002F\u002F 6 years (HIPAA minimum)\n    archiveAfterDays: 365, \u002F\u002F Archive after 1 year\n    encryptionRequired: true,\n  },\n  {\n    dataType: 'query_cache',\n    retentionDays: 1, \u002F\u002F 24 hours max\n    encryptionRequired: true,\n  },\n  {\n    dataType: 'deepconsult_reports',\n    retentionDays: 365, \u002F\u002F 1 year\n    archiveAfterDays: 90,\n    encryptionRequired: true,\n  },\n  {\n    dataType: 'error_logs',\n    retentionDays: 90, \u002F\u002F 90 days\n    encryptionRequired: false, \u002F\u002F No PHI in error logs\n  },\n];\n\nexport class DataRetentionManager {\n  constructor(private db: Database, private archive: ArchiveStorage) {}\n\n  async enforceRetentionPolicies(): Promise\u003CRetentionReport> {\n    const report: RetentionReport = {\n      timestamp: new Date(),\n      actions: [],\n    };\n\n    for (const policy of HIPAA_RETENTION_POLICIES) {\n      const result = await this.enforcePolicy(policy);\n      report.actions.push(result);\n    }\n\n    return report;\n  }\n\n  private async enforcePolicy(policy: RetentionPolicy): Promise\u003CRetentionAction> {\n    const cutoffDate = new Date();\n    cutoffDate.setDate(cutoffDate.getDate() - policy.retentionDays);\n\n    \u002F\u002F Delete expired data\n    const deletedCount = await this.db.deleteOlderThan(\n      policy.dataType,\n      cutoffDate\n    );\n\n    \u002F\u002F Archive if applicable\n    let archivedCount = 0;\n    if (policy.archiveAfterDays) {\n      const archiveCutoff = new Date();\n      archiveCutoff.setDate(archiveCutoff.getDate() - policy.archiveAfterDays);\n\n      const toArchive = await this.db.findOlderThan(policy.dataType, archiveCutoff);\n      if (toArchive.length > 0) {\n        await this.archive.store(policy.dataType, toArchive);\n        archivedCount = toArchive.length;\n      }\n    }\n\n    return {\n      dataType: policy.dataType,\n      deletedCount,\n      archivedCount,\n      cutoffDate,\n    };\n  }\n}\n\n\u002F\u002F Run daily via cron job\nexport async function runDailyRetentionJob(): Promise\u003Cvoid> {\n  const manager = new DataRetentionManager(db, archiveStorage);\n  const report = await manager.enforceRetentionPolicies();\n\n  console.log('[Retention] Daily job completed:', JSON.stringify(report));\n\n  \u002F\u002F Alert if significant data deleted\n  const totalDeleted = report.actions.reduce((sum, a) => sum + a.deletedCount, 0);\n  if (totalDeleted > 10000) {\n    await alertOps(`Data retention: ${totalDeleted} records deleted`);\n  }\n}\n```\n\n### Step 5: Audit Trail for PHI Access\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Faudit-trail.ts\n\ninterface PHIAccessLog {\n  timestamp: Date;\n  userId: string;\n  userRole: string;\n  action: 'query' | 'view_result' | 'export' | 'deepconsult';\n  resourceType: string;\n  resourceId: string;\n  accessReason?: string;\n  ipAddress: string;\n  userAgent: string;\n  \u002F\u002F Never log: actual query content, patient identifiers, clinical data\n}\n\nexport class PHIAuditLogger {\n  private db: Database;\n  private encryption: EncryptionService;\n\n  constructor(db: Database, encryptionKey: string) {\n    this.db = db;\n    this.encryption = new EncryptionService(encryptionKey);\n  }\n\n  async logAccess(entry: Omit\u003CPHIAccessLog, 'timestamp'>): Promise\u003Cvoid> {\n    const log: PHIAccessLog = {\n      ...entry,\n      timestamp: new Date(),\n    };\n\n    \u002F\u002F Encrypt sensitive fields\n    const encryptedLog = {\n      ...log,\n      ipAddress: this.encryption.encrypt(log.ipAddress),\n      userAgent: this.encryption.encrypt(log.userAgent),\n    };\n\n    await this.db.auditLogs.create({ data: encryptedLog });\n  }\n\n  async queryLogs(\n    filters: {\n      userId?: string;\n      startDate?: Date;\n      endDate?: Date;\n      action?: string;\n    },\n    pagination: { page: number; pageSize: number }\n  ): Promise\u003C{ logs: PHIAccessLog[]; total: number }> {\n    \u002F\u002F Require authorization to query audit logs\n    const result = await this.db.auditLogs.findMany({\n      where: {\n        ...(filters.userId && { userId: filters.userId }),\n        ...(filters.startDate && { timestamp: { gte: filters.startDate } }),\n        ...(filters.endDate && { timestamp: { lte: filters.endDate } }),\n        ...(filters.action && { action: filters.action }),\n      },\n      skip: (pagination.page - 1) * pagination.pageSize,\n      take: pagination.pageSize,\n      orderBy: { timestamp: 'desc' },\n    });\n\n    \u002F\u002F Decrypt sensitive fields for authorized viewer\n    const decryptedLogs = result.map(log => ({\n      ...log,\n      ipAddress: this.encryption.decrypt(log.ipAddress),\n      userAgent: this.encryption.decrypt(log.userAgent),\n    }));\n\n    const total = await this.db.auditLogs.count({ where: filters });\n\n    return { logs: decryptedLogs, total };\n  }\n}\n```\n\n## Data Flow Diagram (HIPAA Compliant)\n\n```\nClinical User\n      │\n      ▼\n┌─────────────────┐\n│ PHI Detection   │──── Block if PHI detected\n│ & Sanitization  │\n└────────┬────────┘\n         │ (De-identified query)\n         ▼\n┌─────────────────┐    ┌─────────────────┐\n│ Audit Logger    │───▶│ Encrypted       │\n│ (Access Log)    │    │ Audit Storage   │\n└────────┬────────┘    └─────────────────┘\n         │\n         ▼\n┌─────────────────┐    ┌─────────────────┐\n│ OpenEvidence    │───▶│ Encrypted       │\n│ API Call        │    │ Cache           │\n└────────┬────────┘    └─────────────────┘\n         │\n         ▼\n┌─────────────────┐\n│ Response to     │\n│ Clinical User   │\n└─────────────────┘\n```\n\n## Output\n- PHI detection and sanitization\n- Patient context de-identification\n- Encrypted storage for cached data\n- HIPAA-compliant retention policies\n- Comprehensive audit trail\n\n## Data Handling Checklist\n- [ ] PHI patterns defined and detected\n- [ ] Patient context de-identified before queries\n- [ ] All cached data encrypted (AES-256)\n- [ ] Audit logging for all PHI access\n- [ ] 6-year retention for audit logs\n- [ ] Daily retention job running\n- [ ] Encryption keys managed securely\n- [ ] BAA signed with OpenEvidence\n\n## Resources\n- [HIPAA Privacy Rule](https:\u002F\u002Fwww.hhs.gov\u002Fhipaa\u002Ffor-professionals\u002Fprivacy\u002Findex.html)\n- [HIPAA Security Rule](https:\u002F\u002Fwww.hhs.gov\u002Fhipaa\u002Ffor-professionals\u002Fsecurity\u002Findex.html)\n- [OpenEvidence Security](https:\u002F\u002Fwww.openevidence.com\u002Fsecurity)\n\n## Next Steps\nFor enterprise access control, see `openevidence-enterprise-rbac`.\n","# OpenEvidence Data Handling\n\n## Overview\nImplement HIPAA-compliant Protected Health Information (PHI) handling for OpenEvidence clinical AI integrations.\n\n## Prerequisites\n- Signed BAA with OpenEvidence\n- Understanding of HIPAA regulations\n- Data classification policy\n- Encryption infrastructure\n\n## HIPAA Data Categories\n\n| Category | Examples | Handling |\n|----------|----------|----------|\n| PHI Identifiers | Name, DOB, SSN, MRN | Never send to OpenEvidence |\n| Clinical Data | Conditions, medications | May send de-identified |\n| Query Results | Answers, citations | Cache with encryption, audit access |\n| Audit Logs | User actions, timestamps | Retain 6 years, encrypt |\n\n## 18 HIPAA Identifiers (Never Send to OpenEvidence)\n\n1. Names\n2. Geographic data (smaller than state)\n3. Dates (except year) related to individual\n4. Phone numbers\n5. Fax numbers\n6. Email addresses\n7. Social Security numbers\n8. Medical record numbers\n9. Health plan beneficiary numbers\n10. Account numbers\n11. Certificate\u002Flicense numbers\n12. Vehicle identifiers and serial numbers\n13. Device identifiers and serial numbers\n14. Web URLs\n15. IP addresses\n16. Biometric identifiers\n17. Full-face photographs\n18. Any other unique identifying characteristic\n\n## Instructions\n\n### Step 1: PHI Detection and Removal\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fphi-detector.ts\n\n\u002F\u002F Patterns for common PHI\nconst PHI_PATTERNS = {\n  ssn: \u002F\\b\\d{3}[-\\s]?\\d{2}[-\\s]?\\d{4}\\b\u002Fg,\n  mrn: \u002F\\b(MRN|Medical Record)[\\s:#]*\\d{6,12}\\b\u002Fgi,\n  phone: \u002F\\b(\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}\\b\u002Fg,\n  email: \u002F\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b\u002Fg,\n  dob: \u002F\\b(DOB|Date of Birth|Born)[\\s:]*\\d{1,2}[\\\u002F\\-]\\d{1,2}[\\\u002F\\-]\\d{2,4}\\b\u002Fgi,\n  date: \u002F\\b\\d{1,2}[\\\u002F\\-]\\d{1,2}[\\\u002F\\-]\\d{2,4}\\b\u002Fg,\n  name: \u002F\\b(Mr\\.|Mrs\\.|Ms\\.|Dr\\.|Patient)\\s+[A-Z][a-z]+\\s+[A-Z][a-z]+\\b\u002Fg,\n  address: \u002F\\b\\d{1,5}\\s+[A-Z][a-z]+\\s+(Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd)\\b\u002Fgi,\n};\n\nexport interface PHIDetectionResult {\n  containsPHI: boolean;\n  detectedPatterns: string[];\n  sanitizedText: string;\n}\n\nexport function detectPHI(text: string): PHIDetectionResult {\n  const detectedPatterns: string[] = [];\n  let sanitized = text;\n\n  for (const [pattern, regex] of Object.entries(PHI_PATTERNS)) {\n    if (regex.test(text)) {\n      detectedPatterns.push(pattern);\n      \u002F\u002F Reset regex lastIndex for global patterns\n      regex.lastIndex = 0;\n      sanitized = sanitized.replace(regex, `[${pattern.toUpperCase()}_REDACTED]`);\n    }\n  }\n\n  return {\n    containsPHI: detectedPatterns.length > 0,\n    detectedPatterns,\n    sanitizedText: sanitized,\n  };\n}\n\nexport function sanitizeForOpenEvidence(text: string): string {\n  const result = detectPHI(text);\n\n  if (result.containsPHI) {\n    console.warn('[PHI] Detected PHI in query, sanitizing:', result.detectedPatterns);\n  }\n\n  return result.sanitizedText;\n}\n```\n\n### Step 2: Patient Context De-identification\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fdeidentify.ts\n\ninterface IdentifiedPatientContext {\n  patientId?: string;\n  name?: string;\n  dateOfBirth?: Date;\n  age?: number;\n  sex?: 'male' | 'female' | 'other';\n  conditions?: string[];\n  medications?: string[];\n  allergies?: string[];\n}\n\ninterface DeidentifiedPatientContext {\n  ageRange?: string;\n  sex?: string;\n  conditionCategories?: string[];\n  medicationClasses?: string[];\n  hasAllergies?: boolean;\n}\n\n\u002F\u002F Age ranges for de-identification\nfunction getAgeRange(age?: number): string | undefined {\n  if (!age) return undefined;\n  if (age \u003C 1) return 'infant';\n  if (age \u003C 5) return 'toddler';\n  if (age \u003C 13) return 'child';\n  if (age \u003C 18) return 'adolescent';\n  if (age \u003C 30) return 'young-adult';\n  if (age \u003C 50) return 'adult';\n  if (age \u003C 65) return 'middle-aged';\n  if (age \u003C 80) return 'elderly';\n  return 'elderly-80+';\n}\n\n\u002F\u002F Condition category mapping\nconst CONDITION_CATEGORIES: Record\u003Cstring, string> = {\n  \u002F\u002F Cardiovascular\n  'hypertension': 'cardiovascular',\n  'coronary artery disease': 'cardiovascular',\n  'heart failure': 'cardiovascular',\n  'atrial fibrillation': 'cardiovascular',\n\n  \u002F\u002F Metabolic\n  'diabetes': 'metabolic',\n  'type 2 diabetes': 'metabolic',\n  'hyperlipidemia': 'metabolic',\n  'obesity': 'metabolic',\n\n  \u002F\u002F Respiratory\n  'asthma': 'respiratory',\n  'copd': 'respiratory',\n  'pneumonia': 'respiratory',\n\n  \u002F\u002F Mental Health\n  'depression': 'mental-health',\n  'anxiety': 'mental-health',\n\n  \u002F\u002F Add more as needed\n};\n\nfunction categorizeConditions(conditions?: string[]): string[] | undefined {\n  if (!conditions) return undefined;\n\n  const categories = new Set\u003Cstring>();\n  for (const condition of conditions) {\n    const category = CONDITION_CATEGORIES[condition.toLowerCase()];\n    if (category) {\n      categories.add(category);\n    } else {\n      categories.add('other');\n    }\n  }\n\n  return Array.from(categories);\n}\n\n\u002F\u002F Drug class mapping\nconst DRUG_CLASSES: Record\u003Cstring, string> = {\n  'metformin': 'antidiabetic',\n  'lisinopril': 'ace-inhibitor',\n  'atorvastatin': 'statin',\n  'amlodipine': 'calcium-channel-blocker',\n  'metoprolol': 'beta-blocker',\n  \u002F\u002F Add more as needed\n};\n\nfunction classifyMedications(medications?: string[]): string[] | undefined {\n  if (!medications) return undefined;\n\n  const classes = new Set\u003Cstring>();\n  for (const med of medications) {\n    const drugClass = DRUG_CLASSES[med.toLowerCase()];\n    if (drugClass) {\n      classes.add(drugClass);\n    } else {\n      classes.add('other-medication');\n    }\n  }\n\n  return Array.from(classes);\n}\n\nexport function deidentifyPatientContext(\n  context: IdentifiedPatientContext\n): DeidentifiedPatientContext {\n  return {\n    ageRange: context.age ? getAgeRange(context.age) : undefined,\n    sex: context.sex,\n    conditionCategories: categorizeConditions(context.conditions),\n    medicationClasses: classifyMedications(context.medications),\n    hasAllergies: context.allergies ? context.allergies.length > 0 : undefined,\n  };\n}\n```\n\n### Step 3: Encrypted Storage\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fencryption.ts\nimport crypto from 'crypto';\n\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 16;\nconst AUTH_TAG_LENGTH = 16;\n\nexport class EncryptionService {\n  private key: Buffer;\n\n  constructor(encryptionKey: string) {\n    \u002F\u002F Key should be 32 bytes for AES-256\n    this.key = crypto.scryptSync(encryptionKey, 'salt', 32);\n  }\n\n  encrypt(plaintext: string): string {\n    const iv = crypto.randomBytes(IV_LENGTH);\n    const cipher = crypto.createCipheriv(ALGORITHM, this.key, iv);\n\n    let encrypted = cipher.update(plaintext, 'utf8', 'hex');\n    encrypted += cipher.final('hex');\n\n    const authTag = cipher.getAuthTag();\n\n    \u002F\u002F Return IV + AuthTag + Encrypted data\n    return iv.toString('hex') + authTag.toString('hex') + encrypted;\n  }\n\n  decrypt(ciphertext: string): string {\n    const iv = Buffer.from(ciphertext.slice(0, IV_LENGTH * 2), 'hex');\n    const authTag = Buffer.from(\n      ciphertext.slice(IV_LENGTH * 2, IV_LENGTH * 2 + AUTH_TAG_LENGTH * 2),\n      'hex'\n    );\n    const encrypted = ciphertext.slice(IV_LENGTH * 2 + AUTH_TAG_LENGTH * 2);\n\n    const decipher = crypto.createDecipheriv(ALGORITHM, this.key, iv);\n    decipher.setAuthTag(authTag);\n\n    let decrypted = decipher.update(encrypted, 'hex', 'utf8');\n    decrypted += decipher.final('utf8');\n\n    return decrypted;\n  }\n}\n\n\u002F\u002F Usage for caching OpenEvidence responses\nexport class EncryptedCacheStore {\n  private encryption: EncryptionService;\n  private redis: Redis;\n\n  constructor(redis: Redis, encryptionKey: string) {\n    this.redis = redis;\n    this.encryption = new EncryptionService(encryptionKey);\n  }\n\n  async set(key: string, value: any, ttlSeconds: number): Promise\u003Cvoid> {\n    const encrypted = this.encryption.encrypt(JSON.stringify(value));\n    await this.redis.setex(key, ttlSeconds, encrypted);\n  }\n\n  async get\u003CT>(key: string): Promise\u003CT | null> {\n    const encrypted = await this.redis.get(key);\n    if (!encrypted) return null;\n\n    try {\n      const decrypted = this.encryption.decrypt(encrypted);\n      return JSON.parse(decrypted) as T;\n    } catch (error) {\n      console.error('Decryption failed, cache entry may be corrupted');\n      await this.redis.del(key);\n      return null;\n    }\n  }\n}\n```\n\n### Step 4: Data Retention Policies\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Fretention.ts\n\ninterface RetentionPolicy {\n  dataType: string;\n  retentionDays: number;\n  archiveAfterDays?: number;\n  encryptionRequired: boolean;\n}\n\nconst HIPAA_RETENTION_POLICIES: RetentionPolicy[] = [\n  {\n    dataType: 'audit_logs',\n    retentionDays: 2190, \u002F\u002F 6 years (HIPAA minimum)\n    archiveAfterDays: 365, \u002F\u002F Archive after 1 year\n    encryptionRequired: true,\n  },\n  {\n    dataType: 'query_cache',\n    retentionDays: 1, \u002F\u002F 24 hours max\n    encryptionRequired: true,\n  },\n  {\n    dataType: 'deepconsult_reports',\n    retentionDays: 365, \u002F\u002F 1 year\n    archiveAfterDays: 90,\n    encryptionRequired: true,\n  },\n  {\n    dataType: 'error_logs',\n    retentionDays: 90, \u002F\u002F 90 days\n    encryptionRequired: false, \u002F\u002F No PHI in error logs\n  },\n];\n\nexport class DataRetentionManager {\n  constructor(private db: Database, private archive: ArchiveStorage) {}\n\n  async enforceRetentionPolicies(): Promise\u003CRetentionReport> {\n    const report: RetentionReport = {\n      timestamp: new Date(),\n      actions: [],\n    };\n\n    for (const policy of HIPAA_RETENTION_POLICIES) {\n      const result = await this.enforcePolicy(policy);\n      report.actions.push(result);\n    }\n\n    return report;\n  }\n\n  private async enforcePolicy(policy: RetentionPolicy): Promise\u003CRetentionAction> {\n    const cutoffDate = new Date();\n    cutoffDate.setDate(cutoffDate.getDate() - policy.retentionDays);\n\n    \u002F\u002F Delete expired data\n    const deletedCount = await this.db.deleteOlderThan(\n      policy.dataType,\n      cutoffDate\n    );\n\n    \u002F\u002F Archive if applicable\n    let archivedCount = 0;\n    if (policy.archiveAfterDays) {\n      const archiveCutoff = new Date();\n      archiveCutoff.setDate(archiveCutoff.getDate() - policy.archiveAfterDays);\n\n      const toArchive = await this.db.findOlderThan(policy.dataType, archiveCutoff);\n      if (toArchive.length > 0) {\n        await this.archive.store(policy.dataType, toArchive);\n        archivedCount = toArchive.length;\n      }\n    }\n\n    return {\n      dataType: policy.dataType,\n      deletedCount,\n      archivedCount,\n      cutoffDate,\n    };\n  }\n}\n\n\u002F\u002F Run daily via cron job\nexport async function runDailyRetentionJob(): Promise\u003Cvoid> {\n  const manager = new DataRetentionManager(db, archiveStorage);\n  const report = await manager.enforceRetentionPolicies();\n\n  console.log('[Retention] Daily job completed:', JSON.stringify(report));\n\n  \u002F\u002F Alert if significant data deleted\n  const totalDeleted = report.actions.reduce((sum, a) => sum + a.deletedCount, 0);\n  if (totalDeleted > 10000) {\n    await alertOps(`Data retention: ${totalDeleted} records deleted`);\n  }\n}\n```\n\n### Step 5: Audit Trail for PHI Access\n```typescript\n\u002F\u002F src\u002Fcompliance\u002Faudit-trail.ts\n\ninterface PHIAccessLog {\n  timestamp: Date;\n  userId: string;\n  userRole: string;\n  action: 'query' | 'view_result' | 'export' | 'deepconsult';\n  resourceType: string;\n  resourceId: string;\n  accessReason?: string;\n  ipAddress: string;\n  userAgent: string;\n  \u002F\u002F Never log: actual query content, patient identifiers, clinical data\n}\n\nexport class PHIAuditLogger {\n  private db: Database;\n  private encryption: EncryptionService;\n\n  constructor(db: Database, encryptionKey: string) {\n    this.db = db;\n    this.encryption = new EncryptionService(encryptionKey);\n  }\n\n  async logAccess(entry: Omit\u003CPHIAccessLog, 'timestamp'>): Promise\u003Cvoid> {\n    const log: PHIAccessLog = {\n      ...entry,\n      timestamp: new Date(),\n    };\n\n    \u002F\u002F Encrypt sensitive fields\n    const encryptedLog = {\n      ...log,\n      ipAddress: this.encryption.encrypt(log.ipAddress),\n      userAgent: this.encryption.encrypt(log.userAgent),\n    };\n\n    await this.db.auditLogs.create({ data: encryptedLog });\n  }\n\n  async queryLogs(\n    filters: {\n      userId?: string;\n      startDate?: Date;\n      endDate?: Date;\n      action?: string;\n    },\n    pagination: { page: number; pageSize: number }\n  ): Promise\u003C{ logs: PHIAccessLog[]; total: number }> {\n    \u002F\u002F Require authorization to query audit logs\n    const result = await this.db.auditLogs.findMany({\n      where: {\n        ...(filters.userId && { userId: filters.userId }),\n        ...(filters.startDate && { timestamp: { gte: filters.startDate } }),\n        ...(filters.endDate && { timestamp: { lte: filters.endDate } }),\n        ...(filters.action && { action: filters.action }),\n      },\n      skip: (pagination.page - 1) * pagination.pageSize,\n      take: pagination.pageSize,\n      orderBy: { timestamp: 'desc' },\n    });\n\n    \u002F\u002F Decrypt sensitive fields for authorized viewer\n    const decryptedLogs = result.map(log => ({\n      ...log,\n      ipAddress: this.encryption.decrypt(log.ipAddress),\n      userAgent: this.encryption.decrypt(log.userAgent),\n    }));\n\n    const total = await this.db.auditLogs.count({ where: filters });\n\n    return { logs: decryptedLogs, total };\n  }\n}\n```\n\n## Data Flow Diagram (HIPAA Compliant)\n\n```\nClinical User\n      │\n      ▼\n┌─────────────────┐\n│ PHI Detection   │──── Block if PHI detected\n│ & Sanitization  │\n└────────┬────────┘\n         │ (De-identified query)\n         ▼\n┌─────────────────┐    ┌─────────────────┐\n│ Audit Logger    │───▶│ Encrypted       │\n│ (Access Log)    │    │ Audit Storage   │\n└────────┬────────┘    └─────────────────┘\n         │\n         ▼\n┌─────────────────┐    ┌─────────────────┐\n│ OpenEvidence    │───▶│ Encrypted       │\n│ API Call        │    │ Cache           │\n└────────┬────────┘    └─────────────────┘\n         │\n         ▼\n┌─────────────────┐\n│ Response to     │\n│ Clinical User   │\n└─────────────────┘\n```\n\n## Output\n- PHI detection and sanitization\n- Patient context de-identification\n- Encrypted storage for cached data\n- HIPAA-compliant retention policies\n- Comprehensive audit trail\n\n## Data Handling Checklist\n- [ ] PHI patterns defined and detected\n- [ ] Patient context de-identified before queries\n- [ ] All cached data encrypted (AES-256)\n- [ ] Audit logging for all PHI access\n- [ ] 6-year retention for audit logs\n- [ ] Daily retention job running\n- [ ] Encryption keys managed securely\n- [ ] BAA signed with OpenEvidence\n\n## Resources\n- [HIPAA Privacy Rule](https:\u002F\u002Fwww.hhs.gov\u002Fhipaa\u002Ffor-professionals\u002Fprivacy\u002Findex.html)\n- [HIPAA Security Rule](https:\u002F\u002Fwww.hhs.gov\u002Fhipaa\u002Ffor-professionals\u002Fsecurity\u002Findex.html)\n- [OpenEvidence Security](https:\u002F\u002Fwww.openevidence.com\u002Fsecurity)\n\n## Next Steps\nFor enterprise access control, see `openevidence-enterprise-rbac`.\n",{"name":5,"author":52,"license":15,"version":53,"description":54,"allowed-tools":55},"Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Implement HIPAA-compliant PHI data handling for OpenEvidence integrations.\nUse when implementing data protection, configuring retention policies,\nor ensuring compliance for clinical AI data flows.\nTrigger with phrases like \"openevidence phi\", \"openevidence data\",\n\"openevidence hipaa data\", \"clinical data handling\", \"patient data protection\".\n","Read, Write, Edit","main","90b50ed0778745a80fd5bfb13b1843d45b9e82f7","tests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fopenevidence-pack\u002Fskills\u002Fopenevidence-data-handling",234,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust","https:\u002F\u002Fgithub.com\u002FDicklesworthstone","https:\u002F\u002Fwww.jeffreyemanuel.com\u002F","Building in NY","2026-11-13 11:50:27",{"source":8,"skillName":5,"commands":66,"prompt":76,"zipAvailable":77},[67,70,73],{"tool":68,"command":69},"npx","npx skills add Dicklesworthstone\u002Fpi_agent_rust --skill openevidence-data-handling",{"tool":71,"command":72},"bunx","bunx skills add Dicklesworthstone\u002Fpi_agent_rust --skill openevidence-data-handling",{"tool":74,"command":75},"pnpm","pnpm dlx skills add Dicklesworthstone\u002Fpi_agent_rust --skill openevidence-data-handling","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fopenevidence-pack\u002Fskills\u002Fopenevidence-data-handling\u002FSKILL.md",true,[],[80,119,143,168,194,216],{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":84,"licenseFileUrl":85,"sourceHtmlUrl":86,"categories":87,"keywords":93,"publishedAt":47},"01M6FEGPPK0ZTKCCM0R3E3KS33","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FSKILL.md",[88],{"categoryNo":89,"categoryName":90,"taxonomyType":91,"taxonomyTypeDesc":92},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,38,112,113,114,115,116,28,117,118],"web artifacts builder","web","artifacts","artifacts builder","builder","frontend","css","components","react","component","routing","state management","state","shadcn","file","ui","Tools","Tailwind","html","single-file","simple","ui components","multi","jsx","suite",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":84,"licenseFileUrl":123,"sourceHtmlUrl":124,"categories":125,"keywords":130,"publishedAt":47},"01M6FEE9VTK1W4GY21AKREJXRG","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FSKILL.md",[126,129],{"categoryNo":127,"categoryName":128,"taxonomyType":91,"taxonomyTypeDesc":92},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":89,"categoryName":90,"taxonomyType":91,"taxonomyTypeDesc":92},[131,132,133,99,134,135,95,136,137,138,109,139,140,141,142,30,28],"testing","webapp testing","webapp","screenshots","playwright","debugging","toolkit","browser","logs","applications","behavior","local",{"skillNo":144,"name":145,"nameZh":6,"description":146,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":147,"sourceHtmlUrl":148,"categories":149,"keywords":150,"publishedAt":47},"01M6FE20F7ZTQBFWKF4MPJ2SBX","neuropixels-analysis","Neuropixels neural recording analysis. Load SpikeGLX\u002FOpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen\u002FIBL curation, AI-assisted visual analysis, for Neuropixels 1.0\u002F2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FSKILL.md",[],[151,152,153,154,24,155,156,157,38,158,159,160,161,162,163,164,30,165,166,167,28,27],"analysis","neuropixels analysis","spike","metrics","open","quality","user","recordings","Neural","motion","visual","quality metrics","unit","sorting","curation","recording","load",{"skillNo":169,"name":170,"nameZh":6,"description":171,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":84,"licenseFileUrl":172,"sourceHtmlUrl":173,"categories":174,"keywords":178,"publishedAt":47},"01M6FDP0PDK6F1GGX395C7625Z","skill-creator","Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FSKILL.md",[175],{"categoryNo":176,"categoryName":177,"taxonomyType":91,"taxonomyTypeDesc":92},"01JBSEEDCAT000000000000023","Skill与智能体开发",[179,180,181,182,183,184,185,186,42,187,188,189,190,191,30,28,192,27,193],"skill creator","creator","workflows","create","update","capabilities","knowledge","tool","specialized","guide","new skill","existing skill","users","Existing","new",{"skillNo":195,"name":196,"nameZh":6,"description":197,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":198,"sourceHtmlUrl":199,"categories":200,"keywords":202,"publishedAt":47},"01M6FDM8Y7VK7NYG9SM7QPA3PW","notebooklm","Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FSKILL.md",[201],{"categoryNo":176,"categoryName":177,"taxonomyType":91,"taxonomyTypeDesc":92},[196,203,204,205,206,207,208,138,209,210,38,211,212,213,214,28,215],"google","browser automation","gemini","automation","auth","code","library","management","only","document","query","Citation","source",{"skillNo":217,"name":218,"nameZh":6,"description":219,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":84,"licenseFileUrl":220,"sourceHtmlUrl":221,"categories":222,"keywords":229,"publishedAt":47},"01M6FDHPXE10J3XGTCH5HBHS1M","slack-gif-creator","Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.\"","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FSKILL.md",[223,226],{"categoryNo":224,"categoryName":225,"taxonomyType":91,"taxonomyTypeDesc":92},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":227,"categoryName":228,"taxonomyType":91,"taxonomyTypeDesc":92},"01JBSEEDCAT000000000000016","图像与多媒体",[230,231,232,180,233,110,234,235,185,38,236,191,237,238,239,28,240,27],"slack gif creator","slack","gif","validation","constraints","Animation","me","make","Utilities","concepts","request"]