Skip to content

Commit 9c86bb7

Browse files
fix: update settings and skills handling with assistant redirect
1 parent 6d12eb4 commit 9c86bb7

7 files changed

Lines changed: 344 additions & 30 deletions

File tree

backend/src/routes/settings.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,8 +1126,9 @@ export function createSettingsRoutes(db: Database, gitAuthService: GitAuthServic
11261126
if (repoId !== undefined && isNaN(repoId)) {
11271127
return c.json({ error: 'Invalid repoId' }, 400)
11281128
}
1129+
const directory = c.req.query('directory')
11291130

1130-
const skills = await listManagedSkills(db, openCodeClient, repoId)
1131+
const skills = await listManagedSkills(db, openCodeClient, repoId, directory)
11311132
return c.json(skills)
11321133
} catch (error) {
11331134
logger.error('Failed to list skills:', error)

backend/src/services/skills.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,17 @@ function classifySkillLocation(
128128
location: string,
129129
globalPrefix: string,
130130
repos: Repo[],
131+
customDirectory?: string,
131132
): { scope: SkillScope; repo?: Repo } | null {
132133
if (location.startsWith(globalPrefix + path.sep)) {
133134
return { scope: 'global' }
134135
}
136+
if (customDirectory) {
137+
const projectPrefix = path.join(customDirectory, '.opencode', 'skills')
138+
if (location.startsWith(projectPrefix + path.sep)) {
139+
return { scope: 'project' }
140+
}
141+
}
135142
for (const repo of repos) {
136143
const projectPrefix = getProjectSkillsPath(repo)
137144
if (location.startsWith(projectPrefix + path.sep)) {
@@ -160,34 +167,46 @@ export async function listManagedSkills(
160167
db: Database,
161168
openCodeClient: OpenCodeClient,
162169
repoId?: number,
170+
directory?: string,
163171
): Promise<SkillFileInfo[]> {
164172
const globalPrefix = getGlobalSkillsPath()
165173
const allRepos = listRepos(db)
166174

167-
const targetRepos = repoId
168-
? allRepos.filter(r => r.id === repoId)
169-
: allRepos
170-
171-
if (repoId && targetRepos.length === 0) {
172-
throw new Error(`Repository with id ${repoId} not found`)
173-
}
174-
175-
const directories = targetRepos.length > 0
176-
? targetRepos.map(r => r.fullPath)
177-
: [getWorkspacePath()]
178-
179175
const seenLocations = new Set<string>()
180176
const result: SkillFileInfo[] = []
181177

182-
for (const directory of directories) {
178+
if (directory) {
183179
const skills = await fetchOpenCodeSkills(openCodeClient, directory)
184180
for (const skill of skills) {
185181
if (seenLocations.has(skill.location)) continue
186-
const classification = classifySkillLocation(skill.location, globalPrefix, allRepos)
182+
const classification = classifySkillLocation(skill.location, globalPrefix, allRepos, directory)
187183
if (!classification) continue
188184
seenLocations.add(skill.location)
189185
result.push(toSkillFileInfo(skill, classification))
190186
}
187+
} else {
188+
const targetRepos = repoId
189+
? allRepos.filter(r => r.id === repoId)
190+
: allRepos
191+
192+
if (repoId && targetRepos.length === 0) {
193+
throw new Error(`Repository with id ${repoId} not found`)
194+
}
195+
196+
const directories = targetRepos.length > 0
197+
? targetRepos.map(r => r.fullPath)
198+
: [getWorkspacePath()]
199+
200+
for (const dir of directories) {
201+
const skills = await fetchOpenCodeSkills(openCodeClient, dir)
202+
for (const skill of skills) {
203+
if (seenLocations.has(skill.location)) continue
204+
const classification = classifySkillLocation(skill.location, globalPrefix, allRepos)
205+
if (!classification) continue
206+
seenLocations.add(skill.location)
207+
result.push(toSkillFileInfo(skill, classification))
208+
}
209+
}
191210
}
192211

193212
return result

frontend/src/api/settings.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,12 @@ export const settingsApi = {
229229
return fetchWrapper(`${API_BASE_URL}/api/health/version`)
230230
},
231231

232-
listManagedSkills: async (repoId?: number): Promise<SkillFileInfo[]> => {
233-
const params = repoId ? `?repoId=${repoId}` : ''
234-
return fetchWrapper(`${API_BASE_URL}/api/settings/skills${params}`)
232+
listManagedSkills: async (repoId?: number, directory?: string): Promise<SkillFileInfo[]> => {
233+
const searchParams = new URLSearchParams()
234+
if (repoId) searchParams.set('repoId', String(repoId))
235+
if (directory) searchParams.set('directory', directory)
236+
const query = searchParams.toString() ? `?${searchParams.toString()}` : ''
237+
return fetchWrapper(`${API_BASE_URL}/api/settings/skills${query}`)
235238
},
236239

237240
getSkill: async (name: string, scope: SkillScope, repoId?: number): Promise<SkillFileInfo> => {

frontend/src/components/repo/RepoSkillsDialog.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type RepoSkillsDialogBaseProps = {
1414
}
1515

1616
type RepoSkillsDialogProps = RepoSkillsDialogBaseProps & (
17-
| { sessionId: string; opcodeUrl: string; directory: string; onSkillLoaded?: (skill: SkillFileInfo) => void }
17+
| { sessionId: string; opcodeUrl: string; directory?: string; onSkillLoaded?: (skill: SkillFileInfo) => void }
1818
| { sessionId?: undefined; opcodeUrl?: undefined; directory?: undefined; onSkillLoaded?: undefined }
1919
)
2020

@@ -28,9 +28,9 @@ export function RepoSkillsDialog({
2828
onSkillLoaded,
2929
}: RepoSkillsDialogProps) {
3030
const { isLoading, data, error } = useQuery({
31-
queryKey: ['settings', 'skills', repoId],
32-
queryFn: () => settingsApi.listManagedSkills(repoId),
33-
enabled: open && !!repoId,
31+
queryKey: directory ? ['settings', 'skills', 'directory', directory] : ['settings', 'skills', repoId],
32+
queryFn: () => settingsApi.listManagedSkills(repoId, directory),
33+
enabled: open && (!!repoId || !!directory),
3434
staleTime: 30000,
3535
})
3636

@@ -64,7 +64,7 @@ export function RepoSkillsDialog({
6464
onOpenChange(false)
6565
}
6666

67-
if (!repoId) {
67+
if (!repoId && !sessionId) {
6868
return null
6969
}
7070

frontend/src/pages/AssistantRedirect.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,22 @@ export function AssistantRedirect() {
145145
<>
146146
<FileBrowserSheet isOpen={fileBrowserOpen} onClose={() => setFileBrowserOpen(false)} basePath={assistantFileBasePath} repoName="Assistant" repoId={repoId} />
147147
<RepoMcpDialog open={mcpDialogOpen} onOpenChange={setMcpDialogOpen} directory={assistantDirectory} />
148-
<RepoSkillsDialog open={skillsDialogOpen} onOpenChange={setSkillsDialogOpen} repoId={repoId} />
148+
{assistantDirectory && opcodeUrl ? (
149+
<RepoSkillsDialog
150+
open={skillsDialogOpen}
151+
onOpenChange={setSkillsDialogOpen}
152+
repoId={repoId}
153+
sessionId="assistant-session"
154+
opcodeUrl={opcodeUrl}
155+
directory={assistantDirectory}
156+
/>
157+
) : (
158+
<RepoSkillsDialog
159+
open={skillsDialogOpen}
160+
onOpenChange={setSkillsDialogOpen}
161+
repoId={repoId}
162+
/>
163+
)}
149164
<SourceControlPanel repoId={repoId} isOpen={sourceControlOpen} onClose={() => setSourceControlOpen(false)} currentBranch={repo?.currentBranch || repo?.branch || "main"} repoName="Assistant" />
150165
<ResetPermissionsDialog open={resetPermissionsOpen} onOpenChange={setResetPermissionsOpen} repoId={repoId} repoDirectory={assistantDirectory} />
151166
</>

frontend/src/pages/SessionDetail.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,14 @@ export function SessionDetail() {
118118
const { data: assistantMode, isLoading: assistantModeLoading } = useQuery({
119119
queryKey: ["repo", repoId, "assistant-mode"],
120120
queryFn: () => initializeAssistantMode(repoId),
121-
enabled: isAssistantSession && !!repoId,
121+
enabled: isAssistantSession,
122122
});
123123

124124
useRepoActivity(repoId, Boolean(repo));
125125

126126
const opcodeUrl = OPENCODE_API_ENDPOINT;
127127

128-
const repoDirectory = isAssistantSession ? assistantMode?.directory : repo?.fullPath;
128+
const repoDirectory = isAssistantSession ? (assistantMode?.directory || repo?.fullPath) : repo?.fullPath;
129129
const sessionRouteSuffix = isAssistantSession ? '?assistant=1' : '';
130130

131131
const { isConnected, isReconnecting } = useSSE(opcodeUrl, repoDirectory, sessionId);
@@ -438,7 +438,7 @@ export function SessionDetail() {
438438
return <Navigate to="/" replace />;
439439
}
440440

441-
if (!repo) {
441+
if (!repo && !isAssistantSession) {
442442
return (
443443
<div className="flex items-center justify-center min-h-screen bg-gradient-to-br from-background via-background to-background">
444444
<div className="flex flex-col items-center gap-2">
@@ -449,7 +449,9 @@ export function SessionDetail() {
449449
);
450450
}
451451

452-
const workspaceDisplayName = isAssistantSession ? 'Assistant' : getRepoDisplayName(repo.repoUrl, repo.localPath, repo.sourcePath);
452+
const workspaceDisplayName = isAssistantSession || !repo
453+
? 'Assistant'
454+
: getRepoDisplayName(repo.repoUrl, repo.localPath, repo.sourcePath);
453455
const sessionBackPath = getSessionListPath(repoId, isAssistantSession);
454456

455457
return (
@@ -644,7 +646,7 @@ export function SessionDetail() {
644646
directory={repoDirectory}
645647
/>
646648

647-
{repoDirectory && opcodeUrl && sessionId && (
649+
{opcodeUrl && sessionId && (
648650
<RepoSkillsDialog
649651
open={skillsDialogOpen}
650652
onOpenChange={setSkillsDialogOpen}
@@ -666,7 +668,7 @@ export function SessionDetail() {
666668
repoId={repoId}
667669
isOpen={sourceControlOpen}
668670
onClose={() => setSourceControlOpen(false)}
669-
currentBranch={repo.currentBranch || repo.branch || "main"}
671+
currentBranch={repo?.currentBranch || repo?.branch || "main"}
670672
repoName={workspaceDisplayName}
671673
/>
672674

0 commit comments

Comments
 (0)