Merge branch 'dev' into feat/dialog-esc-click

This commit is contained in:
Akshar Patel
2026-01-31 00:56:18 -05:00
committed by GitHub
12 changed files with 50 additions and 32 deletions

View File

@@ -1,21 +1,15 @@
name: beta name: beta
on: on:
push: workflow_dispatch:
branches: [dev] schedule:
pull_request: - cron: "0 * * * *"
types: [opened, synchronize, labeled, unlabeled]
jobs: jobs:
sync: sync:
if: |
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'contributor'))
runs-on: blacksmith-4vcpu-ubuntu-2404 runs-on: blacksmith-4vcpu-ubuntu-2404
permissions: permissions:
contents: write contents: write
pull-requests: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -1,6 +1,9 @@
name: test name: test
on: on:
push:
branches:
- dev
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
jobs: jobs:

View File

@@ -345,8 +345,9 @@ export function Autocomplete(props: {
const results: AutocompleteOption[] = [...command.slashes()] const results: AutocompleteOption[] = [...command.slashes()]
for (const serverCommand of sync.data.command) { for (const serverCommand of sync.data.command) {
const label = serverCommand.source === "mcp" ? ":mcp" : serverCommand.source === "skill" ? ":skill" : ""
results.push({ results.push({
display: "/" + serverCommand.name + (serverCommand.mcp ? " (MCP)" : ""), display: "/" + serverCommand.name + label,
description: serverCommand.description, description: serverCommand.description,
onSelect: () => { onSelect: () => {
const newText = "/" + serverCommand.name + " " const newText = "/" + serverCommand.name + " "

View File

@@ -6,6 +6,7 @@ import { Identifier } from "../id/id"
import PROMPT_INITIALIZE from "./template/initialize.txt" import PROMPT_INITIALIZE from "./template/initialize.txt"
import PROMPT_REVIEW from "./template/review.txt" import PROMPT_REVIEW from "./template/review.txt"
import { MCP } from "../mcp" import { MCP } from "../mcp"
import { Skill } from "../skill"
export namespace Command { export namespace Command {
export const Event = { export const Event = {
@@ -26,7 +27,7 @@ export namespace Command {
description: z.string().optional(), description: z.string().optional(),
agent: z.string().optional(), agent: z.string().optional(),
model: z.string().optional(), model: z.string().optional(),
mcp: z.boolean().optional(), source: z.enum(["command", "mcp", "skill"]).optional(),
// workaround for zod not supporting async functions natively so we use getters // workaround for zod not supporting async functions natively so we use getters
// https://zod.dev/v4/changelog?id=zfunction // https://zod.dev/v4/changelog?id=zfunction
template: z.promise(z.string()).or(z.string()), template: z.promise(z.string()).or(z.string()),
@@ -94,7 +95,7 @@ export namespace Command {
for (const [name, prompt] of Object.entries(await MCP.prompts())) { for (const [name, prompt] of Object.entries(await MCP.prompts())) {
result[name] = { result[name] = {
name, name,
mcp: true, source: "mcp",
description: prompt.description, description: prompt.description,
get template() { get template() {
// since a getter can't be async we need to manually return a promise here // since a getter can't be async we need to manually return a promise here
@@ -118,6 +119,21 @@ export namespace Command {
} }
} }
// Add skills as invokable commands
for (const skill of await Skill.all()) {
// Skip if a command with this name already exists
if (result[skill.name]) continue
result[skill.name] = {
name: skill.name,
description: skill.description,
source: "skill",
get template() {
return skill.content
},
hints: [],
}
}
return result return result
}) })

View File

@@ -2,7 +2,9 @@ import { Instance } from "../project/instance"
export namespace Env { export namespace Env {
const state = Instance.state(() => { const state = Instance.state(() => {
return process.env as Record<string, string | undefined> // Create a shallow copy to isolate environment per instance
// Prevents parallel tests from interfering with each other's env vars
return { ...process.env } as Record<string, string | undefined>
}) })
export function get(key: string) { export function get(key: string) {

View File

@@ -214,8 +214,8 @@ export namespace Ripgrep {
input.signal?.throwIfAborted() input.signal?.throwIfAborted()
const args = [await filepath(), "--files", "--glob=!.git/*"] const args = [await filepath(), "--files", "--glob=!.git/*"]
if (input.follow !== false) args.push("--follow") if (input.follow) args.push("--follow")
if (input.hidden !== false) args.push("--hidden") if (input.hidden) args.push("--hidden")
if (input.maxDepth !== undefined) args.push(`--max-depth=${input.maxDepth}`) if (input.maxDepth !== undefined) args.push(`--max-depth=${input.maxDepth}`)
if (input.glob) { if (input.glob) {
for (const g of input.glob) { for (const g of input.glob) {
@@ -381,7 +381,7 @@ export namespace Ripgrep {
follow?: boolean follow?: boolean
}) { }) {
const args = [`${await filepath()}`, "--json", "--hidden", "--glob='!.git/*'"] const args = [`${await filepath()}`, "--json", "--hidden", "--glob='!.git/*'"]
if (input.follow !== false) args.push("--follow") if (input.follow) args.push("--follow")
if (input.glob) { if (input.glob) {
for (const g of input.glob) { for (const g of input.glob) {

View File

@@ -83,7 +83,11 @@ export namespace ProviderTransform {
return msg return msg
}) })
} }
if (model.providerID === "mistral" || model.api.id.toLowerCase().includes("mistral")) { if (
model.providerID === "mistral" ||
model.api.id.toLowerCase().includes("mistral") ||
model.api.id.toLocaleLowerCase().includes("devstral")
) {
const result: ModelMessage[] = [] const result: ModelMessage[] = []
for (let i = 0; i < msgs.length; i++) { for (let i = 0; i < msgs.length; i++) {
const msg = msgs[i] const msg = msgs[i]

View File

@@ -18,6 +18,7 @@ export namespace Skill {
name: z.string(), name: z.string(),
description: z.string(), description: z.string(),
location: z.string(), location: z.string(),
content: z.string(),
}) })
export type Info = z.infer<typeof Info> export type Info = z.infer<typeof Info>
@@ -74,6 +75,7 @@ export namespace Skill {
name: parsed.data.name, name: parsed.data.name,
description: parsed.data.description, description: parsed.data.description,
location: match, location: match,
content: md.content,
} }
} }

View File

@@ -37,15 +37,7 @@ export const GrepTool = Tool.define("grep", {
await assertExternalDirectory(ctx, searchPath, { kind: "directory" }) await assertExternalDirectory(ctx, searchPath, { kind: "directory" })
const rgPath = await Ripgrep.filepath() const rgPath = await Ripgrep.filepath()
const args = [ const args = ["-nH", "--hidden", "--no-messages", "--field-match-separator=|", "--regexp", params.pattern]
"-nH",
"--hidden",
"--follow",
"--no-messages",
"--field-match-separator=|",
"--regexp",
params.pattern,
]
if (params.include) { if (params.include) {
args.push("--glob", params.include) args.push("--glob", params.include)
} }

View File

@@ -2,7 +2,6 @@ import path from "path"
import z from "zod" import z from "zod"
import { Tool } from "./tool" import { Tool } from "./tool"
import { Skill } from "../skill" import { Skill } from "../skill"
import { ConfigMarkdown } from "../config/markdown"
import { PermissionNext } from "../permission/next" import { PermissionNext } from "../permission/next"
export const SkillTool = Tool.define("skill", async (ctx) => { export const SkillTool = Tool.define("skill", async (ctx) => {
@@ -62,7 +61,7 @@ export const SkillTool = Tool.define("skill", async (ctx) => {
always: [params.name], always: [params.name],
metadata: {}, metadata: {},
}) })
const content = (await ConfigMarkdown.parse(skill.location)).content const content = skill.content
const dir = path.dirname(skill.location) const dir = path.dirname(skill.location)
// Format output similar to plugin pattern // Format output similar to plugin pattern

View File

@@ -2116,7 +2116,7 @@ export type Command = {
description?: string description?: string
agent?: string agent?: string
model?: string model?: string
mcp?: boolean source?: "command" | "mcp" | "skill"
template: string template: string
subtask?: boolean subtask?: boolean
hints: Array<string> hints: Array<string>
@@ -4913,6 +4913,7 @@ export type AppSkillsResponses = {
name: string name: string
description: string description: string
location: string location: string
content: string
}> }>
} }

View File

@@ -5723,9 +5723,12 @@
}, },
"location": { "location": {
"type": "string" "type": "string"
},
"content": {
"type": "string"
} }
}, },
"required": ["name", "description", "location"] "required": ["name", "description", "location", "content"]
} }
} }
} }
@@ -10770,8 +10773,9 @@
"model": { "model": {
"type": "string" "type": "string"
}, },
"mcp": { "source": {
"type": "boolean" "type": "string",
"enum": ["command", "mcp", "skill"]
}, },
"template": { "template": {
"anyOf": [ "anyOf": [