Skip to content

Commit 0f33982

Browse files
committed
feat(studio): skills at the top
1 parent cd682a1 commit 0f33982

8 files changed

Lines changed: 37 additions & 42 deletions

File tree

‎apps/studio/src/client/components/files-grid.tsx‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,16 @@ export function FilesGrid({
3636
const navigate = useNavigate({ from: "/projects/$subdomain" });
3737
const [isExpanded, setIsExpanded] = useState(false);
3838
const [isScriptsExpanded, setIsScriptsExpanded] = useState(false);
39+
const [isSkillsExpanded, setIsSkillsExpanded] = useState(false);
3940
const [isUserProvidedExpanded, setIsUserProvidedExpanded] = useState(false);
4041
const [isAgentRetrievedExpanded, setIsAgentRetrievedExpanded] =
4142
useState(false);
4243

4344
const [outputFiles, nonOutputFiles] = fork(files, isOutputFile);
4445
const [scriptFiles, nonScriptFiles] = fork(nonOutputFiles, isScriptFile);
46+
const [skillFiles, nonSkillFiles] = fork(nonScriptFiles, isSkillFile);
4547
const [userProvidedFiles, nonUserProvidedFiles] = fork(
46-
nonScriptFiles,
48+
nonSkillFiles,
4749
isUserProvidedFile,
4850
);
4951
const [agentRetrievedFiles, regularFiles] = fork(
@@ -81,6 +83,7 @@ export function FilesGrid({
8183
const hasMoreFiles =
8284
mainFiles.length > initialVisibleCount ||
8385
scriptFiles.length > 0 ||
86+
skillFiles.length > 0 ||
8487
collapsedUserProvidedFiles.length > 0 ||
8588
agentRetrievedFiles.length > 0;
8689

@@ -89,6 +92,7 @@ export function FilesGrid({
8992
const hiddenFileCount =
9093
expandedFiles.length +
9194
scriptFiles.length +
95+
skillFiles.length +
9296
collapsedUserProvidedFiles.length +
9397
agentRetrievedFiles.length;
9498

@@ -174,6 +178,7 @@ export function FilesGrid({
174178
collapsedUserProvidedFiles.length === 0
175179
) {
176180
setIsScriptsExpanded(true);
181+
setIsSkillsExpanded(true);
177182
setIsUserProvidedExpanded(true);
178183
setIsAgentRetrievedExpanded(true);
179184
}
@@ -208,6 +213,19 @@ export function FilesGrid({
208213
/>
209214
)}
210215

216+
{isExpanded && skillFiles.length > 0 && (
217+
<CategorizedFileSection
218+
alignEnd={alignEnd}
219+
files={skillFiles}
220+
isExpanded={isSkillsExpanded}
221+
onFileClick={handleFileClick}
222+
onToggle={() => {
223+
setIsSkillsExpanded(!isSkillsExpanded);
224+
}}
225+
title="Skills"
226+
/>
227+
)}
228+
211229
{isExpanded && collapsedUserProvidedFiles.length > 0 && (
212230
<CategorizedFileSection
213231
alignEnd={alignEnd}
@@ -240,6 +258,7 @@ export function FilesGrid({
240258
onClick={() => {
241259
setIsExpanded(false);
242260
setIsScriptsExpanded(false);
261+
setIsSkillsExpanded(false);
243262
setIsUserProvidedExpanded(false);
244263
setIsAgentRetrievedExpanded(false);
245264
}}
@@ -338,6 +357,10 @@ function isScriptFile(file: ProjectFileViewerFile) {
338357
return file.filePath.startsWith(`${APP_FOLDER_NAMES.scripts}/`);
339358
}
340359

360+
function isSkillFile(file: ProjectFileViewerFile) {
361+
return file.filePath.startsWith(`${APP_FOLDER_NAMES.skills}/`);
362+
}
363+
341364
function isUserProvidedFile(file: ProjectFileViewerFile) {
342365
return file.filePath.startsWith(`${APP_FOLDER_NAMES.userProvided}/`);
343366
}

‎apps/studio/src/client/lib/project-file-groups.ts‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,7 @@ const ROOT_SOURCE_EXTENSIONS = [
4040
".tsx",
4141
];
4242

43-
const FILTERED_DIRECTORY_PREFIXES = [".agents/"];
44-
4543
export function shouldFilterProjectFile(filePath: string): boolean {
46-
if (isFilteredFolder(filePath)) {
47-
return true;
48-
}
4944
const baseName = filenameFromFilePath(filePath).toLowerCase();
5045
if (
5146
FILTERED_FILENAMES.some((filtered) => baseName === filtered.toLowerCase())
@@ -58,10 +53,3 @@ export function shouldFilterProjectFile(filePath: string): boolean {
5853
}
5954
return false;
6055
}
61-
62-
function isFilteredFolder(filePath: string): boolean {
63-
const normalized = filePath.startsWith("./") ? filePath.slice(2) : filePath;
64-
return FILTERED_DIRECTORY_PREFIXES.some((prefix) =>
65-
normalized.startsWith(prefix),
66-
);
67-
}

‎apps/studio/src/client/lib/tool-display.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ const TOOL_STREAMING_DISPLAY_NAMES_WITH_VALUE: Record<
8181
generate_image: "Generating",
8282
glob: "Searching for",
8383
grep: "Searching for",
84-
load_skill: "Loading",
84+
load_skill: "Loading skill",
8585
read_file: "Reading",
8686
run_diagnostics: TOOL_STREAMING_DISPLAY_NAMES.run_diagnostics,
8787
run_shell_command: TOOL_STREAMING_DISPLAY_NAMES.run_shell_command,

‎packages/workspace/src/agents/main.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,12 @@ export const mainAgent = setupAgent({
189189
- No other runtimes are bundled with this product.
190190
- You can use the \`${agentTools.RunDiagnostics.name}\` tool to check for errors in your scripts.
191191
- You don't need to add shebangs to TypeScript script files.
192-
- Before running scripts, add dependencies with \`${PNPM_COMMAND.name}\`. No \`cd\` is available -- to target a skill folder use \`${PNPM_COMMAND.name} add <package> --filter ./${APP_FOLDER_NAMES.agents}/${APP_FOLDER_NAMES.agentsSkills}/<skill-name>\`.
192+
- Before running scripts, add dependencies with \`${PNPM_COMMAND.name}\`. No \`cd\` is available -- to target a skill folder use \`${PNPM_COMMAND.name} add <package> --filter ./${APP_FOLDER_NAMES.skills}/<skill-name>\`.
193193
194194
## Where to place scripts
195195
Each skill folder has its own \`package.json\` with dependencies already installed. The skill files are yours to edit freely -- treat them as a starting point, not read-only templates.
196196
197-
- **Skill folder** (\`${APP_FOLDER_NAMES.agents}/${APP_FOLDER_NAMES.agentsSkills}/<skill-name>/scripts/\`): Default whenever any skill is involved. New scripts placed here get the skill's dependencies for free with no extra setup.
197+
- **Skill folder** (\`${APP_FOLDER_NAMES.skills}/<skill-name>/scripts/\`): Default whenever any skill is involved. New scripts placed here get the skill's dependencies for free with no extra setup.
198198
- **Project scripts** (\`${APP_FOLDER_NAMES.scripts}/\`): Only when no skills are involved, or when combining multiple skills requires deps that span more than one skill folder. Install any needed deps at the project root first.
199199
200200
# Output Files

‎packages/workspace/src/constants.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ export const REGISTRY_FOLDER_NAMES = {
55

66
export const APP_FOLDER_NAMES = {
77
agentRetrieved: "agent-retrieved",
8-
agents: ".agents",
9-
agentsSkills: "skills",
108
output: "output",
119
private: ".quests",
1210
scripts: "scripts",
11+
skills: "skills",
1312
src: "src",
1413
userProvided: "user-provided",
1514
} as const;

‎packages/workspace/src/lib/copy-skill.ts‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,13 @@ export async function copySkill({
1919
skillDir: AbsolutePath;
2020
skillName: string;
2121
}): Promise<Result<AbsolutePath, TypedError.Conflict>> {
22-
const destDir = absolutePathJoin(
23-
appDir,
24-
APP_FOLDER_NAMES.agents,
25-
APP_FOLDER_NAMES.agentsSkills,
26-
skillName,
27-
);
22+
const destDir = absolutePathJoin(appDir, APP_FOLDER_NAMES.skills, skillName);
2823

2924
try {
3025
await fs.access(destDir);
3126
return err(
3227
new TypedError.Conflict(
33-
`Skill "${skillName}" is already loaded at ${path.join(APP_FOLDER_NAMES.agents, APP_FOLDER_NAMES.agentsSkills, skillName)}.`,
28+
`Skill "${skillName}" is already loaded at ${path.join(APP_FOLDER_NAMES.skills, skillName)}.`,
3429
),
3530
);
3631
} catch (error) {

‎packages/workspace/src/tools/load-skill.test.ts‎

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,7 @@ describe("LoadSkill", () => {
110110
input: { explanation: "loading", name: "my-skill" },
111111
});
112112

113-
const destBase = path.join(
114-
appDir,
115-
APP_FOLDER_NAMES.agents,
116-
APP_FOLDER_NAMES.agentsSkills,
117-
"my-skill",
118-
);
113+
const destBase = path.join(appDir, APP_FOLDER_NAMES.skills, "my-skill");
119114
const md = await fs.readFile(path.join(destBase, "SKILL.md"), "utf8");
120115
expect(md).toMatchInlineSnapshot(`
121116
"---
@@ -161,9 +156,9 @@ describe("LoadSkill", () => {
161156
The skill files below are copied into your project and are yours to edit. Before writing anything new, read the relevant script(s) with read_file and run them with \`ts\` if they fit. Only write a custom script if the existing ones cannot handle the task even with modification.
162157
163158
<skill_files>
164-
<file>.agents/skills/my-skill/references/notes.md</file>
165-
<file>.agents/skills/my-skill/scripts/lib/helper.ts</file>
166-
<file>.agents/skills/my-skill/scripts/run.ts</file>
159+
<file>skills/my-skill/references/notes.md</file>
160+
<file>skills/my-skill/scripts/lib/helper.ts</file>
161+
<file>skills/my-skill/scripts/run.ts</file>
167162
</skill_files>
168163
</skill_content>"
169164
`);
@@ -184,8 +179,7 @@ describe("LoadSkill", () => {
184179

185180
const destScript = path.join(
186181
appDir,
187-
APP_FOLDER_NAMES.agents,
188-
APP_FOLDER_NAMES.agentsSkills,
182+
APP_FOLDER_NAMES.skills,
189183
"my-skill",
190184
"scripts",
191185
"run.ts",
@@ -198,7 +192,7 @@ describe("LoadSkill", () => {
198192
const fileContent = await fs.readFile(destScript, "utf8");
199193
expect(fileContent).toMatchInlineSnapshot(`"modified"`);
200194
expect(result.content).toMatchInlineSnapshot(
201-
`"Skill "my-skill" is already loaded at .agents/skills/my-skill."`,
195+
`"Skill "my-skill" is already loaded at skills/my-skill."`,
202196
);
203197
});
204198

‎packages/workspace/src/tools/load-skill.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,7 @@ export const LoadSkill = setupTool({
119119
}
120120

121121
const destDir = copyResult.value;
122-
const relativeSkillRoot = path.join(
123-
APP_FOLDER_NAMES.agents,
124-
APP_FOLDER_NAMES.agentsSkills,
125-
skill.name,
126-
);
122+
const relativeSkillRoot = path.join(APP_FOLDER_NAMES.skills, skill.name);
127123
const { files: copiedFiles, truncated } = await listSkillFiles(
128124
destDir,
129125
signal,

0 commit comments

Comments
 (0)