-
Notifications
You must be signed in to change notification settings - Fork 319
Jminnetian/askskills #1410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Jminnetian/askskills #1410
Changes from all commits
e6b20a9
6720a87
67d0afb
a36ae18
7df801a
54d7750
13e9e03
199d2e9
7e27ead
15f5546
0893716
6e87376
bcbd0bf
0bc199a
2e3df3e
71861cf
57a2641
24398b1
000e05c
1487fa1
00ef1ca
20df848
bd2ce09
da8eda7
688c2d3
a97d4b3
9d8b7e2
517eb70
4c9bac3
a671233
65e914f
f217135
adfd815
8e133b4
bda4bcc
1d07eb9
b293ee3
5572678
6e5a184
e434cd6
f7e0dda
cf4a168
8cc4193
ac8a9d8
116fe76
9643b82
0133ae4
9c31557
7488cb9
2087cf4
566f2c7
209fe90
4910aa5
6bfde52
b698991
74c48b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -576,6 +576,9 @@ | |
| }, | ||
| "externalWebUrl": { | ||
| "type": "string" | ||
| }, | ||
| "blobSha": { | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| --- | ||
| title: Skills | ||
| --- | ||
|
|
||
| Skills are reusable context for [Ask Sourcebot](/docs/features/ask/ask-sourcebot). | ||
| They can capture organizational knowledge, define a workflow, or give Ask Sourcebot extra context for a class of questions. | ||
|
|
||
| <Frame> | ||
| <img src="/images/skills_page.png" alt="Skills page overview" /> | ||
| </Frame> | ||
|
|
||
| ## Creating a skill | ||
|
|
||
| Go to **Settings -> Skills** and click **Add skill**. | ||
|
|
||
| You can create a skill in three ways: | ||
|
|
||
| - **New skill**: Create a skill directly in Sourcebot. | ||
| - **Import from file**: Upload a local `.md` or `.markdown` file and review it before saving. | ||
| - **Import from repository**: Choose a markdown file from an indexed repository and keep the skill synced to that file. | ||
|
|
||
| Each skill has four fields: | ||
|
|
||
| - **Name**: A recognizable name for the skill. | ||
| - **Command**: The slash command users type to run the skill, such as `/deep-research`. | ||
| - **Description**: A short summary of when Ask Sourcebot should use the skill. | ||
| - **Instructions**: The markdown context or guidance Ask Sourcebot uses when the skill runs. | ||
|
|
||
| <Frame> | ||
| <img src="/images/skills_create.png" alt="Create a skill" /> | ||
| </Frame> | ||
|
|
||
| ## Writing instructions | ||
|
|
||
| Keep the description specific. Ask Sourcebot uses it to decide when a skill is relevant, and users see it when browsing slash commands. | ||
|
|
||
| Put the useful context or guidance in the instructions. | ||
|
|
||
| <Frame> | ||
| <img src="/images/skills_editor.png" alt="Skill editor" /> | ||
| </Frame> | ||
|
|
||
| ## Using skills in chat | ||
|
|
||
| Type `/` at the start of an Ask Sourcebot prompt to browse available skill commands. | ||
|
|
||
| When you select a skill, Ask Sourcebot expands the command into that skill's instructions before answering. If the instructions include file mentions, those files are added to the chat context automatically. | ||
|
|
||
| Ask Sourcebot may also load a matching skill automatically. When this happens, the chat details show the loaded skill name and command. | ||
|
|
||
| <Frame> | ||
| <img src="/images/skills_slash_command.png" alt="Skill slash command suggestions" /> | ||
| </Frame> | ||
|
|
||
| ## Personal and shared skills | ||
|
|
||
| Skills can be personal or shared: | ||
|
|
||
| - **Personal skills** are private to you in the current workspace. | ||
| - **Shared skills** are published to the workspace skill catalog. | ||
|
|
||
| You can add or remove shared skills from your own Ask commands from **Settings -> Skills**. Skill authors and organization owners can edit or delete shared skills. | ||
|
|
||
| Organization owners can manage shared skills from **Settings -> Workspace -> Ask Sourcebot**. Owners can also enable **Auto** for a shared skill, which adds it to members' Ask commands by default. | ||
|
|
||
| <Frame> | ||
| <img src="/images/skills_workspace_manager.png" alt="Workspace shared skills manager" /> | ||
| </Frame> | ||
|
|
||
| ## Importing skills | ||
|
|
||
| ### Import from file | ||
|
|
||
| File imports support `.md` and `.markdown` files. If the file has front matter, Sourcebot uses it to prefill the skill name, command, and description. | ||
|
|
||
| Supported front matter fields: | ||
|
|
||
| ```md | ||
| --- | ||
| name: Debug Build Failure | ||
| slug: debug-build | ||
| description: Investigate a failing CI or build error. | ||
| --- | ||
| ``` | ||
|
|
||
| You can also use `title` instead of `name`, and `command` instead of `slug`. | ||
|
|
||
| After importing a local file, the skill is editable in Sourcebot. | ||
|
|
||
| ### Import from repository | ||
|
|
||
| Repository imports let you choose a markdown file from an indexed repository. Sourcebot prefills the create form from the file so you can review it, add a missing description, and save. The resulting skill stays synced to that file. | ||
|
|
||
| Synced skills behave like manually-created skills, with a link back to their source file: | ||
|
|
||
| - Every field stays editable. Local edits are kept until you sync. | ||
| - Sourcebot checks whether the indexed source file has changed. | ||
| - When an update is available, use **Update from source** to replace the skill's content with the file's. | ||
| - When the skill is already up to date, use **Force sync** to restore the content from the source file. | ||
| - Either action warns you first if it would overwrite local edits. | ||
| - Fields the source file does not provide (for example, a missing `description`) keep their local values when you sync. | ||
|
|
||
| <Frame> | ||
| <img src="/images/skills_synced_from_repo.png" alt="Synced repository skill" /> | ||
| </Frame> | ||
|
|
||
| ## Permissions and synced skills | ||
|
|
||
| Synced skills mirror content from a repository file, so Sourcebot only shows them to users who can access the source repository. | ||
|
|
||
| If repository permission syncing is enabled, sharing a synced skill shows a warning before publishing it to the workspace. Members without access to the source repository will not see the skill. | ||
|
|
||
| Organization owners can still see and manage shared synced skills from workspace settings, even if they do not have access to the source repository. | ||
|
|
||
| ## Managing skills | ||
|
|
||
| From **Settings -> Skills**, you can: | ||
|
|
||
| - Search personal and shared skills. | ||
| - Create, edit, delete, or share skills. | ||
| - Add or remove shared skills from your commands. | ||
| - Make a shared skill personal if you manage it. | ||
| - Update synced skills from their source file. | ||
|
|
||
| Synced skills show their source repository and file path, plus one of these statuses: | ||
|
|
||
| - **Up to date**: The skill matches the indexed source file. | ||
| - **Update available**: The indexed source file has changed. | ||
| - **Source file not found**: The file was renamed, deleted, or no longer resolves at the imported revision. | ||
| - **Source repo unavailable**: The repository is no longer available or visible. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| -- CreateEnum | ||
| CREATE TYPE "AgentSkillVisibility" AS ENUM ('PERSONAL', 'SHARED'); | ||
|
|
||
| -- CreateTable | ||
| CREATE TABLE "AgentSkill" ( | ||
| "id" TEXT NOT NULL, | ||
| "visibility" "AgentSkillVisibility" NOT NULL, | ||
| "scopeId" TEXT NOT NULL, | ||
| "slug" TEXT NOT NULL, | ||
| "name" TEXT NOT NULL, | ||
| "description" TEXT NOT NULL, | ||
| "instructions" TEXT NOT NULL, | ||
| "createdById" TEXT NOT NULL, | ||
| "updatedById" TEXT, | ||
| "orgId" INTEGER NOT NULL, | ||
| "enabled" BOOLEAN NOT NULL DEFAULT true, | ||
| "autoEnrolled" BOOLEAN NOT NULL DEFAULT false, | ||
| "sourceRepoName" TEXT, | ||
| "sourceFilePath" TEXT, | ||
| "sourceRevision" TEXT, | ||
| "sourceBlobSha" TEXT, | ||
| "sourceImportedAt" TIMESTAMP(3), | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "updatedAt" TIMESTAMP(3) NOT NULL, | ||
|
|
||
| CONSTRAINT "AgentSkill_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateTable | ||
| CREATE TABLE "AgentSkillAdoption" ( | ||
| "id" TEXT NOT NULL, | ||
| "userId" TEXT NOT NULL, | ||
| "orgId" INTEGER NOT NULL, | ||
| "agentSkillId" TEXT NOT NULL, | ||
| "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "removedAt" TIMESTAMP(3), | ||
|
|
||
| CONSTRAINT "AgentSkillAdoption_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkill_createdById_idx" ON "AgentSkill"("createdById"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkill_updatedById_idx" ON "AgentSkill"("updatedById"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkill_orgId_idx" ON "AgentSkill"("orgId"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkill_orgId_visibility_scopeId_idx" ON "AgentSkill"("orgId", "visibility", "scopeId"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkill_shared_catalog_idx" ON "AgentSkill"("orgId", "visibility", "scopeId", "enabled", "updatedAt" DESC, "name"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "AgentSkill_orgId_visibility_scopeId_slug_key" ON "AgentSkill"("orgId", "visibility", "scopeId", "slug"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkillAdoption_userId_orgId_removedAt_idx" ON "AgentSkillAdoption"("userId", "orgId", "removedAt"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkillAdoption_agentSkillId_idx" ON "AgentSkillAdoption"("agentSkillId"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "AgentSkillAdoption_orgId_idx" ON "AgentSkillAdoption"("orgId"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "AgentSkillAdoption_orgId_userId_agentSkillId_key" ON "AgentSkillAdoption"("orgId", "userId", "agentSkillId"); | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "AgentSkill" ADD CONSTRAINT "AgentSkill_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "AgentSkill" ADD CONSTRAINT "AgentSkill_updatedById_fkey" FOREIGN KEY ("updatedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "AgentSkill" ADD CONSTRAINT "AgentSkill_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "AgentSkillAdoption" ADD CONSTRAINT "AgentSkillAdoption_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "AgentSkillAdoption" ADD CONSTRAINT "AgentSkillAdoption_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "Org"("id") ON DELETE CASCADE ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "AgentSkillAdoption" ADD CONSTRAINT "AgentSkillAdoption_agentSkillId_fkey" FOREIGN KEY ("agentSkillId") REFERENCES "AgentSkill"("id") ON DELETE CASCADE ON UPDATE CASCADE; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -334,6 +334,8 @@ model Org { | |||||||||||||||||||||||||||||
| repoVisits RepoVisit[] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| mcpServers McpServer[] | ||||||||||||||||||||||||||||||
| agentSkills AgentSkill[] | ||||||||||||||||||||||||||||||
| agentSkillAdoptions AgentSkillAdoption[] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| license License? | ||||||||||||||||||||||||||||||
| servicePingEvents ServicePingEvent[] | ||||||||||||||||||||||||||||||
|
|
@@ -519,6 +521,9 @@ model User { | |||||||||||||||||||||||||||||
| sessionVersion Int @default(0) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| userMcpServers UserMcpServer[] | ||||||||||||||||||||||||||||||
| createdAgentSkills AgentSkill[] @relation("AgentSkillCreatedBy") | ||||||||||||||||||||||||||||||
| updatedAgentSkills AgentSkill[] @relation("AgentSkillUpdatedBy") | ||||||||||||||||||||||||||||||
| agentSkillAdoptions AgentSkillAdoption[] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| createdAt DateTime @default(now()) | ||||||||||||||||||||||||||||||
| updatedAt DateTime @updatedAt | ||||||||||||||||||||||||||||||
|
|
@@ -741,6 +746,93 @@ model ChatAccess { | |||||||||||||||||||||||||||||
| @@unique([chatId, userId]) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| enum AgentSkillVisibility { | ||||||||||||||||||||||||||||||
| PERSONAL | ||||||||||||||||||||||||||||||
| SHARED | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| model AgentSkill { | ||||||||||||||||||||||||||||||
| id String @id @default(cuid()) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Namespace identity for slash-command uniqueness and lookup. | ||||||||||||||||||||||||||||||
| // This is not a foreign key and should not be used as an authorization check by itself. | ||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||
| // Every skill belongs to an org (orgId is always set). | ||||||||||||||||||||||||||||||
| // PERSONAL: scopeId is the user id; the skill is private to that user within the org. | ||||||||||||||||||||||||||||||
| // SHARED: scopeId is String(orgId); the skill is visible to the whole org (opt-in via adoptions). | ||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||
| // Future sharing/grants should treat this as the skill's home namespace, not as the ACL. | ||||||||||||||||||||||||||||||
| visibility AgentSkillVisibility | ||||||||||||||||||||||||||||||
| scopeId String | ||||||||||||||||||||||||||||||
| slug String | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| name String | ||||||||||||||||||||||||||||||
| description String | ||||||||||||||||||||||||||||||
| instructions String | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // The user who authored the skill. This is audit metadata, separate from | ||||||||||||||||||||||||||||||
| // the namespace that owns the slash-command slug. | ||||||||||||||||||||||||||||||
| createdBy User @relation("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: Cascade) | ||||||||||||||||||||||||||||||
| createdById String | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| updatedBy User? @relation("AgentSkillUpdatedBy", fields: [updatedById], references: [id], onDelete: SetNull) | ||||||||||||||||||||||||||||||
| updatedById String? | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+773
to
+780
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
This contradicts the stated intent and risks unexpected data loss on a routine admin action (user removal). 🛠️ Suggested fix- createdBy User `@relation`("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: Cascade)
- createdById String
+ createdBy User? `@relation`("AgentSkillCreatedBy", fields: [createdById], references: [id], onDelete: SetNull)
+ createdById String?Note: this requires updating all 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As of current this was actually intentional, but it's unclear if it should be this way or if a shared skill truly becomes an org resource (but then it's weird that the creator is the sole person who can then remove everybodies access to it).
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Given this is a genuine product/design decision rather than a clear-cut bug, I'll leave it to you and the team to decide the intended semantics. If you'd like, I can open a follow-up issue to track "clarify/rework AgentSkill ownership semantics for org offboarding" so it doesn't get lost — just let me know. ✏️ Learnings added
🧠 Learnings used |
||||||||||||||||||||||||||||||
| // The owning org. Both PERSONAL and SHARED skills are scoped to an org, so a | ||||||||||||||||||||||||||||||
| // user's personal skills change when they switch orgs. | ||||||||||||||||||||||||||||||
| org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) | ||||||||||||||||||||||||||||||
| orgId Int | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| enabled Boolean @default(true) | ||||||||||||||||||||||||||||||
| autoEnrolled Boolean @default(false) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Provenance for skills imported from an indexed repository file. All null for | ||||||||||||||||||||||||||||||
| // skills created manually or imported from a local file. When set, the skill's | ||||||||||||||||||||||||||||||
| // content is a read-only mirror of the source file: edits happen in the repo and | ||||||||||||||||||||||||||||||
| // are pulled in via "Update from source". sourceBlobSha (the git blob OID at | ||||||||||||||||||||||||||||||
| // import) is the comparison key used to detect when the indexed file has changed. | ||||||||||||||||||||||||||||||
| // Sync is personal-only; publishing to shared snapshots the content and drops | ||||||||||||||||||||||||||||||
| // these fields. | ||||||||||||||||||||||||||||||
| sourceRepoName String? | ||||||||||||||||||||||||||||||
| sourceFilePath String? | ||||||||||||||||||||||||||||||
| sourceRevision String? | ||||||||||||||||||||||||||||||
| sourceBlobSha String? | ||||||||||||||||||||||||||||||
| sourceImportedAt DateTime? | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| createdAt DateTime @default(now()) | ||||||||||||||||||||||||||||||
| updatedAt DateTime @updatedAt | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| adoptions AgentSkillAdoption[] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| @@unique([orgId, visibility, scopeId, slug]) | ||||||||||||||||||||||||||||||
| @@index([createdById]) | ||||||||||||||||||||||||||||||
| @@index([updatedById]) | ||||||||||||||||||||||||||||||
| @@index([orgId]) | ||||||||||||||||||||||||||||||
| @@index([orgId, visibility, scopeId]) | ||||||||||||||||||||||||||||||
| @@index([orgId, visibility, scopeId, enabled, updatedAt(sort: Desc), name], map: "AgentSkill_shared_catalog_idx") | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| model AgentSkillAdoption { | ||||||||||||||||||||||||||||||
| id String @id @default(cuid()) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| user User @relation(fields: [userId], references: [id], onDelete: Cascade) | ||||||||||||||||||||||||||||||
| userId String | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) | ||||||||||||||||||||||||||||||
| orgId Int | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| agentSkill AgentSkill @relation(fields: [agentSkillId], references: [id], onDelete: Cascade) | ||||||||||||||||||||||||||||||
| agentSkillId String | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| createdAt DateTime @default(now()) | ||||||||||||||||||||||||||||||
| removedAt DateTime? | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| @@unique([orgId, userId, agentSkillId]) | ||||||||||||||||||||||||||||||
| @@index([userId, orgId, removedAt]) | ||||||||||||||||||||||||||||||
| @@index([agentSkillId]) | ||||||||||||||||||||||||||||||
| @@index([orgId]) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // OAuth2 Authorization Server models | ||||||||||||||||||||||||||||||
| // @see: https://datatracker.ietf.org/doc/html/rfc6749 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.