返回 CodeWhale
schema.rs
根目录 / crates / tui / src / tools / github / schema.rs
1 //! The input contracts the model sees: the canonical union schema, and the
2 //! per-action schemas the hidden legacy aliases still advertise.
3
4 use serde_json::{Value, json};
5
6 /// The union schema for the canonical `github` tool: `action` plus every
7 /// field any allowed action reads. `read_only` drops the write fields so the
8 /// Plan-mode surface cannot even describe a mutation.
9 pub(super) fn canonical_schema(allowed_actions: &[&str], read_only: bool) -> Value {
10 let actions: Vec<&str> = allowed_actions.to_vec();
11 let mut properties = serde_json::Map::new();
12 properties.insert(
13 "action".to_string(),
14 json!({
15 "type": "string",
16 "enum": actions,
17 "description": "Action to perform."
18 }),
19 );
20 properties.insert(
21 "number".to_string(),
22 json!({ "type": "integer", "minimum": 1, "description": "Issue/PR number (all actions)." }),
23 );
24 properties.insert(
25 "include_comments".to_string(),
26 json!({ "type": "boolean", "default": true, "description": "(action=issue_context)" }),
27 );
28 properties.insert(
29 "include_diff".to_string(),
30 json!({ "type": "boolean", "default": false, "description": "(action=pr_context)" }),
31 );
32 if !read_only {
33 properties.insert(
34 "target".to_string(),
35 json!({ "type": "string", "enum": ["issue", "pr"], "description": "(action=comment)" }),
36 );
37 properties.insert(
38 "body".to_string(),
39 json!({ "type": "string", "description": "Comment body (action=comment)." }),
40 );
41 properties.insert(
42 "evidence".to_string(),
43 json!({
44 "type": "object",
45 "description": "Evidence object (action=comment/close_*). Close actions require files_changed, tests_run, final_status.",
46 "properties": {
47 "files_changed": { "type": "array", "items": { "type": "string" } },
48 "tests_run": { "type": "array", "items": { "type": "string" } },
49 "commits": { "type": "array", "items": { "type": "string" } },
50 "final_status": { "type": "string" }
51 }
52 }),
53 );
54 properties.insert(
55 "acceptance_criteria".to_string(),
56 json!({ "type": "array", "items": { "type": "string" }, "minItems": 1, "description": "(action=close_issue/close_pr)" }),
57 );
58 properties.insert(
59 "comment".to_string(),
60 json!({ "type": "string", "description": "Optional closing comment (action=close_issue/close_pr)." }),
61 );
62 properties.insert(
63 "allow_dirty".to_string(),
64 json!({ "type": "boolean", "default": false, "description": "(action=close_issue/close_pr)" }),
65 );
66 properties.insert(
67 "dry_run".to_string(),
68 json!({ "type": "boolean", "default": false, "description": "(action=comment/close_issue/close_pr)" }),
69 );
70 }
71 json!({
72 "type": "object",
73 "properties": properties,
74 "additionalProperties": false
75 })
76 }
77
78 /// The exact schema the legacy per-action tool exposed, kept so hidden alias
79 /// registrations report an identical contract to the pre-unification tools.
80 pub(super) fn legacy_action_schema(action: &str) -> Value {
81 match action {
82 "issue_context" => json!({
83 "type": "object",
84 "properties": {
85 "number": { "type": "integer", "minimum": 1 },
86 "include_comments": { "type": "boolean", "default": true }
87 },
88 "required": ["number"],
89 "additionalProperties": false
90 }),
91 "pr_context" => json!({
92 "type": "object",
93 "properties": {
94 "number": { "type": "integer", "minimum": 1 },
95 "include_diff": { "type": "boolean", "default": false }
96 },
97 "required": ["number"],
98 "additionalProperties": false
99 }),
100 "comment" => json!({
101 "type": "object",
102 "properties": {
103 "target": { "type": "string", "enum": ["issue", "pr"] },
104 "number": { "type": "integer", "minimum": 1 },
105 "body": { "type": "string" },
106 "evidence": { "type": "object" },
107 "dry_run": { "type": "boolean", "default": false }
108 },
109 "required": ["target", "number", "body", "evidence"],
110 "additionalProperties": false
111 }),
112 // close_issue / close_pr share the close schema.
113 _ => close_input_schema(),
114 }
115 }
116
117 fn close_input_schema() -> Value {
118 json!({
119 "type": "object",
120 "properties": {
121 "number": { "type": "integer", "minimum": 1 },
122 "acceptance_criteria": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
123 "evidence": {
124 "type": "object",
125 "properties": {
126 "files_changed": { "type": "array", "items": { "type": "string" } },
127 "tests_run": { "type": "array", "items": { "type": "string" } },
128 "commits": { "type": "array", "items": { "type": "string" } },
129 "final_status": { "type": "string" }
130 },
131 "required": ["files_changed", "tests_run", "final_status"]
132 },
133 "comment": { "type": "string" },
134 "allow_dirty": { "type": "boolean", "default": false },
135 "dry_run": { "type": "boolean", "default": false }
136 },
137 "required": ["number", "acceptance_criteria", "evidence"],
138 "additionalProperties": false
139 })
140 }
141
141 lines RUST