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
2 changes: 1 addition & 1 deletion src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ function registerWindowAndDialogHandlers(): void {
})

// Confirm reloading the canvas after the workspace.json file changed on disk
// (edited externally, e.g. by an agent following the .cate skill).
// (edited externally while Cate was running).
ipcMain.handle(DIALOG_CONFIRM_RELOAD_WORKSPACE, async (event, payload: { name?: string }) => {
const win = BrowserWindow.fromWebContents(event.sender) ?? undefined
const name = payload?.name?.trim()
Expand Down
32 changes: 5 additions & 27 deletions src/main/projectWorkspaceStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ function sessionPath(rootPath: string): string {
// ---------------------------------------------------------------------------
// External-edit guard for workspace.json
//
// workspace.json is committable and meant to be hand- or agent-edited (e.g. via
// the .cate skill) to reconfigure the canvas. But the renderer also autosaves
// the live layout back over it (~30s + on quit), which would clobber any edit
// made while the app is running. To prevent that, we remember the hash of the
// content we last wrote/read per project; before any autosave overwrite we
// compare it against what's on disk. A mismatch means the file was edited
// workspace.json is committable and may be edited on disk (by hand or another
// tool) while Cate is running. But the renderer also autosaves the live layout
// back over it (~30s + on quit), which would clobber any such edit. To prevent
// that, we remember the hash of the content we last wrote/read per project;
// before any autosave overwrite we compare it against what's on disk. A mismatch means the file was edited
// behind our back, so we skip the overwrite and preserve the edit until the
// user reloads the workspace from disk.
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -364,26 +363,6 @@ function snapshotToSessionFile(snapshot: SessionSnapshot): ProjectSessionFile {
}
}

const SKILL_FILE = 'skill.md'
const seededSkillRoots = new Set<string>()

async function seedSkillFile(rootPath: string): Promise<void> {
if (seededSkillRoots.has(rootPath)) return
seededSkillRoots.add(rootPath)
const skillPath = path.join(rootPath, CATE_DIR, SKILL_FILE)
try {
await fs.access(skillPath)
} catch {
try {
const { SKILL_TEMPLATE } = await import('./templates/skillTemplate')
await fs.writeFile(skillPath, SKILL_TEMPLATE, 'utf-8')
log.debug('Seeded skill.md in %s', cateDir(rootPath))
} catch (err) {
log.warn('Failed to seed skill.md: %O', err)
}
}
}

// Serialize saves per root. Overlapping saves for the same project would race
// on disk and, worse, desync the remembered-hash guard (one write finishes
// last on disk while another finishes last in memory), spuriously flagging
Expand Down Expand Up @@ -420,7 +399,6 @@ export function registerProjectStateHandlers(): void {
writes.push(atomicWrite(workspacePath(rootPath), wsJson).then(() => rememberWorkspaceContent(rootPath, wsJson)))
}
await Promise.all(writes)
seedSkillFile(rootPath).catch(() => {})
log.debug('Project state saved to %s', cateDir(rootPath))
})
},
Expand Down
201 changes: 0 additions & 201 deletions src/main/templates/skillTemplate.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ function MainApp() {
}, [currentWorkspace?.name])

// When the active workspace's workspace.json is detected to have changed on
// disk (edited externally, e.g. by an agent following the .cate skill), prompt
// to reload the canvas. The detector (main's autosave guard) fires once per
// change via WORKSPACE_EXTERNAL_EDIT.
// disk (edited externally while Cate was running), prompt to reload the
// canvas. The detector (main's autosave guard) fires once per change via
// WORKSPACE_EXTERNAL_EDIT.
useEffect(() => {
return window.electronAPI.onWorkspaceExternalEdit?.(async ({ rootPath }) => {
if (reloadPromptOpenRef.current) return
Expand Down
5 changes: 2 additions & 3 deletions src/renderer/lib/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,9 +505,8 @@ async function loadFromProjectFiles(): Promise<MultiWorkspaceSession | null> {
/**
* Re-read the active workspace's .cate/workspace.json from disk and rebuild the
* canvas from it, discarding the current in-memory layout. This is how an
* external edit (e.g. an agent following the .cate skill) is applied without
* quitting the app — the autosave guard in main keeps the edit from being
* clobbered until this runs.
* external edit to the file is applied without quitting the app — the autosave
* guard in main keeps the edit from being clobbered until this runs.
*
* Tears down current panels (disposing terminals) then replays the on-disk
* snapshot through the same restore path used at launch.
Expand Down
Loading