Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ macOS note: the vendored binary is unsigned — if Gatekeeper blocks it:
`harmoniqs/opencode` (thin fork, patch stack in its `AMICODE-PATCHES.md`). Rebrand/UI work
happens THERE, product logic lives HERE in config/plugin/scores (Layer 0). To change the
fork, see "Changing opencode (the vendored fork)" below.
- **`packages/app-bundle/overlay/` is a read-only tracking copy** — never edit the overlay
directly. It is copied FROM the opencode fork, never TO it, and is not a build input. To
change any app-layer file (components, pages, styles), edit the fork
(`~/harmoniqs/opencode` or `$AMICODE_OPENCODE_SRC`), rebuild with
`pnpm --filter amicode opencode:build`, then `sync:apply`. Edits to the overlay
silently vanish on the next sync.
- `packages/extension/opencode-plugin/` executes inside opencode's Bun runtime — it is NOT
part of the extension bundle; keep it dependency-free; exactly one export.
- `packages/extension/scores/` — interview flows as data. New user path = new `SCORE.md`
Expand Down Expand Up @@ -123,6 +129,20 @@ built with `OPENCODE_CHANNEL=latest` (→ `"prod"`) compiles the features in but
`opencode:build` and the release workflow both force `dev`; `scripts/assert_ui_gate.sh` fails
CI and release if a binary ever ships with the gate off.

**Overlay sync:** `packages/app-bundle/overlay/` is a **tracking copy** of the fork's diff
against upstream — it is NOT a build input (the binary builds from the fork directly). The fork
is the source of truth for all app source files. **Never edit the overlay directly; never copy
overlay → fork** for files that exist in both; that overwrites newer fork code with stale
overlay snapshots.

After editing the fork:
```bash
pnpm --filter @amicode/app-bundle sync:check # detect overlay ↔ fork drift
pnpm --filter @amicode/app-bundle sync:apply # fix: copy fork → overlay + update hashes
```
`opencode:build` warns on drift but does not block. The `sync:apply` command is one-way
(fork → overlay) and updates `manifest.json` hashes automatically.

## Releasing & publishing (amicode → Marketplace)

Two channels. **`vX.Y.Z-alpha.N`** is internal — the `alpha.N` mirrors the vendored fork's
Expand Down
126 changes: 66 additions & 60 deletions packages/app-bundle/manifest.json

Large diffs are not rendered by default.

17 changes: 3 additions & 14 deletions packages/app-bundle/overlay/packages/app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,7 @@ function ResolvedDraftRoute(props: { draft: DraftTab }) {

function UiI18nBridge(props: ParentProps) {
const language = useLanguage()
return (
<I18nProvider
value={{ locale: language.intl, layoutLocale: language.layoutLocale, t: language.t, plural: language.plural }}
>
{props.children}
</I18nProvider>
)
return <I18nProvider value={{ locale: language.intl, t: language.t }}>{props.children}</I18nProvider>
}

function LayoutCompatibility(props: ParentProps) {
Expand Down Expand Up @@ -506,12 +500,7 @@ function AmicodeNavigateBridge() {
return null
}

export function AppBaseProviders(
props: ParentProps<{
locale?: Locale
onNativeTranslations?: Parameters<typeof LanguageProvider>[0]["onNativeTranslations"]
}>,
) {
export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
return (
<MetaProvider>
<Font />
Expand All @@ -522,7 +511,7 @@ export function AppBaseProviders(
}}
>
<AmicodeThemeBridge />
<LanguageProvider locale={props.locale} onNativeTranslations={props.onNativeTranslations}>
<LanguageProvider locale={props.locale}>
<UiI18nBridge>
<ErrorBoundary
fallback={(error) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never),
}),
)
const designPlaceholder = () =>
promptDesignPlaceholder(mode(), placeholder(), (key, params) =>
language.t(key as Parameters<typeof language.t>[0], params as never),
)
const designPlaceholder = () => promptDesignPlaceholder(mode(), placeholder())

const historyComments = () => {
const byID = new Map(comments.all().map((item) => [`${item.file}\n${item.id}`, item] as const))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1491,7 +1491,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}
onRemove={removeAttachment}
removeLabel={language.t("prompt.attachment.remove")}
fileLabel={language.t("ui.common.file")}
newLayoutDesigns={false}
/>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ type PromptImageAttachmentsProps = {
onOpen: (attachment: ImageAttachmentPart) => void
onRemove: (id: string) => void
removeLabel: string
fileLabel: string
newLayoutDesigns: boolean
comments?: PromptCommentItem[]
commentActive?: (item: PromptCommentItem) => boolean
Expand Down Expand Up @@ -95,7 +94,7 @@ export const PromptImageAttachments: Component<PromptImageAttachmentsProps> = (p
}
>
<AttachmentCardV2 title={attachment.filename}>
{typeLabel(attachment.filename, attachment.mime, props.fileLabel)}
{typeLabel(attachment.filename, attachment.mime)}
</AttachmentCardV2>
</Show>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { useCommand } from "@/context/command"
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry } from "@/desktop-menu"
import { usePlatform } from "@/context/platform"
import { useLanguage } from "@/context/language"

export function WindowsAppMenu(props: {
command: ReturnType<typeof useCommand>
platform: ReturnType<typeof usePlatform>
variant?: "legacy" | "v2"
}) {
let lastFocused: HTMLElement | undefined
const language = useLanguage()

const rememberFocus = () => {
const active = document.activeElement
Expand Down Expand Up @@ -60,7 +58,7 @@ export function WindowsAppMenu(props: {
variant="ghost-muted"
size="large"
icon={<IconV2 name="menu" />}
aria-label={language.t("desktop.menu.ariaLabel")}
aria-label="OpenCode menu"
onPointerDown={rememberFocus}
onKeyDown={rememberFocus}
/>
Expand All @@ -71,7 +69,7 @@ export function WindowsAppMenu(props: {
icon="menu"
variant="ghost"
class="titlebar-icon rounded-md shrink-0"
aria-label={language.t("desktop.menu.ariaLabel")}
aria-label="OpenCode menu"
onPointerDown={rememberFocus}
onKeyDown={rememberFocus}
/>
Expand All @@ -81,15 +79,15 @@ export function WindowsAppMenu(props: {
<DropdownMenu.Group>
<DropdownMenu.GroupLabel class="desktop-app-menu-heading">OpenCode</DropdownMenu.GroupLabel>
{DESKTOP_MENU.filter((menu) => desktopMenuVisible(menu, "windows")).map((menu) => (
<DesktopMenuSubmenu label={language.t(menu.labelKey)}>
<DesktopMenuSubmenu label={menu.label}>
{menu.items
?.filter((entry) => desktopMenuVisible(entry, "windows"))
.map((entry) =>
entry.type === "separator" ? (
<DropdownMenu.Separator />
) : (
<DesktopMenuItem
label={entry.labelKey ? language.t(entry.labelKey) : ""}
label={entry.label ?? ""}
keybind={entry.command ? props.command.keybind(entry.command) : entry.accelerator?.windows}
disabled={entry.command ? commandDisabled(entry.command) : false}
onSelect={() => runEntry(entry)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ export function dropSessionCaches(store: SessionCache, sessionIDs: Iterable<stri
delete store.todo[sessionID]
delete store.session_message[sessionID]
delete store.session_diff[sessionID]
// amicode: diff_version is a fork-side field — upstream-shaped stores
// (upstream tests/fixtures) do not carry it, so guard the eviction.
if (store.diff_version) delete store.diff_version[sessionID]
delete store.diff_version[sessionID]
delete store.session_status[sessionID]
delete store.permission[sessionID]
delete store.question[sessionID]
Expand Down
Loading
Loading