[Bug] caveman skill /caveman lite|full|ultra does not inject compression directives into agent system prompt #41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Sync GitHub issues to Feishu | |
| on: | |
| issues: | |
| types: [opened] | |
| permissions: | |
| contents: read | |
| issues: read | |
| jobs: | |
| notify-feishu: | |
| name: Notify Feishu | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: ${{ !github.event.issue.pull_request }} | |
| env: | |
| FEISHU_BOT_WEBHOOK: ${{ secrets.FEISHU_BOT_WEBHOOK }} | |
| FEISHU_BOT_SECRET: ${{ secrets.FEISHU_BOT_SECRET }} | |
| steps: | |
| - name: Check Feishu webhook | |
| if: ${{ env.FEISHU_BOT_WEBHOOK == '' }} | |
| run: | | |
| echo "::error::Missing repository secret FEISHU_BOT_WEBHOOK" | |
| exit 1 | |
| - name: Build Feishu payload | |
| shell: bash | |
| run: | | |
| node <<'EOF' | |
| const crypto = require('crypto'); | |
| const fs = require('fs'); | |
| const event = JSON.parse( | |
| fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'), | |
| ); | |
| const issue = event.issue; | |
| const repository = event.repository; | |
| const repoName = repository.full_name; | |
| const normalizeText = (value) => | |
| String(value || '') | |
| .replace(/\r\n/g, '\n') | |
| .replace(/\n{3,}/g, '\n\n') | |
| .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, '') | |
| .trim(); | |
| const truncate = (value, maxLength) => | |
| value.length > maxLength | |
| ? `${value.slice(0, maxLength - 1)}...` | |
| : value; | |
| const escapeLarkMarkdown = (value) => | |
| normalizeText(value) | |
| .replace(/\\/g, '\\\\') | |
| .replace(/`/g, '\\`') | |
| .replace(/\*/g, '\\*') | |
| .replace(/_/g, '\\_') | |
| .replace(/\[/g, '\\[') | |
| .replace(/\]/g, '\\]') | |
| .replace(/\(/g, '\\(') | |
| .replace(/\)/g, '\\)') | |
| .replace(/~/g, '\\~') | |
| .replace(/</g, '‹') | |
| .replace(/>/g, '›'); | |
| const labelNames = issue.labels.map((label) => label.name); | |
| const getIssueKind = (labels) => { | |
| if (labels.includes('bug')) { | |
| return 'bug'; | |
| } | |
| if (labels.includes('enhancement')) { | |
| return 'enhancement'; | |
| } | |
| if (labels.includes('question')) { | |
| return 'question'; | |
| } | |
| return 'issue'; | |
| }; | |
| const issueKind = getIssueKind(labelNames); | |
| const primaryLabel = labelNames[0] || issueKind; | |
| const headerTemplateByKind = { | |
| bug: 'red', | |
| enhancement: 'green', | |
| question: 'blue', | |
| issue: 'blue', | |
| }; | |
| const getLabelColor = (label) => ({ | |
| bug: 'red', | |
| enhancement: 'green', | |
| question: 'blue', | |
| })[label] || 'neutral'; | |
| const headerTemplate = headerTemplateByKind[issueKind]; | |
| const labelTags = | |
| labelNames | |
| .map((label) => { | |
| const color = getLabelColor(label); | |
| return `<text_tag color='${color}'>${escapeLarkMarkdown(label)}</text_tag>`; | |
| }) | |
| .join(' ') || "<font color='grey'>none</font>"; | |
| const body = issue.body || ''; | |
| const bodyPreview = truncate(escapeLarkMarkdown(body) || '(empty)', 1000); | |
| const title = truncate(normalizeText(issue.title).replace(/\n/g, ' '), 100); | |
| const createdAt = new Intl.DateTimeFormat('zh-CN', { | |
| year: 'numeric', | |
| month: '2-digit', | |
| day: '2-digit', | |
| hour: '2-digit', | |
| minute: '2-digit', | |
| hour12: false, | |
| timeZone: 'Asia/Shanghai', | |
| }) | |
| .format(new Date(issue.created_at)) | |
| .replace(/\//g, '-'); | |
| const payload = { | |
| msg_type: 'interactive', | |
| card: { | |
| config: { | |
| enable_forward: true, | |
| update_multi: true, | |
| }, | |
| card_link: { | |
| url: issue.html_url, | |
| }, | |
| header: { | |
| template: headerTemplate, | |
| title: { | |
| tag: 'plain_text', | |
| content: `GitHub Issue #${issue.number}: ${title}`, | |
| }, | |
| }, | |
| elements: [ | |
| { | |
| tag: 'markdown', | |
| content: [ | |
| `**Repository:** [${repoName}](${repository.html_url})`, | |
| `**Type:** ${escapeLarkMarkdown(primaryLabel)}`, | |
| `**Author:** ${escapeLarkMarkdown(issue.user.login)}`, | |
| `**Created:** ${createdAt} UTC+8`, | |
| `**Labels:** ${labelTags}`, | |
| ].join('\n'), | |
| }, | |
| { | |
| tag: 'hr', | |
| }, | |
| { | |
| tag: 'markdown', | |
| content: `**Summary**\n${bodyPreview}`, | |
| }, | |
| { | |
| tag: 'action', | |
| actions: [ | |
| { | |
| tag: 'button', | |
| text: { | |
| tag: 'plain_text', | |
| content: 'Open issue', | |
| }, | |
| type: 'primary', | |
| url: issue.html_url, | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| }; | |
| const secret = process.env.FEISHU_BOT_SECRET; | |
| if (secret) { | |
| const timestamp = Math.floor(Date.now() / 1000).toString(); | |
| const stringToSign = `${timestamp}\n${secret}`; | |
| payload.timestamp = timestamp; | |
| payload.sign = crypto | |
| .createHmac('sha256', stringToSign) | |
| .update('') | |
| .digest('base64'); | |
| } | |
| fs.writeFileSync('feishu-payload.json', JSON.stringify(payload)); | |
| EOF | |
| - name: Send Feishu notification | |
| shell: bash | |
| run: | | |
| response="$( | |
| curl --fail-with-body --show-error --silent \ | |
| --request POST \ | |
| --header 'Content-Type: application/json' \ | |
| --data-binary @feishu-payload.json \ | |
| "$FEISHU_BOT_WEBHOOK" | |
| )" | |
| RESPONSE="$response" node <<'EOF' | |
| const response = JSON.parse(process.env.RESPONSE); | |
| const success = | |
| response.code === 0 || | |
| response.StatusCode === 0 || | |
| response.StatusMessage === 'success'; | |
| if (!success) { | |
| console.error(JSON.stringify(response)); | |
| process.exit(1); | |
| } | |
| console.log('Feishu notification sent.'); | |
| EOF |