docs+ci(v0.8.12): Resume by UUID, triage workflows, CHANGELOG refresh
- README Usage block now documents `deepseek resume <SESSION_ID>` and `deepseek fork <SESSION_ID>`. Both commands have existed since v0.7 but were undiscoverable; #682 reported "no way to resume." - New GitHub Actions for issue triage (#688): * triage.yml — keyword-driven auto-labeller (bug / feat / docs / question, area:* by file-path mention, os:*, lang:zh on CJK titles). Only adds labels that already exist on the repo so it can't create noise unilaterally. * stale.yml — 14 d stale → 7 d close on `needs-info` issues only; PRs untouched; respects pinned/keep-open/ bug/security exemptions. * spam-lockdown.yml — auto-closes promotional issues from accounts <30 days old. Pure github-script (no third-party action) so the matching rules stay readable. - CHANGELOG (v0.8.12) updated: README install rewrite (#672), Scoop (#696), pricing extension (#692), Resume docs surface, and the cargo-install-on-stable fix from the previous commit. Lease "pending" caveat removed since it's now actually fixed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
name: Lock down obvious spam issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
lockdown:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Auto-close spam patterns from new accounts
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const author = issue.user;
|
||||
|
||||
// Only consider brand-new accounts. If the user has been around
|
||||
// long enough to file good-faith issues elsewhere, don't touch.
|
||||
const created = new Date(author.created_at || 0);
|
||||
const ageDays = (Date.now() - created.getTime()) / 86_400_000;
|
||||
if (ageDays > 30) return;
|
||||
|
||||
const blob = `${issue.title || ''}\n${issue.body || ''}`;
|
||||
const patterns = [
|
||||
/\bcrypto\b/i,
|
||||
/\bairdrop\b/i,
|
||||
/\bnft\b/i,
|
||||
/\bpresale\b/i,
|
||||
/\busdt\b/i,
|
||||
/\btg\s*@/i,
|
||||
/\btelegram\s+@/i,
|
||||
/\bt\.me\//i,
|
||||
/\bwhatsapp\s+\+/i,
|
||||
/\bseo\s+service/i,
|
||||
/\bguest\s+post/i,
|
||||
/\bbacklink/i,
|
||||
/\bbuy\s+followers/i,
|
||||
/\bjoin\s+our\s+(community|server|group)/i,
|
||||
/\bpromot[ei]\s+your\b/i,
|
||||
];
|
||||
const hit = patterns.find(p => p.test(blob));
|
||||
if (!hit) return;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: [
|
||||
'This issue was auto-closed because the title or body matches',
|
||||
'a spam pattern (paid promotion / unrelated link) and the author',
|
||||
'account is less than 30 days old. If this is a real bug or',
|
||||
'feature request, please reopen with a clearer description',
|
||||
'(in English or 中文) of the project-relevant context.',
|
||||
].join(' '),
|
||||
});
|
||||
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed',
|
||||
state_reason: 'not_planned',
|
||||
});
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: ['spam'],
|
||||
}).catch(() => {}); // ignore if label doesn't exist yet
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Close stale issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '17 5 * * *' # daily, off-peak
|
||||
workflow_dispatch: {}
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 7
|
||||
stale-issue-message: >
|
||||
This issue has been inactive for 14 days while waiting on
|
||||
additional information. It will close automatically in 7 days
|
||||
unless someone responds. If you still need help, drop a
|
||||
comment with the requested details and a maintainer can
|
||||
reopen.
|
||||
close-issue-message: >
|
||||
Closing for inactivity. Feel free to comment to reopen if
|
||||
you can share the requested information.
|
||||
stale-issue-label: 'stale'
|
||||
only-labels: 'needs-info'
|
||||
exempt-issue-labels: 'pinned,keep-open,bug,security'
|
||||
# Don't touch PRs — `actions/stale` defaults can be aggressive
|
||||
# there. We only want it for `needs-info` issues.
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
operations-per-run: 60
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Issue triage
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Auto-label by title and body
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const title = (issue.title || '').toLowerCase();
|
||||
const body = (issue.body || '').toLowerCase();
|
||||
const text = `${title}\n${body}`;
|
||||
const labels = new Set();
|
||||
|
||||
// Type
|
||||
if (/\b(bug|crash|panic|broken|stack ?trace|regression|err(?:or)?|fail(?:ed|ure)?)\b/.test(text)) labels.add('bug');
|
||||
if (/\b(feat(?:ure)?|request|enhancement|wishlist|proposal|please add|would be nice|support for)\b/.test(text)) labels.add('enhancement');
|
||||
if (/\b(docs?|readme|documentation|typo|grammar|wording|spelling)\b/.test(text)) labels.add('documentation');
|
||||
if (/\b(question|how (?:do|to)|why does|what does|is it possible)\b/.test(text)) labels.add('question');
|
||||
|
||||
// Locale — title contains CJK (Chinese, Japanese, Korean) characters
|
||||
if (/[-ヿ㐀-鿿가-]/.test(issue.title || '')) labels.add('lang:zh');
|
||||
|
||||
// Areas (path-driven hints)
|
||||
if (/crates\/tui|\btui\b|ratatui|composer|sidebar/.test(text)) labels.add('area:tui');
|
||||
if (/crates\/core|engine|turn ?loop|agent ?loop/.test(text)) labels.add('area:core');
|
||||
if (/crates\/mcp|\bmcp\b/.test(text)) labels.add('area:mcp');
|
||||
if (/crates\/state|sqlite|sessions?|persistence/.test(text)) labels.add('area:state');
|
||||
if (/crates\/execpolicy|approval|sandbox|seatbelt|landlock/.test(text)) labels.add('area:execpolicy');
|
||||
if (/crates\/tools|tool[ _]call|tool[ _]registry/.test(text)) labels.add('area:tools');
|
||||
if (/install|cargo install|npm install|scoop|homebrew|prebuilt|binary/.test(text)) labels.add('area:install');
|
||||
if (/windows/.test(text)) labels.add('os:windows');
|
||||
if (/macos|darwin|apple silicon/.test(text)) labels.add('os:macos');
|
||||
if (/\blinux\b|ubuntu|debian|fedora|arch ?linux/.test(text)) labels.add('os:linux');
|
||||
|
||||
if (labels.size === 0) return;
|
||||
|
||||
// Only add labels that already exist on the repo to avoid creating noise.
|
||||
const existing = await github.paginate(github.rest.issues.listLabelsForRepo, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
});
|
||||
const existingNames = new Set(existing.map(l => l.name));
|
||||
const toAdd = [...labels].filter(name => existingNames.has(name));
|
||||
if (toAdd.length === 0) return;
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: toAdd,
|
||||
});
|
||||
Reference in New Issue
Block a user