Skip to content

Commit 863ea7d

Browse files
Merge upstream/dev: resolve conflicts and fix home screen overlap bug
Merged latest changes from upstream/dev (135f8ff). Resolved conflicts: - session/index.tsx: Combined layout.showHeader config with upstream's showHeader() toggle and new header visibility logic - component/prompt/index.tsx: Merged selectedForeground import addition Bug fixes for layout system to preserve stock OpenCode behavior: - Fixed home screen input overlap: Only add paddingBottom when > 0 to preserve stock OpenCode's flex layout behavior (prompt/index.tsx:822) - Fixed default layout userMessagePaddingBottom: Changed from 0 to 1 to match stock OpenCode's hardcoded value (layout/default.jsonc:11) These fixes ensure the default layout behaves identically to stock OpenCode, following our core principle of identical default behavior.
1 parent a6dbaa6 commit 863ea7d

862 files changed

Lines changed: 229867 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/VOUCHED.td

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Vouched contributors for this project.
2+
#
3+
# See https://github.com/mitchellh/vouch for details.
4+
#
5+
# Syntax:
6+
# - One handle per line (without @), sorted alphabetically.
7+
# - Optional platform prefix: platform:username (e.g., github:user).
8+
# - Denounce with minus prefix: -username or -platform:username.
9+
# - Optional details after a space following the handle.
10+
adamdotdevin
11+
-florianleibert
12+
fwang
13+
iamdavidhill
14+
jayair
15+
kitlangton
16+
kommander
17+
r44vc0rp
18+
rekram1-node
19+
-spider-yamet clawdbot/llm psychosis, spam pinging the team
20+
thdxr
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: compliance-close
2+
3+
on:
4+
schedule:
5+
# Run every 30 minutes to check for expired compliance windows
6+
- cron: "*/30 * * * *"
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
issues: write
12+
pull-requests: write
13+
14+
jobs:
15+
close-non-compliant:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Close non-compliant issues and PRs after 2 hours
19+
uses: actions/github-script@v7
20+
with:
21+
script: |
22+
const { data: items } = await github.rest.issues.listForRepo({
23+
owner: context.repo.owner,
24+
repo: context.repo.repo,
25+
labels: 'needs:compliance',
26+
state: 'open',
27+
per_page: 100,
28+
});
29+
30+
if (items.length === 0) {
31+
core.info('No open issues/PRs with needs:compliance label');
32+
return;
33+
}
34+
35+
const now = Date.now();
36+
const twoHours = 2 * 60 * 60 * 1000;
37+
38+
for (const item of items) {
39+
const isPR = !!item.pull_request;
40+
const kind = isPR ? 'PR' : 'issue';
41+
42+
const { data: comments } = await github.rest.issues.listComments({
43+
owner: context.repo.owner,
44+
repo: context.repo.repo,
45+
issue_number: item.number,
46+
});
47+
48+
const complianceComment = comments.find(c => c.body.includes('<!-- issue-compliance -->'));
49+
if (!complianceComment) continue;
50+
51+
const commentAge = now - new Date(complianceComment.created_at).getTime();
52+
if (commentAge < twoHours) {
53+
core.info(`${kind} #${item.number} still within 2-hour window (${Math.round(commentAge / 60000)}m elapsed)`);
54+
continue;
55+
}
56+
57+
const closeMessage = isPR
58+
? 'This pull request has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new pull request that follows our guidelines.'
59+
: 'This issue has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new issue that follows our issue templates.';
60+
61+
await github.rest.issues.createComment({
62+
owner: context.repo.owner,
63+
repo: context.repo.repo,
64+
issue_number: item.number,
65+
body: closeMessage,
66+
});
67+
68+
if (isPR) {
69+
await github.rest.pulls.update({
70+
owner: context.repo.owner,
71+
repo: context.repo.repo,
72+
pull_number: item.number,
73+
state: 'closed',
74+
});
75+
} else {
76+
await github.rest.issues.update({
77+
owner: context.repo.owner,
78+
repo: context.repo.repo,
79+
issue_number: item.number,
80+
state: 'closed',
81+
state_reason: 'not_planned',
82+
});
83+
}
84+
85+
core.info(`Closed non-compliant ${kind} #${item.number} after 2-hour window`);
86+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: docs-locale-sync
2+
3+
on:
4+
push:
5+
branches:
6+
- dev
7+
paths:
8+
- packages/web/src/content/docs/*.mdx
9+
10+
jobs:
11+
sync-locales:
12+
if: github.actor != 'opencode-agent[bot]'
13+
runs-on: blacksmith-4vcpu-ubuntu-2404
14+
permissions:
15+
id-token: write
16+
contents: write
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Setup Bun
24+
uses: ./.github/actions/setup-bun
25+
26+
- name: Setup git committer
27+
id: committer
28+
uses: ./.github/actions/setup-git-committer
29+
with:
30+
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
31+
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
32+
33+
- name: Compute changed English docs
34+
id: changes
35+
run: |
36+
FILES=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- 'packages/web/src/content/docs/*.mdx' || true)
37+
if [ -z "$FILES" ]; then
38+
echo "has_changes=false" >> "$GITHUB_OUTPUT"
39+
echo "No English docs changed in push range"
40+
exit 0
41+
fi
42+
echo "has_changes=true" >> "$GITHUB_OUTPUT"
43+
{
44+
echo "files<<EOF"
45+
echo "$FILES"
46+
echo "EOF"
47+
} >> "$GITHUB_OUTPUT"
48+
49+
- name: Sync locale docs with OpenCode
50+
if: steps.changes.outputs.has_changes == 'true'
51+
uses: sst/opencode/github@latest
52+
env:
53+
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
54+
with:
55+
model: opencode/gpt-5.2
56+
agent: docs
57+
prompt: |
58+
Update localized docs to match the latest English docs changes.
59+
60+
Changed English doc files:
61+
<changed_english_docs>
62+
${{ steps.changes.outputs.files }}
63+
</changed_english_docs>
64+
65+
Requirements:
66+
1. Update all relevant locale docs under packages/web/src/content/docs/<locale>/ so they reflect these English page changes.
67+
2. You MUST use the Task tool for translation work and launch subagents with subagent_type `translator` (defined in .opencode/agent/translator.md).
68+
3. Do not translate directly in the primary agent. Use translator subagent output as the source for locale text updates.
69+
4. Run translator subagent Task calls in parallel whenever file/locale translation work is independent.
70+
5. Preserve frontmatter keys, internal links, code blocks, and existing locale-specific metadata unless the English change requires an update.
71+
6. Keep locale docs structure aligned with their corresponding English pages.
72+
7. Do not modify English source docs in packages/web/src/content/docs/*.mdx.
73+
8. If no locale updates are needed, make no changes.
74+
75+
- name: Commit and push locale docs updates
76+
if: steps.changes.outputs.has_changes == 'true'
77+
run: |
78+
if [ -z "$(git status --porcelain)" ]; then
79+
echo "No locale docs changes to commit"
80+
exit 0
81+
fi
82+
git add -A
83+
git commit -m "docs(i18n): sync locale docs from english changes"
84+
git pull --rebase --autostash origin "$GITHUB_REF_NAME"
85+
git push origin HEAD:"$GITHUB_REF_NAME"
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: vouch-check-issue
2+
3+
on:
4+
issues:
5+
types: [opened]
6+
7+
permissions:
8+
contents: read
9+
issues: write
10+
11+
jobs:
12+
check:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Check if issue author is denounced
16+
uses: actions/github-script@v7
17+
with:
18+
script: |
19+
const author = context.payload.issue.user.login;
20+
const issueNumber = context.payload.issue.number;
21+
22+
// Skip bots
23+
if (author.endsWith('[bot]')) {
24+
core.info(`Skipping bot: ${author}`);
25+
return;
26+
}
27+
28+
// Read the VOUCHED.td file via API (no checkout needed)
29+
let content;
30+
try {
31+
const response = await github.rest.repos.getContent({
32+
owner: context.repo.owner,
33+
repo: context.repo.repo,
34+
path: '.github/VOUCHED.td',
35+
});
36+
content = Buffer.from(response.data.content, 'base64').toString('utf-8');
37+
} catch (error) {
38+
if (error.status === 404) {
39+
core.info('No .github/VOUCHED.td file found, skipping check.');
40+
return;
41+
}
42+
throw error;
43+
}
44+
45+
// Parse the .td file for denounced users
46+
const denounced = new Map();
47+
for (const line of content.split('\n')) {
48+
const trimmed = line.trim();
49+
if (!trimmed || trimmed.startsWith('#')) continue;
50+
if (!trimmed.startsWith('-')) continue;
51+
52+
const rest = trimmed.slice(1).trim();
53+
if (!rest) continue;
54+
const spaceIdx = rest.indexOf(' ');
55+
const handle = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx);
56+
const reason = spaceIdx === -1 ? null : rest.slice(spaceIdx + 1).trim();
57+
58+
// Handle platform:username or bare username
59+
// Only match bare usernames or github: prefix (skip other platforms)
60+
const colonIdx = handle.indexOf(':');
61+
if (colonIdx !== -1) {
62+
const platform = handle.slice(0, colonIdx).toLowerCase();
63+
if (platform !== 'github') continue;
64+
}
65+
const username = colonIdx === -1 ? handle : handle.slice(colonIdx + 1);
66+
if (!username) continue;
67+
68+
denounced.set(username.toLowerCase(), reason);
69+
}
70+
71+
// Check if the author is denounced
72+
const reason = denounced.get(author.toLowerCase());
73+
if (reason === undefined) {
74+
core.info(`User ${author} is not denounced. Allowing issue.`);
75+
return;
76+
}
77+
78+
// Author is denounced — close the issue
79+
const body = 'This issue has been automatically closed.';
80+
81+
await github.rest.issues.createComment({
82+
owner: context.repo.owner,
83+
repo: context.repo.repo,
84+
issue_number: issueNumber,
85+
body,
86+
});
87+
88+
await github.rest.issues.update({
89+
owner: context.repo.owner,
90+
repo: context.repo.repo,
91+
issue_number: issueNumber,
92+
state: 'closed',
93+
state_reason: 'not_planned',
94+
});
95+
96+
core.info(`Closed issue #${issueNumber} from denounced user ${author}`);
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: vouch-check-pr
2+
3+
on:
4+
pull_request_target:
5+
types: [opened]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
11+
jobs:
12+
check:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Check if PR author is denounced
16+
uses: actions/github-script@v7
17+
with:
18+
script: |
19+
const author = context.payload.pull_request.user.login;
20+
const prNumber = context.payload.pull_request.number;
21+
22+
// Skip bots
23+
if (author.endsWith('[bot]')) {
24+
core.info(`Skipping bot: ${author}`);
25+
return;
26+
}
27+
28+
// Read the VOUCHED.td file via API (no checkout needed)
29+
let content;
30+
try {
31+
const response = await github.rest.repos.getContent({
32+
owner: context.repo.owner,
33+
repo: context.repo.repo,
34+
path: '.github/VOUCHED.td',
35+
});
36+
content = Buffer.from(response.data.content, 'base64').toString('utf-8');
37+
} catch (error) {
38+
if (error.status === 404) {
39+
core.info('No .github/VOUCHED.td file found, skipping check.');
40+
return;
41+
}
42+
throw error;
43+
}
44+
45+
// Parse the .td file for denounced users
46+
const denounced = new Map();
47+
for (const line of content.split('\n')) {
48+
const trimmed = line.trim();
49+
if (!trimmed || trimmed.startsWith('#')) continue;
50+
if (!trimmed.startsWith('-')) continue;
51+
52+
const rest = trimmed.slice(1).trim();
53+
if (!rest) continue;
54+
const spaceIdx = rest.indexOf(' ');
55+
const handle = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx);
56+
const reason = spaceIdx === -1 ? null : rest.slice(spaceIdx + 1).trim();
57+
58+
// Handle platform:username or bare username
59+
// Only match bare usernames or github: prefix (skip other platforms)
60+
const colonIdx = handle.indexOf(':');
61+
if (colonIdx !== -1) {
62+
const platform = handle.slice(0, colonIdx).toLowerCase();
63+
if (platform !== 'github') continue;
64+
}
65+
const username = colonIdx === -1 ? handle : handle.slice(colonIdx + 1);
66+
if (!username) continue;
67+
68+
denounced.set(username.toLowerCase(), reason);
69+
}
70+
71+
// Check if the author is denounced
72+
const reason = denounced.get(author.toLowerCase());
73+
if (reason === undefined) {
74+
core.info(`User ${author} is not denounced. Allowing PR.`);
75+
return;
76+
}
77+
78+
// Author is denounced — close the PR
79+
await github.rest.issues.createComment({
80+
owner: context.repo.owner,
81+
repo: context.repo.repo,
82+
issue_number: prNumber,
83+
body: 'This pull request has been automatically closed.',
84+
});
85+
86+
await github.rest.pulls.update({
87+
owner: context.repo.owner,
88+
repo: context.repo.repo,
89+
pull_number: prNumber,
90+
state: 'closed',
91+
});
92+
93+
core.info(`Closed PR #${prNumber} from denounced user ${author}`);

0 commit comments

Comments
 (0)