Skip to content

Commit b98068e

Browse files
feat: Add /waz slash command to generate TMP schemas via AI
1 parent d84de48 commit b98068e

7 files changed

Lines changed: 126 additions & 0 deletions

File tree

app/i18n/en/warp.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,6 +2426,8 @@ slash-cmd-fork-desc = Fork the current conversation in a new pane or a new tab
24262426
slash-cmd-fork-hint = <optional prompt to send in forked conversation>
24272427
slash-cmd-open-code-review-desc = Open code review
24282428
slash-cmd-init-desc = Generate or update an AGENTS.md file
2429+
slash-cmd-waz-desc = Generate a TMP schema for a CLI tool using AI
2430+
slash-cmd-waz-hint = <command or tool>
24292431
slash-cmd-open-project-rules-desc = Open the project rules file (AGENTS.md)
24302432
slash-cmd-open-mcp-servers-desc = Open MCP servers
24312433
slash-cmd-open-settings-file-desc = Open settings file (TOML)

app/i18n/ja/warp.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2330,6 +2330,8 @@ slash-cmd-fork-desc = 現在の会話を新しいペインまたはタブにフ
23302330
slash-cmd-fork-hint = <フォーク後の会話に送る任意のプロンプト>
23312331
slash-cmd-open-code-review-desc = コードレビューを開く
23322332
slash-cmd-init-desc = AGENTS.md ファイルを生成または更新
2333+
slash-cmd-waz-desc = AIを使用してCLIツールのTMPスキーマを生成します
2334+
slash-cmd-waz-hint = <コマンドまたはツール名>
23332335
slash-cmd-open-project-rules-desc = プロジェクトルールファイル (AGENTS.md) を開く
23342336
slash-cmd-open-mcp-servers-desc = MCP サーバーを開く
23352337
slash-cmd-open-settings-file-desc = 設定ファイル (TOML) を開く

app/i18n/zh-CN/warp.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,6 +2352,8 @@ slash-cmd-fork-desc = 在新窗格或新标签页中分叉当前对话
23522352
slash-cmd-fork-hint = <可选:在分叉后的对话中发送的提示词>
23532353
slash-cmd-open-code-review-desc = 打开代码评审
23542354
slash-cmd-init-desc = 生成或更新 AGENTS.md 文件
2355+
slash-cmd-waz-desc = 使用 AI 为 CLI 工具生成 TMP 模式 (schema)
2356+
slash-cmd-waz-hint = <命令或工具名称>
23552357
slash-cmd-open-project-rules-desc = 打开项目规则文件(AGENTS.md)
23562358
slash-cmd-open-mcp-servers-desc = 打开 MCP 服务器
23572359
slash-cmd-open-settings-file-desc = 打开设置文件(TOML)

app/src/ai/agent_providers/prompt_renderer.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ fn build_env() -> Environment<'static> {
7373
include_str!("prompts/commands/init_project.j2"),
7474
)
7575
.expect("init_project command template parses");
76+
env.add_template(
77+
"commands/generate_schema.j2",
78+
include_str!("prompts/commands/generate_schema.j2"),
79+
)
80+
.expect("generate_schema command template parses");
7681

7782
// Distribute system prompt (aligned opencode) by model id substring matching
7883
// `packages/opencode/src/session/system.ts::provider`). The OpenRouter path looks like
@@ -217,6 +222,11 @@ struct InitProjectCommandContext {
217222
arguments: String,
218223
}
219224

225+
#[derive(Debug, Default, Serialize)]
226+
struct GenerateSchemaCommandContext {
227+
tool: String,
228+
}
229+
220230
#[derive(Debug, Default, Serialize)]
221231
struct PromptContext {
222232
cwd: Option<String>,
@@ -383,6 +393,28 @@ pub fn render_init_project_command(arguments: Option<&str>) -> String {
383393
}
384394
}
385395

396+
pub fn render_generate_schema_command(tool: &str) -> String {
397+
let ctx = GenerateSchemaCommandContext {
398+
tool: tool.trim().to_owned(),
399+
};
400+
let env = env();
401+
let template_name = "commands/generate_schema.j2";
402+
let tmpl = match env.get_template(template_name) {
403+
Ok(t) => t,
404+
Err(e) => {
405+
log::error!("[byop prompt] failed to get template {template_name}: {e}");
406+
return fallback_generate_schema_command(&ctx.tool);
407+
}
408+
};
409+
match tmpl.render(Value::from_serialize(&ctx)) {
410+
Ok(s) => s,
411+
Err(e) => {
412+
log::error!("[byop prompt] render {template_name} failed: {e}");
413+
fallback_generate_schema_command(&ctx.tool)
414+
}
415+
}
416+
}
417+
386418
/// Render the system message string ultimately sent to the upstream model.
387419
///
388420
/// `ctx` usually comes from the latest `AIAgentInput::UserQuery.context` in `params.input`.
@@ -441,6 +473,13 @@ fn fallback_init_project_command(arguments: &str) -> String {
441473
)
442474
}
443475

476+
fn fallback_generate_schema_command(tool: &str) -> String {
477+
format!(
478+
"Generate a Token Model Protocol (TMP) JSON schema for the CLI tool `{tool}`. \
479+
Please identify its most common subcommands, options, and parameters, and design the schema."
480+
)
481+
}
482+
444483
/// Rendering system (only used when template loading/rendering fails, should not be triggered in normal paths).
445484
fn fallback_system(model_id: &str) -> String {
446485
format!(
@@ -465,6 +504,12 @@ mod tests {
465504
assert!(out.contains("## Writing rules"), "{out}");
466505
}
467506

507+
#[test]
508+
fn render_generate_schema_command_uses_template() {
509+
let out = render_generate_schema_command("docker");
510+
assert!(out.contains("Generate a Token Model Protocol (TMP) JSON schema for the CLI tool \"docker\""), "{out}");
511+
}
512+
468513
#[test]
469514
fn pick_template_dispatches_by_model_family() {
470515
// Direct connection mode
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Generate a Token Model Protocol (TMP) JSON schema for the CLI tool "{{ tool }}".
2+
3+
A TMP schema defines the subcommand structure, parameters (flags and arguments), primitive token types, descriptions, and dynamic resolvers for a CLI tool.
4+
5+
Please design a comprehensive and detailed schema file for "{{ tool }}", mapping its most common subcommands, options, and parameters.
6+
7+
## Schema Specification and Format
8+
The output MUST be a valid JSON object matching the following structure:
9+
{
10+
"meta": {
11+
"tool": "{{ tool }}",
12+
"description": "Short description of what the tool does."
13+
},
14+
"commands": [
15+
{
16+
"command": "{{ tool }} <subcommand>",
17+
"description": "Short description of the subcommand.",
18+
"group": "{{ tool }}",
19+
"verified": true,
20+
"tokens": [
21+
{
22+
"name": "parameter_name",
23+
"description": "What this flag/argument does.",
24+
"required": false,
25+
"token_type": "Enum" | "Boolean" | "String" | "File" | "Number",
26+
"flag": "--flag-name" (optional),
27+
"data_source": { (optional)
28+
"resolver": "git:branches" | "git:status_files" | "cargo:packages" | "npm:scripts" | etc.
29+
}
30+
}
31+
]
32+
}
33+
]
34+
}
35+
36+
## Guidelines
37+
1. The JSON must be valid and ready to be saved to `.waz/schemas/{{ tool }}.json`.
38+
2. Do not wrap the JSON in markdown code blocks or add conversational commentary; output ONLY the raw, clean JSON content.
39+
3. Make sure to define the correct `token_type` for each parameter. Use "Boolean" for flags that take no values (like --force or -f), "Enum" for parameters with specific values, "File" for paths/files, and "String" for generic text.

app/src/ai/blocklist/controller/slash_command.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ pub enum SlashCommandRequest {
3131
InitProjectRules {
3232
arguments: Option<String>,
3333
},
34+
GenerateSchema {
35+
tool: String,
36+
},
3437
Summarize {
3538
prompt: Option<String>,
3639
/// Waz BYOP local session compression: Whether this summary is automatically triggered by token-overflow.
@@ -65,6 +68,15 @@ impl SlashCommandRequest {
6568
});
6669
}
6770

71+
if let Some(tool) = query
72+
.strip_prefix(commands::WAZ_NAME)
73+
.and_then(|query| query.strip_prefix(' '))
74+
{
75+
return Some(Self::GenerateSchema {
76+
tool: tool.trim().to_string(),
77+
});
78+
}
79+
6880
// Check if query starts with /compact and route to summarize conversation
6981
if let Some(prompt) = query.strip_prefix(commands::COMPACT.name) {
7082
return Some(Self::Summarize {
@@ -222,6 +234,17 @@ impl SlashCommandRequest {
222234
running_command: None,
223235
intended_agent: None,
224236
}],
237+
SlashCommandRequest::GenerateSchema { tool } => vec![AIAgentInput::UserQuery {
238+
query: crate::ai::agent_providers::prompt_renderer::render_generate_schema_command(
239+
&tool,
240+
),
241+
context,
242+
static_query_type: None,
243+
referenced_attachments: HashMap::<String, AIAgentAttachment>::new(),
244+
user_query_mode: UserQueryMode::Normal,
245+
running_command: None,
246+
intended_agent: None,
247+
}],
225248
SlashCommandRequest::Summarize { prompt, overflow } => {
226249
vec![AIAgentInput::SummarizeConversation { prompt, overflow }]
227250
}
@@ -258,6 +281,7 @@ impl SlashCommandRequest {
258281
SlashCommandRequest::CloneRepository { .. } => EntrypointType::CloneRepository,
259282
SlashCommandRequest::InitProjectRules { .. } => EntrypointType::InitProjectRules,
260283
SlashCommandRequest::CreateNewProject { .. }
284+
| SlashCommandRequest::GenerateSchema { .. }
261285
| SlashCommandRequest::Summarize { .. }
262286
| SlashCommandRequest::FetchReviewComments { .. }
263287
| SlashCommandRequest::InvokeSkill { .. } => EntrypointType::UserInitiated,

app/src/search/slash_command_menu/static_commands/commands.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,17 @@ pub static INIT: LazyLock<StaticCommand> = LazyLock::new(|| StaticCommand {
146146
argument: Some(Argument::optional()),
147147
});
148148

149+
pub const WAZ_NAME: &str = "/waz";
150+
151+
pub static WAZ: LazyLock<StaticCommand> = LazyLock::new(|| StaticCommand {
152+
name: WAZ_NAME,
153+
description: t_static!("slash-cmd-waz-desc"),
154+
icon_path: "bundled/svg/stars-01.svg",
155+
availability: Availability::AI_ENABLED,
156+
auto_enter_ai_mode: true,
157+
argument: Some(Argument::required().with_hint_text(t_static!("slash-cmd-waz-hint"))),
158+
});
159+
149160
pub static OPEN_PROJECT_RULES: LazyLock<StaticCommand> = LazyLock::new(|| StaticCommand {
150161
name: "/open-project-rules",
151162
description: t_static!("slash-cmd-open-project-rules-desc"),
@@ -428,6 +439,7 @@ fn all_commands() -> Vec<StaticCommand> {
428439
ADD_PROMPT.clone(),
429440
ADD_RULE.clone(),
430441
INIT.clone(),
442+
WAZ.clone(),
431443
OPEN_PROJECT_RULES.clone(),
432444
OPEN_MCP_SERVERS.clone(),
433445
OPEN_RULES.clone(),

0 commit comments

Comments
 (0)