[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F97EB9VDDG94VXV9V8BPMP":3,"skill-same:01M6F97EB9VDDG94VXV9V8BPMP":80},{"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":26,"publishedAt":48,"linkOnly":49,"markdownContent":50,"markdownBody":51,"frontMatter":52,"refName":57,"commitSha":58,"skillDirectory":59,"forksCount":60,"repositoryHtmlUrl":61,"repositoryHomepageUrl":6,"ownerHtmlUrl":62,"ownerBlogUrl":63,"ownerDescription":64,"updatedAt":65,"install":66,"resources":79},"01M6F97EB9VDDG94VXV9V8BPMP","speak-ci-integration",null,"Configure Speak CI\u002FCD integration with GitHub Actions and automated testing.\nUse when setting up automated testing, configuring CI pipelines,\nor integrating Speak language learning tests into your build process.\nTrigger with phrases like \"speak CI\", \"speak GitHub Actions\",\n\"speak automated tests\", \"CI speak\".","Dicklesworthstone\u002Fpi_agent_rust","Dicklesworthstone","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F35050222?v=4","01M6CG26AR942CSVMN1M5E3DSE","Jeff Emanuel","01M6CG26EPP71PSHW1VDS71121",1992,"MIT","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fspeak-pack\u002Fskills\u002Fspeak-ci-integration\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],"integration","ci","speak ci integration","speak","when","testing","CI\u002FCD","trigger","io","tests","language learning","up","edit","write","read","gh","configure","learning","Bash","github actions","actions","2026-11-10 10:12:30",false,"---\nname: speak-ci-integration\ndescription: |\n  Configure Speak CI\u002FCD integration with GitHub Actions and automated testing.\n  Use when setting up automated testing, configuring CI pipelines,\n  or integrating Speak language learning tests into your build process.\n  Trigger with phrases like \"speak CI\", \"speak GitHub Actions\",\n  \"speak automated tests\", \"CI speak\".\nallowed-tools: Read, Write, Edit, Bash(gh:*)\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\n---\n\n# Speak CI Integration\n\n## Overview\nSet up CI\u002FCD pipelines for Speak language learning integrations with automated testing.\n\n## Prerequisites\n- GitHub repository with Actions enabled\n- Speak test API key (sandbox environment)\n- npm\u002Fpnpm project configured\n- Test audio samples for speech recognition tests\n\n## Instructions\n\n### Step 1: Create GitHub Actions Workflow\nCreate `.github\u002Fworkflows\u002Fspeak-integration.yml`:\n\n```yaml\nname: Speak Integration Tests\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\nenv:\n  SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY }}\n  SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID }}\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run unit tests\n        run: npm test -- --coverage\n\n      - name: Upload coverage\n        uses: codecov\u002Fcodecov-action@v4\n        with:\n          files: .\u002Fcoverage\u002Flcov.info\n\n  integration-tests:\n    runs-on: ubuntu-latest\n    needs: unit-tests\n    env:\n      SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY_TEST }}\n      SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID_TEST }}\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run Speak integration tests\n        run: npm run test:integration\n        timeout-minutes: 10\n\n  speech-recognition-tests:\n    runs-on: ubuntu-latest\n    needs: unit-tests\n    env:\n      SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY_TEST }}\n      SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID_TEST }}\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      # Download test audio files\n      - name: Cache test audio\n        uses: actions\u002Fcache@v4\n        with:\n          path: tests\u002Ffixtures\u002Faudio\n          key: test-audio-v1\n\n      - name: Download test audio fixtures\n        run: npm run download:test-audio\n\n      - name: Run speech recognition tests\n        run: npm run test:speech\n        timeout-minutes: 15\n```\n\n### Step 2: Configure Secrets\n```bash\n# Set Speak API credentials\ngh secret set SPEAK_API_KEY --body \"sk_test_***\"\ngh secret set SPEAK_APP_ID --body \"app_***\"\n\n# Separate test environment credentials\ngh secret set SPEAK_API_KEY_TEST --body \"sk_sandbox_***\"\ngh secret set SPEAK_APP_ID_TEST --body \"app_sandbox_***\"\n```\n\n### Step 3: Create Integration Tests\n```typescript\n\u002F\u002F tests\u002Fintegration\u002Fspeak.test.ts\nimport { describe, it, expect, beforeAll, afterAll } from 'vitest';\nimport { SpeakClient, AITutor, LessonSession } from '@speak\u002Flanguage-sdk';\n\ndescribe('Speak Integration', () => {\n  let client: SpeakClient;\n  let session: LessonSession | null = null;\n\n  beforeAll(() => {\n    client = new SpeakClient({\n      apiKey: process.env.SPEAK_API_KEY!,\n      appId: process.env.SPEAK_APP_ID!,\n      language: 'es',\n    });\n  });\n\n  afterAll(async () => {\n    if (session) {\n      await session.end();\n    }\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should verify API health', async () => {\n    const health = await client.health.check();\n    expect(health.healthy).toBe(true);\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should start a lesson session', async () => {\n    const tutor = new AITutor(client, {\n      targetLanguage: 'es',\n      nativeLanguage: 'en',\n      proficiencyLevel: 'beginner',\n    });\n\n    session = await tutor.startSession({\n      topic: 'greetings',\n      duration: 5,\n    });\n\n    expect(session.id).toBeDefined();\n    expect(session.status).toBe('active');\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should get tutor prompt', async () => {\n    if (!session) {\n      throw new Error('Session not initialized');\n    }\n\n    const prompt = await session.getPrompt();\n    expect(prompt.text).toBeTruthy();\n    expect(prompt.audioUrl).toMatch(\u002F^https:\\\u002F\\\u002F\u002F);\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should submit response and get feedback', async () => {\n    if (!session) {\n      throw new Error('Session not initialized');\n    }\n\n    const feedback = await session.submitResponse({\n      text: 'Hola, me llamo Juan',\n    });\n\n    expect(feedback.pronunciationScore).toBeGreaterThanOrEqual(0);\n    expect(feedback.pronunciationScore).toBeLessThanOrEqual(100);\n    expect(feedback.message).toBeTruthy();\n  });\n});\n```\n\n### Step 4: Speech Recognition CI Tests\n```typescript\n\u002F\u002F tests\u002Fintegration\u002Fspeech.test.ts\nimport { describe, it, expect, beforeAll } from 'vitest';\nimport { readFile } from 'fs\u002Fpromises';\nimport { join } from 'path';\nimport { SpeakClient } from '@speak\u002Flanguage-sdk';\n\ndescribe('Speech Recognition', () => {\n  let client: SpeakClient;\n\n  beforeAll(() => {\n    client = new SpeakClient({\n      apiKey: process.env.SPEAK_API_KEY!,\n      appId: process.env.SPEAK_APP_ID!,\n      language: 'es',\n    });\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should recognize Spanish speech', async () => {\n    \u002F\u002F Load test audio file\n    const audioPath = join(__dirname, '..\u002Ffixtures\u002Faudio\u002Fhola-spanish.wav');\n    const audioBuffer = await readFile(audioPath);\n\n    const result = await client.speech.recognize(audioBuffer.buffer);\n\n    expect(result.transcript.toLowerCase()).toContain('hola');\n    expect(result.confidence).toBeGreaterThan(0.5);\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should score pronunciation', async () => {\n    const audioPath = join(__dirname, '..\u002Ffixtures\u002Faudio\u002Fbuenos-dias.wav');\n    const audioBuffer = await readFile(audioPath);\n\n    const result = await client.speech.score({\n      audio: audioBuffer.buffer,\n      expectedText: 'Buenos días',\n      detailed: true,\n    });\n\n    expect(result.overall).toBeGreaterThanOrEqual(0);\n    expect(result.overall).toBeLessThanOrEqual(100);\n    expect(result.words).toHaveLength(2);\n  });\n});\n```\n\n### Step 5: Add Test Audio Download Script\n```typescript\n\u002F\u002F scripts\u002Fdownload-test-audio.ts\nimport { writeFile, mkdir } from 'fs\u002Fpromises';\nimport { join } from 'path';\n\nconst FIXTURES_DIR = join(__dirname, '..\u002Ftests\u002Ffixtures\u002Faudio');\n\nconst TEST_AUDIO_FILES = [\n  {\n    name: 'hola-spanish.wav',\n    url: 'https:\u002F\u002Fexample.com\u002Ftest-audio\u002Fhola-spanish.wav',\n  },\n  {\n    name: 'buenos-dias.wav',\n    url: 'https:\u002F\u002Fexample.com\u002Ftest-audio\u002Fbuenos-dias.wav',\n  },\n  \u002F\u002F Add more test files as needed\n];\n\nasync function downloadTestAudio() {\n  await mkdir(FIXTURES_DIR, { recursive: true });\n\n  for (const file of TEST_AUDIO_FILES) {\n    const response = await fetch(file.url);\n    const buffer = await response.arrayBuffer();\n    await writeFile(join(FIXTURES_DIR, file.name), Buffer.from(buffer));\n    console.log(`Downloaded: ${file.name}`);\n  }\n}\n\ndownloadTestAudio();\n```\n\n## Output\n- Automated test pipeline\n- PR checks configured\n- Coverage reports uploaded\n- Speech recognition tests included\n- Release workflow ready\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Secret not found | Missing configuration | Add secret via `gh secret set` |\n| Tests timeout | Audio processing slow | Increase timeout or mock |\n| Auth failures | Invalid key | Check secret value |\n| Audio fixture missing | Not downloaded | Run download script |\n| Flaky speech tests | Audio quality | Use high-quality fixtures |\n\n## Examples\n\n### Release Workflow\n```yaml\n# .github\u002Fworkflows\u002Frelease.yml\nname: Release\n\non:\n  push:\n    tags: ['v*']\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    env:\n      SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY_PROD }}\n      SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID_PROD }}\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          registry-url: 'https:\u002F\u002Fregistry.npmjs.org'\n\n      - run: npm ci\n      - run: npm test\n      - run: npm run test:integration\n\n      - name: Verify Speak production readiness\n        run: |\n          npm run speak:health-check\n          npm run speak:smoke-test\n\n      - run: npm run build\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n```\n\n### Branch Protection\n```yaml\n# Required status checks\nrequired_status_checks:\n  strict: true\n  contexts:\n    - \"unit-tests\"\n    - \"integration-tests\"\n    - \"speech-recognition-tests\"\n```\n\n### Mock-Based Fast Tests\n```typescript\n\u002F\u002F tests\u002Funit\u002Fspeak-mocked.test.ts\nimport { describe, it, expect, vi, beforeEach } from 'vitest';\nimport { mockSpeakClient } from '..\u002Fmocks\u002Fspeak-mock';\n\nvi.mock('@speak\u002Flanguage-sdk', () => ({\n  SpeakClient: vi.fn().mockImplementation(() => mockSpeakClient),\n}));\n\ndescribe('Speak Client (Mocked)', () => {\n  beforeEach(() => {\n    vi.clearAllMocks();\n  });\n\n  it('should initialize client', () => {\n    \u002F\u002F Fast test without network\n  });\n});\n```\n\n## Resources\n- [GitHub Actions Documentation](https:\u002F\u002Fdocs.github.com\u002Fen\u002Factions)\n- [Speak CI Guide](https:\u002F\u002Fdeveloper.speak.com\u002Fdocs\u002Fci)\n- [Vitest CI Configuration](https:\u002F\u002Fvitest.dev\u002Fguide\u002Fci)\n\n## Next Steps\nFor deployment patterns, see `speak-deploy-integration`.\n","# Speak CI Integration\n\n## Overview\nSet up CI\u002FCD pipelines for Speak language learning integrations with automated testing.\n\n## Prerequisites\n- GitHub repository with Actions enabled\n- Speak test API key (sandbox environment)\n- npm\u002Fpnpm project configured\n- Test audio samples for speech recognition tests\n\n## Instructions\n\n### Step 1: Create GitHub Actions Workflow\nCreate `.github\u002Fworkflows\u002Fspeak-integration.yml`:\n\n```yaml\nname: Speak Integration Tests\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\nenv:\n  SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY }}\n  SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID }}\n\njobs:\n  unit-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run unit tests\n        run: npm test -- --coverage\n\n      - name: Upload coverage\n        uses: codecov\u002Fcodecov-action@v4\n        with:\n          files: .\u002Fcoverage\u002Flcov.info\n\n  integration-tests:\n    runs-on: ubuntu-latest\n    needs: unit-tests\n    env:\n      SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY_TEST }}\n      SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID_TEST }}\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run Speak integration tests\n        run: npm run test:integration\n        timeout-minutes: 10\n\n  speech-recognition-tests:\n    runs-on: ubuntu-latest\n    needs: unit-tests\n    env:\n      SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY_TEST }}\n      SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID_TEST }}\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      # Download test audio files\n      - name: Cache test audio\n        uses: actions\u002Fcache@v4\n        with:\n          path: tests\u002Ffixtures\u002Faudio\n          key: test-audio-v1\n\n      - name: Download test audio fixtures\n        run: npm run download:test-audio\n\n      - name: Run speech recognition tests\n        run: npm run test:speech\n        timeout-minutes: 15\n```\n\n### Step 2: Configure Secrets\n```bash\n# Set Speak API credentials\ngh secret set SPEAK_API_KEY --body \"sk_test_***\"\ngh secret set SPEAK_APP_ID --body \"app_***\"\n\n# Separate test environment credentials\ngh secret set SPEAK_API_KEY_TEST --body \"sk_sandbox_***\"\ngh secret set SPEAK_APP_ID_TEST --body \"app_sandbox_***\"\n```\n\n### Step 3: Create Integration Tests\n```typescript\n\u002F\u002F tests\u002Fintegration\u002Fspeak.test.ts\nimport { describe, it, expect, beforeAll, afterAll } from 'vitest';\nimport { SpeakClient, AITutor, LessonSession } from '@speak\u002Flanguage-sdk';\n\ndescribe('Speak Integration', () => {\n  let client: SpeakClient;\n  let session: LessonSession | null = null;\n\n  beforeAll(() => {\n    client = new SpeakClient({\n      apiKey: process.env.SPEAK_API_KEY!,\n      appId: process.env.SPEAK_APP_ID!,\n      language: 'es',\n    });\n  });\n\n  afterAll(async () => {\n    if (session) {\n      await session.end();\n    }\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should verify API health', async () => {\n    const health = await client.health.check();\n    expect(health.healthy).toBe(true);\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should start a lesson session', async () => {\n    const tutor = new AITutor(client, {\n      targetLanguage: 'es',\n      nativeLanguage: 'en',\n      proficiencyLevel: 'beginner',\n    });\n\n    session = await tutor.startSession({\n      topic: 'greetings',\n      duration: 5,\n    });\n\n    expect(session.id).toBeDefined();\n    expect(session.status).toBe('active');\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should get tutor prompt', async () => {\n    if (!session) {\n      throw new Error('Session not initialized');\n    }\n\n    const prompt = await session.getPrompt();\n    expect(prompt.text).toBeTruthy();\n    expect(prompt.audioUrl).toMatch(\u002F^https:\\\u002F\\\u002F\u002F);\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should submit response and get feedback', async () => {\n    if (!session) {\n      throw new Error('Session not initialized');\n    }\n\n    const feedback = await session.submitResponse({\n      text: 'Hola, me llamo Juan',\n    });\n\n    expect(feedback.pronunciationScore).toBeGreaterThanOrEqual(0);\n    expect(feedback.pronunciationScore).toBeLessThanOrEqual(100);\n    expect(feedback.message).toBeTruthy();\n  });\n});\n```\n\n### Step 4: Speech Recognition CI Tests\n```typescript\n\u002F\u002F tests\u002Fintegration\u002Fspeech.test.ts\nimport { describe, it, expect, beforeAll } from 'vitest';\nimport { readFile } from 'fs\u002Fpromises';\nimport { join } from 'path';\nimport { SpeakClient } from '@speak\u002Flanguage-sdk';\n\ndescribe('Speech Recognition', () => {\n  let client: SpeakClient;\n\n  beforeAll(() => {\n    client = new SpeakClient({\n      apiKey: process.env.SPEAK_API_KEY!,\n      appId: process.env.SPEAK_APP_ID!,\n      language: 'es',\n    });\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should recognize Spanish speech', async () => {\n    \u002F\u002F Load test audio file\n    const audioPath = join(__dirname, '..\u002Ffixtures\u002Faudio\u002Fhola-spanish.wav');\n    const audioBuffer = await readFile(audioPath);\n\n    const result = await client.speech.recognize(audioBuffer.buffer);\n\n    expect(result.transcript.toLowerCase()).toContain('hola');\n    expect(result.confidence).toBeGreaterThan(0.5);\n  });\n\n  it.skipIf(!process.env.SPEAK_API_KEY)('should score pronunciation', async () => {\n    const audioPath = join(__dirname, '..\u002Ffixtures\u002Faudio\u002Fbuenos-dias.wav');\n    const audioBuffer = await readFile(audioPath);\n\n    const result = await client.speech.score({\n      audio: audioBuffer.buffer,\n      expectedText: 'Buenos días',\n      detailed: true,\n    });\n\n    expect(result.overall).toBeGreaterThanOrEqual(0);\n    expect(result.overall).toBeLessThanOrEqual(100);\n    expect(result.words).toHaveLength(2);\n  });\n});\n```\n\n### Step 5: Add Test Audio Download Script\n```typescript\n\u002F\u002F scripts\u002Fdownload-test-audio.ts\nimport { writeFile, mkdir } from 'fs\u002Fpromises';\nimport { join } from 'path';\n\nconst FIXTURES_DIR = join(__dirname, '..\u002Ftests\u002Ffixtures\u002Faudio');\n\nconst TEST_AUDIO_FILES = [\n  {\n    name: 'hola-spanish.wav',\n    url: 'https:\u002F\u002Fexample.com\u002Ftest-audio\u002Fhola-spanish.wav',\n  },\n  {\n    name: 'buenos-dias.wav',\n    url: 'https:\u002F\u002Fexample.com\u002Ftest-audio\u002Fbuenos-dias.wav',\n  },\n  \u002F\u002F Add more test files as needed\n];\n\nasync function downloadTestAudio() {\n  await mkdir(FIXTURES_DIR, { recursive: true });\n\n  for (const file of TEST_AUDIO_FILES) {\n    const response = await fetch(file.url);\n    const buffer = await response.arrayBuffer();\n    await writeFile(join(FIXTURES_DIR, file.name), Buffer.from(buffer));\n    console.log(`Downloaded: ${file.name}`);\n  }\n}\n\ndownloadTestAudio();\n```\n\n## Output\n- Automated test pipeline\n- PR checks configured\n- Coverage reports uploaded\n- Speech recognition tests included\n- Release workflow ready\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Secret not found | Missing configuration | Add secret via `gh secret set` |\n| Tests timeout | Audio processing slow | Increase timeout or mock |\n| Auth failures | Invalid key | Check secret value |\n| Audio fixture missing | Not downloaded | Run download script |\n| Flaky speech tests | Audio quality | Use high-quality fixtures |\n\n## Examples\n\n### Release Workflow\n```yaml\n# .github\u002Fworkflows\u002Frelease.yml\nname: Release\n\non:\n  push:\n    tags: ['v*']\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    env:\n      SPEAK_API_KEY: ${{ secrets.SPEAK_API_KEY_PROD }}\n      SPEAK_APP_ID: ${{ secrets.SPEAK_APP_ID_PROD }}\n    steps:\n      - uses: actions\u002Fcheckout@v4\n\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: '20'\n          registry-url: 'https:\u002F\u002Fregistry.npmjs.org'\n\n      - run: npm ci\n      - run: npm test\n      - run: npm run test:integration\n\n      - name: Verify Speak production readiness\n        run: |\n          npm run speak:health-check\n          npm run speak:smoke-test\n\n      - run: npm run build\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n```\n\n### Branch Protection\n```yaml\n# Required status checks\nrequired_status_checks:\n  strict: true\n  contexts:\n    - \"unit-tests\"\n    - \"integration-tests\"\n    - \"speech-recognition-tests\"\n```\n\n### Mock-Based Fast Tests\n```typescript\n\u002F\u002F tests\u002Funit\u002Fspeak-mocked.test.ts\nimport { describe, it, expect, vi, beforeEach } from 'vitest';\nimport { mockSpeakClient } from '..\u002Fmocks\u002Fspeak-mock';\n\nvi.mock('@speak\u002Flanguage-sdk', () => ({\n  SpeakClient: vi.fn().mockImplementation(() => mockSpeakClient),\n}));\n\ndescribe('Speak Client (Mocked)', () => {\n  beforeEach(() => {\n    vi.clearAllMocks();\n  });\n\n  it('should initialize client', () => {\n    \u002F\u002F Fast test without network\n  });\n});\n```\n\n## Resources\n- [GitHub Actions Documentation](https:\u002F\u002Fdocs.github.com\u002Fen\u002Factions)\n- [Speak CI Guide](https:\u002F\u002Fdeveloper.speak.com\u002Fdocs\u002Fci)\n- [Vitest CI Configuration](https:\u002F\u002Fvitest.dev\u002Fguide\u002Fci)\n\n## Next Steps\nFor deployment patterns, see `speak-deploy-integration`.\n",{"name":5,"author":53,"license":15,"version":54,"description":55,"allowed-tools":56},"Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Configure Speak CI\u002FCD integration with GitHub Actions and automated testing.\nUse when setting up automated testing, configuring CI pipelines,\nor integrating Speak language learning tests into your build process.\nTrigger with phrases like \"speak CI\", \"speak GitHub Actions\",\n\"speak automated tests\", \"CI speak\".\n","Read, Write, Edit, Bash(gh:*)","main","90b50ed0778745a80fd5bfb13b1843d45b9e82f7","tests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fspeak-pack\u002Fskills\u002Fspeak-ci-integration",233,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust","https:\u002F\u002Fgithub.com\u002FDicklesworthstone","https:\u002F\u002Fwww.jeffreyemanuel.com\u002F","Building in NY","2026-11-15 01:54:15",{"source":8,"skillName":5,"commands":67,"prompt":77,"zipAvailable":78},[68,71,74],{"tool":69,"command":70},"npx","npx skills add Dicklesworthstone\u002Fpi_agent_rust --skill speak-ci-integration",{"tool":72,"command":73},"bunx","bunx skills add Dicklesworthstone\u002Fpi_agent_rust --skill speak-ci-integration",{"tool":75,"command":76},"pnpm","pnpm dlx skills add Dicklesworthstone\u002Fpi_agent_rust --skill speak-ci-integration","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fspeak-pack\u002Fskills\u002Fspeak-ci-integration\u002FSKILL.md",true,[],[81,120,142,168,195,217],{"skillNo":82,"name":83,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":85,"licenseFileUrl":86,"sourceHtmlUrl":87,"categories":88,"keywords":92,"publishedAt":48},"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",[89],{"categoryNo":90,"categoryName":91,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119],"web artifacts builder","web","artifacts","artifacts builder","builder","frontend","css","components","react","component","routing","state management","state","shadcn","file","ui","Tools","Tailwind","use","html","single-file","simple","ui components","multi","for","jsx","suite",{"skillNo":121,"name":122,"nameZh":6,"description":123,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":85,"licenseFileUrl":124,"sourceHtmlUrl":125,"categories":126,"keywords":129,"publishedAt":48},"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",[127,128],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":90,"categoryName":91,"taxonomyType":21,"taxonomyTypeDesc":22},[32,130,131,98,132,133,94,134,135,136,108,137,138,139,140,141,117],"webapp testing","webapp","screenshots","playwright","debugging","toolkit","browser","logs","applications","behavior","local","with",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":146,"sourceHtmlUrl":147,"categories":148,"keywords":149,"publishedAt":48},"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",[],[150,151,152,153,154,155,156,157,111,158,159,160,161,162,163,164,141,165,166,167,117,31],"analysis","neuropixels analysis","spike","metrics","data","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":85,"licenseFileUrl":172,"sourceHtmlUrl":173,"categories":174,"keywords":178,"publishedAt":48},"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":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[179,180,181,182,183,184,185,186,187,188,189,190,191,192,141,117,193,31,194],"skill creator","creator","workflows","create","update","capabilities","knowledge","tool","integrations","specialized","guide","new skill","existing skill","users","Existing","new",{"skillNo":196,"name":197,"nameZh":6,"description":198,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":199,"sourceHtmlUrl":200,"categories":201,"keywords":203,"publishedAt":48},"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",[202],{"categoryNo":176,"categoryName":177,"taxonomyType":21,"taxonomyTypeDesc":22},[197,204,205,206,207,208,209,136,210,211,111,212,213,214,215,117,216],"google","browser automation","gemini","automation","auth","code","library","management","only","document","query","Citation","source",{"skillNo":218,"name":219,"nameZh":6,"description":220,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":85,"licenseFileUrl":221,"sourceHtmlUrl":222,"categories":223,"keywords":230,"publishedAt":48},"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",[224,227],{"categoryNo":225,"categoryName":226,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":228,"categoryName":229,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[231,232,233,180,234,109,235,236,185,111,237,192,238,239,240,117,241,31],"slack gif creator","slack","gif","validation","constraints","Animation","me","make","Utilities","concepts","request"]