The 4-step wizard is now fully live — nothing faked.
Backend (core + main):
- GiteaClient.createLabel (POST /labels).
- core/gitea/bootstrap.ts: SCHEMA_LABELS (the full est/p/deadline vocabulary with
colors+descriptions), applySchemaLabels (idempotent — creates only missing),
repoExists + ensurePmStateRepo (token-scoped, org vs personal endpoint). 7 tests.
- config:bootstrap IPC handler + preload bridge + BootstrapResult type.
Wizard:
- Connect step → live config.discover ('connected · N repos visible' is real).
- Repo step → owner dropdown + repo radios from the discovered repos.
- Bootstrap step → real: applies the label schema + ensures the pm-state sidecar,
shows the actual outcome (created vs already-present), then saves config.
- Gate: first run (no config) → wizard; reconnect (existing) → the compact card.
Removed the dead 'firstrun' dev view.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
3.1 KiB
TypeScript
60 lines
3.1 KiB
TypeScript
import { contextBridge, ipcRenderer } from 'electron'
|
|
|
|
const api = {
|
|
platform: process.platform,
|
|
gitea: {
|
|
/** Whether the main process has a gitea token + target repo configured. */
|
|
status: () => ipcRenderer.invoke('gitea:status'),
|
|
/** The last persisted snapshot, for instant boot before the fresh reconcile. */
|
|
boot: () => ipcRenderer.invoke('gitea:boot'),
|
|
/** Full read of the managed repo — every issue + milestone. */
|
|
reconcile: () => ipcRenderer.invoke('gitea:reconcile'),
|
|
/** One issue by index, normalized (or null if unconfigured). */
|
|
getIssue: (index: number) => ipcRenderer.invoke('gitea:getIssue', index),
|
|
/** Apply an estimate/priority change (the write path); resolves to the plan + fresh issue. */
|
|
applyChange: (change: unknown) => ipcRenderer.invoke('gitea:applyChange', change),
|
|
/** File a set of captured issues with their est/* + p/* labels. */
|
|
createIssues: (issues: unknown) => ipcRenderer.invoke('gitea:createIssues', issues),
|
|
/** Assignable people (repo collaborators) for the assignee picker. */
|
|
collaborators: () => ipcRenderer.invoke('gitea:collaborators'),
|
|
},
|
|
pmstate: {
|
|
/** Read the directive ledger from the pm-state repo. */
|
|
directives: () => ipcRenderer.invoke('pmstate:directives'),
|
|
/** Read the capacity config from the pm-state repo. */
|
|
capacity: () => ipcRenderer.invoke('pmstate:capacity'),
|
|
},
|
|
config: {
|
|
/** The saved config (never the token) — null when unset. */
|
|
get: () => ipcRenderer.invoke('config:get'),
|
|
/** Discover the owners + repos a token can reach (for the onboarding pickers). */
|
|
discover: (conn: unknown) => ipcRenderer.invoke('config:discover', conn),
|
|
/** Apply the label schema + ensure the pm-state repo (first-run bootstrap). */
|
|
bootstrap: (req: unknown) => ipcRenderer.invoke('config:bootstrap', req),
|
|
/** Validate a token + repo before saving. */
|
|
test: (cfg: unknown) => ipcRenderer.invoke('config:test', cfg),
|
|
/** Save config (token encrypted in main); takes effect without restart. */
|
|
set: (cfg: unknown) => ipcRenderer.invoke('config:set', cfg),
|
|
/** Forget the saved config. */
|
|
clear: () => ipcRenderer.invoke('config:clear'),
|
|
},
|
|
model: {
|
|
/** Whether a model endpoint is configured (else the UI keeps the scripted Reginald). */
|
|
status: () => ipcRenderer.invoke('model:status'),
|
|
/** One agent turn: messages in, Reginald's prose + the tools it consulted out. */
|
|
chat: (messages: unknown) => ipcRenderer.invoke('model:chat', messages),
|
|
/** Subscribe to streamed prose tokens for the in-flight turn; returns an unsubscribe. */
|
|
onToken: (cb: (delta: string) => void) => {
|
|
const listener = (_e: unknown, delta: string) => cb(delta)
|
|
ipcRenderer.on('model:chat:token', listener)
|
|
return () => ipcRenderer.removeListener('model:chat:token', listener)
|
|
},
|
|
/** Decompose a braindump into a proposed issue set (capture_work). */
|
|
capture: (braindump: string) => ipcRenderer.invoke('model:capture', braindump),
|
|
},
|
|
}
|
|
|
|
export type CommiteaApi = typeof api
|
|
|
|
contextBridge.exposeInMainWorld('commitea', api)
|