fenix-mcp 0.6.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fenix_mcp/__init__.py +1 -1
- fenix_mcp/application/tool_base.py +20 -1
- fenix_mcp/application/tools/initialize.py +37 -21
- fenix_mcp/application/tools/intelligence.py +71 -75
- fenix_mcp/application/tools/knowledge.py +305 -339
- fenix_mcp/application/tools/productivity.py +51 -51
- fenix_mcp/application/tools/user_config.py +32 -32
- fenix_mcp/domain/knowledge.py +43 -48
- fenix_mcp/domain/user_config.py +10 -2
- fenix_mcp/infrastructure/fenix_api/client.py +28 -44
- {fenix_mcp-0.6.0.dist-info → fenix_mcp-1.0.0.dist-info}/METADATA +1 -1
- {fenix_mcp-0.6.0.dist-info → fenix_mcp-1.0.0.dist-info}/RECORD +15 -15
- {fenix_mcp-0.6.0.dist-info → fenix_mcp-1.0.0.dist-info}/WHEEL +0 -0
- {fenix_mcp-0.6.0.dist-info → fenix_mcp-1.0.0.dist-info}/entry_points.txt +0 -0
- {fenix_mcp-0.6.0.dist-info → fenix_mcp-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -22,6 +22,8 @@ from fenix_mcp.application.tool_base import (
|
|
|
22
22
|
ToolRequest,
|
|
23
23
|
UUIDStr,
|
|
24
24
|
VersionStr,
|
|
25
|
+
sanitize_null,
|
|
26
|
+
sanitize_null_list,
|
|
25
27
|
)
|
|
26
28
|
from fenix_mcp.domain.knowledge import KnowledgeService, _format_date
|
|
27
29
|
from fenix_mcp.infrastructure.context import AppContext
|
|
@@ -37,90 +39,93 @@ class KnowledgeAction(str, Enum):
|
|
|
37
39
|
# Work items
|
|
38
40
|
WORK_CREATE = (
|
|
39
41
|
"work_create",
|
|
40
|
-
"
|
|
42
|
+
"Creates a work item with title, status and optional links.",
|
|
41
43
|
)
|
|
42
44
|
WORK_LIST = (
|
|
43
45
|
"work_list",
|
|
44
|
-
"
|
|
46
|
+
"Lists work items with status, priority and context filters.",
|
|
45
47
|
)
|
|
46
|
-
WORK_GET = ("work_get", "
|
|
48
|
+
WORK_GET = ("work_get", "Gets full details of a work item by ID.")
|
|
47
49
|
WORK_UPDATE = (
|
|
48
50
|
"work_update",
|
|
49
|
-
"
|
|
51
|
+
"Updates specific fields of an existing work item.",
|
|
50
52
|
)
|
|
51
|
-
WORK_DELETE = ("work_delete", "
|
|
52
|
-
WORK_BACKLOG = ("work_backlog", "
|
|
53
|
-
WORK_SEARCH = (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
WORK_DELETE = ("work_delete", "Permanently removes a work item.")
|
|
54
|
+
WORK_BACKLOG = ("work_backlog", "Lists backlog items for a team.")
|
|
55
|
+
WORK_SEARCH = (
|
|
56
|
+
"work_search",
|
|
57
|
+
"Searches work items by text with additional filters.",
|
|
58
|
+
)
|
|
59
|
+
WORK_ANALYTICS = ("work_analytics", "Returns consolidated work item metrics.")
|
|
60
|
+
WORK_BY_BOARD = ("work_by_board", "Lists work items associated with a board.")
|
|
61
|
+
WORK_BY_SPRINT = ("work_by_sprint", "Lists work items associated with a sprint.")
|
|
62
|
+
WORK_BY_EPIC = ("work_by_epic", "Lists work items associated with an epic.")
|
|
63
|
+
WORK_CHILDREN = ("work_children", "Lists child work items of a parent item.")
|
|
59
64
|
WORK_STATUS_UPDATE = (
|
|
60
65
|
"work_status_update",
|
|
61
|
-
"
|
|
66
|
+
"Updates only the status of a work item.",
|
|
62
67
|
)
|
|
63
68
|
WORK_ASSIGN_SPRINT = (
|
|
64
69
|
"work_assign_sprint",
|
|
65
|
-
"
|
|
70
|
+
"Assigns work items to a sprint.",
|
|
66
71
|
)
|
|
67
72
|
|
|
68
73
|
# Boards
|
|
69
|
-
BOARD_LIST = ("board_list", "
|
|
70
|
-
BOARD_BY_TEAM = ("board_by_team", "
|
|
71
|
-
BOARD_FAVORITES = ("board_favorites", "
|
|
72
|
-
BOARD_GET = ("board_get", "
|
|
73
|
-
BOARD_COLUMNS = ("board_columns", "
|
|
74
|
+
BOARD_LIST = ("board_list", "Lists available boards with optional filters.")
|
|
75
|
+
BOARD_BY_TEAM = ("board_by_team", "Lists boards for a specific team.")
|
|
76
|
+
BOARD_FAVORITES = ("board_favorites", "Lists boards marked as favorites.")
|
|
77
|
+
BOARD_GET = ("board_get", "Gets board details by ID.")
|
|
78
|
+
BOARD_COLUMNS = ("board_columns", "Lists columns configured for a board.")
|
|
74
79
|
|
|
75
80
|
# Sprints
|
|
76
|
-
SPRINT_LIST = ("sprint_list", "
|
|
77
|
-
SPRINT_BY_TEAM = ("sprint_by_team", "
|
|
78
|
-
SPRINT_ACTIVE = ("sprint_active", "
|
|
79
|
-
SPRINT_GET = ("sprint_get", "
|
|
81
|
+
SPRINT_LIST = ("sprint_list", "Lists available sprints with optional filters.")
|
|
82
|
+
SPRINT_BY_TEAM = ("sprint_by_team", "Lists sprints associated with a team.")
|
|
83
|
+
SPRINT_ACTIVE = ("sprint_active", "Gets the active sprint for a team.")
|
|
84
|
+
SPRINT_GET = ("sprint_get", "Gets sprint details by ID.")
|
|
80
85
|
SPRINT_WORK_ITEMS = (
|
|
81
86
|
"sprint_work_items",
|
|
82
|
-
"
|
|
87
|
+
"Lists work items linked to a sprint.",
|
|
83
88
|
)
|
|
84
89
|
|
|
85
90
|
# Modes
|
|
86
|
-
MODE_CREATE = ("mode_create", "
|
|
87
|
-
MODE_LIST = ("mode_list", "
|
|
88
|
-
MODE_GET = ("mode_get", "
|
|
89
|
-
MODE_UPDATE = ("mode_update", "
|
|
90
|
-
MODE_DELETE = ("mode_delete", "
|
|
91
|
-
MODE_RULE_ADD = ("mode_rule_add", "
|
|
91
|
+
MODE_CREATE = ("mode_create", "Creates a mode with content and optional metadata.")
|
|
92
|
+
MODE_LIST = ("mode_list", "Lists registered modes.")
|
|
93
|
+
MODE_GET = ("mode_get", "Gets full details of a mode.")
|
|
94
|
+
MODE_UPDATE = ("mode_update", "Updates properties of an existing mode.")
|
|
95
|
+
MODE_DELETE = ("mode_delete", "Removes a mode.")
|
|
96
|
+
MODE_RULE_ADD = ("mode_rule_add", "Associates a rule with a mode.")
|
|
92
97
|
MODE_RULE_REMOVE = (
|
|
93
98
|
"mode_rule_remove",
|
|
94
|
-
"
|
|
99
|
+
"Removes the association of a rule with a mode.",
|
|
95
100
|
)
|
|
96
|
-
MODE_RULES = ("mode_rules", "
|
|
101
|
+
MODE_RULES = ("mode_rules", "Lists rules associated with a mode.")
|
|
97
102
|
|
|
98
103
|
# Rules
|
|
99
|
-
RULE_CREATE = ("rule_create", "
|
|
100
|
-
RULE_LIST = ("rule_list", "
|
|
101
|
-
RULE_GET = ("rule_get", "
|
|
102
|
-
RULE_UPDATE = ("rule_update", "
|
|
103
|
-
RULE_DELETE = ("rule_delete", "
|
|
104
|
+
RULE_CREATE = ("rule_create", "Creates a rule with content and metadata.")
|
|
105
|
+
RULE_LIST = ("rule_list", "Lists registered rules.")
|
|
106
|
+
RULE_GET = ("rule_get", "Gets rule details.")
|
|
107
|
+
RULE_UPDATE = ("rule_update", "Updates an existing rule.")
|
|
108
|
+
RULE_DELETE = ("rule_delete", "Removes a rule.")
|
|
104
109
|
|
|
105
110
|
# Documentation
|
|
106
|
-
DOC_CREATE = ("doc_create", "
|
|
107
|
-
DOC_LIST = ("doc_list", "
|
|
108
|
-
DOC_GET = ("doc_get", "
|
|
109
|
-
DOC_UPDATE = ("doc_update", "
|
|
110
|
-
DOC_DELETE = ("doc_delete", "
|
|
111
|
-
DOC_SEARCH = ("doc_search", "
|
|
112
|
-
DOC_ROOTS = ("doc_roots", "
|
|
113
|
-
DOC_RECENT = ("doc_recent", "
|
|
114
|
-
DOC_ANALYTICS = ("doc_analytics", "
|
|
115
|
-
DOC_CHILDREN = ("doc_children", "
|
|
116
|
-
DOC_TREE = ("doc_tree", "
|
|
117
|
-
DOC_FULL_TREE = ("doc_full_tree", "
|
|
118
|
-
DOC_MOVE = ("doc_move", "
|
|
119
|
-
DOC_PUBLISH = ("doc_publish", "
|
|
120
|
-
DOC_VERSION = ("doc_version", "
|
|
121
|
-
DOC_DUPLICATE = ("doc_duplicate", "
|
|
122
|
-
|
|
123
|
-
HELP = ("knowledge_help", "
|
|
111
|
+
DOC_CREATE = ("doc_create", "Creates a documentation item.")
|
|
112
|
+
DOC_LIST = ("doc_list", "Lists documentation items with filters.")
|
|
113
|
+
DOC_GET = ("doc_get", "Gets documentation item details.")
|
|
114
|
+
DOC_UPDATE = ("doc_update", "Updates a documentation item.")
|
|
115
|
+
DOC_DELETE = ("doc_delete", "Removes a documentation item.")
|
|
116
|
+
DOC_SEARCH = ("doc_search", "Searches documentation items by text.")
|
|
117
|
+
DOC_ROOTS = ("doc_roots", "Lists available root documents.")
|
|
118
|
+
DOC_RECENT = ("doc_recent", "Lists recently accessed documents.")
|
|
119
|
+
DOC_ANALYTICS = ("doc_analytics", "Returns document analytics.")
|
|
120
|
+
DOC_CHILDREN = ("doc_children", "Lists child documents of an item.")
|
|
121
|
+
DOC_TREE = ("doc_tree", "Retrieves the direct tree of a document.")
|
|
122
|
+
DOC_FULL_TREE = ("doc_full_tree", "Retrieves the full documentation tree.")
|
|
123
|
+
DOC_MOVE = ("doc_move", "Moves a document to another parent.")
|
|
124
|
+
DOC_PUBLISH = ("doc_publish", "Changes publication status of a document.")
|
|
125
|
+
DOC_VERSION = ("doc_version", "Generates or retrieves a document version.")
|
|
126
|
+
DOC_DUPLICATE = ("doc_duplicate", "Duplicates an existing document.")
|
|
127
|
+
|
|
128
|
+
HELP = ("knowledge_help", "Shows available actions and their uses.")
|
|
124
129
|
|
|
125
130
|
@classmethod
|
|
126
131
|
def choices(cls) -> List[str]:
|
|
@@ -129,7 +134,7 @@ class KnowledgeAction(str, Enum):
|
|
|
129
134
|
@classmethod
|
|
130
135
|
def formatted_help(cls) -> str:
|
|
131
136
|
lines = [
|
|
132
|
-
"| **
|
|
137
|
+
"| **Action** | **Description** |",
|
|
133
138
|
"| --- | --- |",
|
|
134
139
|
]
|
|
135
140
|
for member in cls:
|
|
@@ -137,7 +142,7 @@ class KnowledgeAction(str, Enum):
|
|
|
137
142
|
return "\n".join(lines)
|
|
138
143
|
|
|
139
144
|
|
|
140
|
-
ACTION_FIELD_DESCRIPTION = "
|
|
145
|
+
ACTION_FIELD_DESCRIPTION = "Knowledge action. Choose one of the values: " + ", ".join(
|
|
141
146
|
f"`{member.value}` ({member.description.rstrip('.')})."
|
|
142
147
|
for member in KnowledgeAction
|
|
143
148
|
)
|
|
@@ -154,165 +159,158 @@ _ALLOWED_DOC_TYPES = {
|
|
|
154
159
|
class KnowledgeRequest(ToolRequest):
|
|
155
160
|
action: KnowledgeAction = Field(description=ACTION_FIELD_DESCRIPTION)
|
|
156
161
|
id: Optional[UUIDStr] = Field(
|
|
157
|
-
default=None, description="
|
|
162
|
+
default=None, description="Primary resource ID (UUID)."
|
|
158
163
|
)
|
|
159
|
-
limit: int = Field(default=20, ge=1, le=100, description="
|
|
164
|
+
limit: int = Field(default=20, ge=1, le=100, description="Result limit.")
|
|
160
165
|
offset: int = Field(
|
|
161
|
-
default=0, ge=0, description="
|
|
162
|
-
)
|
|
163
|
-
team_id: Optional[UUIDStr] = Field(
|
|
164
|
-
default=None, description="ID do time para filtros (UUID)."
|
|
166
|
+
default=0, ge=0, description="Pagination offset (when supported)."
|
|
165
167
|
)
|
|
166
168
|
board_id: Optional[UUIDStr] = Field(
|
|
167
|
-
default=None, description="
|
|
169
|
+
default=None, description="Associated board ID (UUID)."
|
|
168
170
|
)
|
|
169
171
|
sprint_id: Optional[UUIDStr] = Field(
|
|
170
|
-
default=None, description="
|
|
172
|
+
default=None, description="Associated sprint ID (UUID)."
|
|
171
173
|
)
|
|
172
174
|
epic_id: Optional[UUIDStr] = Field(
|
|
173
|
-
default=None, description="
|
|
175
|
+
default=None, description="Associated epic ID (UUID)."
|
|
174
176
|
)
|
|
175
|
-
query: Optional[str] = Field(default=None, description="
|
|
177
|
+
query: Optional[str] = Field(default=None, description="Filter/search term.")
|
|
176
178
|
return_content: Optional[bool] = Field(
|
|
177
|
-
default=None, description="
|
|
179
|
+
default=None, description="Return full content."
|
|
178
180
|
)
|
|
179
181
|
return_description: Optional[bool] = Field(
|
|
180
|
-
default=None, description="
|
|
182
|
+
default=None, description="Return full description."
|
|
181
183
|
)
|
|
182
184
|
return_metadata: Optional[bool] = Field(
|
|
183
|
-
default=None, description="
|
|
185
|
+
default=None, description="Return full metadata."
|
|
184
186
|
)
|
|
185
187
|
|
|
186
188
|
# Work item fields
|
|
187
|
-
work_title: Optional[TitleStr] = Field(
|
|
188
|
-
default=None, description="Título do work item."
|
|
189
|
-
)
|
|
189
|
+
work_title: Optional[TitleStr] = Field(default=None, description="Work item title.")
|
|
190
190
|
work_description: Optional[MarkdownStr] = Field(
|
|
191
|
-
default=None, description="
|
|
191
|
+
default=None, description="Work item description (Markdown)."
|
|
192
192
|
)
|
|
193
193
|
work_type: Optional[str] = Field(
|
|
194
194
|
default="task",
|
|
195
|
-
description="
|
|
195
|
+
description="Work item type (epic, feature, story, task, subtask, bug).",
|
|
196
196
|
)
|
|
197
197
|
work_status: Optional[str] = Field(
|
|
198
198
|
default=None,
|
|
199
|
-
description="
|
|
199
|
+
description="Work item status (backlog, todo, in_progress, review, testing, done, cancelled).",
|
|
200
200
|
)
|
|
201
201
|
work_priority: Optional[str] = Field(
|
|
202
202
|
default=None,
|
|
203
|
-
description="
|
|
203
|
+
description="Work item priority (critical, high, medium, low).",
|
|
204
204
|
)
|
|
205
205
|
story_points: Optional[int] = Field(
|
|
206
206
|
default=None, ge=0, le=100, description="Story points (0-100)."
|
|
207
207
|
)
|
|
208
208
|
assignee_id: Optional[UUIDStr] = Field(
|
|
209
|
-
default=None, description="ID
|
|
209
|
+
default=None, description="Assignee ID (UUID)."
|
|
210
210
|
)
|
|
211
211
|
parent_id: Optional[UUIDStr] = Field(
|
|
212
|
-
default=None, description="
|
|
212
|
+
default=None, description="Parent item ID (UUID)."
|
|
213
213
|
)
|
|
214
214
|
work_due_date: Optional[DateTimeStr] = Field(
|
|
215
|
-
default=None, description="
|
|
215
|
+
default=None, description="Work item due date (ISO 8601)."
|
|
216
216
|
)
|
|
217
217
|
work_tags: Optional[List[TagStr]] = Field(
|
|
218
|
-
default=None, description="
|
|
218
|
+
default=None, description="Work item tags."
|
|
219
219
|
)
|
|
220
220
|
work_item_ids: Optional[List[UUIDStr]] = Field(
|
|
221
221
|
default=None,
|
|
222
|
-
description="
|
|
222
|
+
description="List of work item IDs for batch operations (UUIDs).",
|
|
223
223
|
)
|
|
224
224
|
|
|
225
225
|
# Mode fields
|
|
226
226
|
mode_id: Optional[UUIDStr] = Field(
|
|
227
|
-
default=None, description="
|
|
227
|
+
default=None, description="Related mode ID (UUID)."
|
|
228
228
|
)
|
|
229
|
-
mode_name: Optional[TitleStr] = Field(default=None, description="
|
|
229
|
+
mode_name: Optional[TitleStr] = Field(default=None, description="Mode name.")
|
|
230
230
|
mode_description: Optional[DescriptionStr] = Field(
|
|
231
|
-
default=None, description="
|
|
231
|
+
default=None, description="Mode description."
|
|
232
232
|
)
|
|
233
233
|
mode_content: Optional[MarkdownStr] = Field(
|
|
234
|
-
default=None, description="
|
|
234
|
+
default=None, description="Mode content (Markdown)."
|
|
235
235
|
)
|
|
236
236
|
mode_is_default: Optional[bool] = Field(
|
|
237
|
-
default=None, description="
|
|
237
|
+
default=None, description="Indicates if the mode is default."
|
|
238
238
|
)
|
|
239
239
|
mode_metadata: Optional[MarkdownStr] = Field(
|
|
240
|
-
default=None, description="
|
|
240
|
+
default=None, description="Mode metadata for AI processing."
|
|
241
241
|
)
|
|
242
242
|
|
|
243
243
|
# Rule fields
|
|
244
244
|
rule_id: Optional[UUIDStr] = Field(
|
|
245
|
-
default=None, description="
|
|
245
|
+
default=None, description="Related rule ID (UUID)."
|
|
246
246
|
)
|
|
247
|
-
rule_name: Optional[TitleStr] = Field(default=None, description="
|
|
247
|
+
rule_name: Optional[TitleStr] = Field(default=None, description="Rule name.")
|
|
248
248
|
rule_description: Optional[DescriptionStr] = Field(
|
|
249
|
-
default=None, description="
|
|
249
|
+
default=None, description="Rule description."
|
|
250
250
|
)
|
|
251
251
|
rule_content: Optional[MarkdownStr] = Field(
|
|
252
|
-
default=None, description="
|
|
252
|
+
default=None, description="Rule content (Markdown)."
|
|
253
253
|
)
|
|
254
|
-
rule_is_default: Optional[bool] = Field(default=None, description="
|
|
254
|
+
rule_is_default: Optional[bool] = Field(default=None, description="Default rule.")
|
|
255
255
|
rule_metadata: Optional[MarkdownStr] = Field(
|
|
256
|
-
default=None, description="
|
|
256
|
+
default=None, description="Rule metadata for AI processing."
|
|
257
257
|
)
|
|
258
258
|
|
|
259
259
|
# Documentation fields
|
|
260
260
|
doc_title: Optional[TitleStr] = Field(
|
|
261
|
-
default=None, description="
|
|
261
|
+
default=None, description="Documentation title."
|
|
262
262
|
)
|
|
263
263
|
doc_description: Optional[DescriptionStr] = Field(
|
|
264
|
-
default=None, description="
|
|
264
|
+
default=None, description="Documentation description."
|
|
265
265
|
)
|
|
266
266
|
doc_content: Optional[MarkdownStr] = Field(
|
|
267
|
-
default=None, description="
|
|
267
|
+
default=None, description="Documentation content (Markdown)."
|
|
268
268
|
)
|
|
269
269
|
doc_status: Optional[str] = Field(
|
|
270
270
|
default=None,
|
|
271
|
-
description="
|
|
271
|
+
description="Documentation status (draft, review, published, archived).",
|
|
272
272
|
)
|
|
273
273
|
doc_type: Optional[str] = Field(
|
|
274
|
-
default=None,
|
|
274
|
+
default=None,
|
|
275
|
+
description="Documentation type. Values: folder, page, api_doc, guide.",
|
|
275
276
|
)
|
|
276
277
|
doc_language: Optional[LanguageStr] = Field(
|
|
277
|
-
default=None, description="
|
|
278
|
+
default=None, description="Documentation language (e.g.: pt, en, pt-BR)."
|
|
278
279
|
)
|
|
279
280
|
doc_parent_id: Optional[UUIDStr] = Field(
|
|
280
|
-
default=None, description="
|
|
281
|
-
)
|
|
282
|
-
doc_team_id: Optional[UUIDStr] = Field(
|
|
283
|
-
default=None, description="ID do time responsável pela documentação (UUID)."
|
|
281
|
+
default=None, description="Parent document ID (UUID)."
|
|
284
282
|
)
|
|
285
283
|
doc_owner_id: Optional[UUIDStr] = Field(
|
|
286
|
-
default=None, description="ID
|
|
284
|
+
default=None, description="Owner ID (UUID)."
|
|
287
285
|
)
|
|
288
286
|
doc_reviewer_id: Optional[UUIDStr] = Field(
|
|
289
|
-
default=None, description="ID
|
|
287
|
+
default=None, description="Reviewer ID (UUID)."
|
|
290
288
|
)
|
|
291
289
|
doc_version: Optional[VersionStr] = Field(
|
|
292
|
-
default=None, description="
|
|
290
|
+
default=None, description="Document version (e.g.: 1.0, 2.1.3)."
|
|
293
291
|
)
|
|
294
|
-
doc_category: Optional[CategoryStr] = Field(default=None, description="
|
|
292
|
+
doc_category: Optional[CategoryStr] = Field(default=None, description="Category.")
|
|
295
293
|
doc_tags: Optional[List[TagStr]] = Field(default=None, description="Tags.")
|
|
296
294
|
doc_position: Optional[int] = Field(
|
|
297
|
-
default=None, ge=0, description="
|
|
295
|
+
default=None, ge=0, description="Desired position when moving documents."
|
|
298
296
|
)
|
|
299
297
|
doc_emoji: Optional[EmojiStr] = Field(
|
|
300
|
-
default=None, description="Emoji
|
|
298
|
+
default=None, description="Emoji displayed with the document."
|
|
301
299
|
)
|
|
302
300
|
doc_emote: Optional[EmojiStr] = Field(
|
|
303
|
-
default=None, description="Alias
|
|
301
|
+
default=None, description="Alias for emoji, kept for compatibility."
|
|
304
302
|
)
|
|
305
303
|
doc_keywords: Optional[List[TagStr]] = Field(
|
|
306
|
-
default=None, description="
|
|
304
|
+
default=None, description="Keywords for search."
|
|
307
305
|
)
|
|
308
306
|
doc_is_public: Optional[bool] = Field(
|
|
309
|
-
default=None, description="
|
|
307
|
+
default=None, description="Whether the document is public."
|
|
310
308
|
)
|
|
311
309
|
|
|
312
310
|
|
|
313
311
|
class KnowledgeTool(Tool):
|
|
314
312
|
name = "knowledge"
|
|
315
|
-
description = "
|
|
313
|
+
description = "Fenix Cloud knowledge operations (Work Items, Boards, Sprints, Modes, Rules, Docs)."
|
|
316
314
|
request_model = KnowledgeRequest
|
|
317
315
|
|
|
318
316
|
def __init__(self, context: AppContext):
|
|
@@ -336,7 +334,7 @@ class KnowledgeTool(Tool):
|
|
|
336
334
|
if action.value.startswith("doc_"):
|
|
337
335
|
return await self._run_doc(payload)
|
|
338
336
|
return text(
|
|
339
|
-
"❌
|
|
337
|
+
"❌ Invalid action for knowledge.\n\nChoose one of the values:\n"
|
|
340
338
|
+ "\n".join(f"- `{value}`" for value in KnowledgeAction.choices())
|
|
341
339
|
)
|
|
342
340
|
|
|
@@ -347,9 +345,7 @@ class KnowledgeTool(Tool):
|
|
|
347
345
|
action = payload.action
|
|
348
346
|
if action is KnowledgeAction.WORK_CREATE:
|
|
349
347
|
if not payload.work_title:
|
|
350
|
-
return text("❌
|
|
351
|
-
if not payload.team_id:
|
|
352
|
-
return text("❌ Informe team_id para criar o item.")
|
|
348
|
+
return text("❌ Provide work_title to create the item.")
|
|
353
349
|
work = await self._service.work_create(
|
|
354
350
|
{
|
|
355
351
|
"title": payload.work_title,
|
|
@@ -362,12 +358,11 @@ class KnowledgeTool(Tool):
|
|
|
362
358
|
"sprint_id": payload.sprint_id,
|
|
363
359
|
"board_id": payload.board_id,
|
|
364
360
|
"parent_id": payload.parent_id,
|
|
365
|
-
"team_id": payload.team_id,
|
|
366
361
|
"due_date": payload.work_due_date,
|
|
367
362
|
"tags": payload.work_tags,
|
|
368
363
|
}
|
|
369
364
|
)
|
|
370
|
-
return text(_format_work(work, header="✅ Work item
|
|
365
|
+
return text(_format_work(work, header="✅ Work item created"))
|
|
371
366
|
|
|
372
367
|
if action is KnowledgeAction.WORK_LIST:
|
|
373
368
|
items = await self._service.work_list(
|
|
@@ -380,19 +375,19 @@ class KnowledgeTool(Tool):
|
|
|
380
375
|
board=payload.board_id,
|
|
381
376
|
)
|
|
382
377
|
if not items:
|
|
383
|
-
return text("🎯
|
|
378
|
+
return text("🎯 No work items found.")
|
|
384
379
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
385
380
|
return text(f"🎯 **Work items ({len(items)}):**\n\n{body}")
|
|
386
381
|
|
|
387
382
|
if action is KnowledgeAction.WORK_GET:
|
|
388
383
|
if not payload.id:
|
|
389
|
-
return text("❌
|
|
384
|
+
return text("❌ Provide the work item ID.")
|
|
390
385
|
work = await self._service.work_get(payload.id)
|
|
391
|
-
return text(_format_work(work, header="🎯
|
|
386
|
+
return text(_format_work(work, header="🎯 Work item details"))
|
|
392
387
|
|
|
393
388
|
if action is KnowledgeAction.WORK_UPDATE:
|
|
394
389
|
if not payload.id:
|
|
395
|
-
return text("❌
|
|
390
|
+
return text("❌ Provide the work item ID.")
|
|
396
391
|
work = await self._service.work_update(
|
|
397
392
|
payload.id,
|
|
398
393
|
{
|
|
@@ -406,102 +401,97 @@ class KnowledgeTool(Tool):
|
|
|
406
401
|
"sprint_id": payload.sprint_id,
|
|
407
402
|
"board_id": payload.board_id,
|
|
408
403
|
"parent_id": payload.parent_id,
|
|
409
|
-
"team_id": payload.team_id,
|
|
410
404
|
"due_date": payload.work_due_date,
|
|
411
405
|
"tags": payload.work_tags,
|
|
412
406
|
},
|
|
413
407
|
)
|
|
414
|
-
return text(_format_work(work, header="✅ Work item
|
|
408
|
+
return text(_format_work(work, header="✅ Work item updated"))
|
|
415
409
|
|
|
416
410
|
if action is KnowledgeAction.WORK_DELETE:
|
|
417
411
|
if not payload.id:
|
|
418
|
-
return text("❌
|
|
412
|
+
return text("❌ Provide the work item ID.")
|
|
419
413
|
await self._service.work_delete(payload.id)
|
|
420
|
-
return text(f"🗑️ Work item {payload.id}
|
|
414
|
+
return text(f"🗑️ Work item {payload.id} removed.")
|
|
421
415
|
|
|
422
416
|
if action is KnowledgeAction.WORK_BACKLOG:
|
|
423
|
-
|
|
424
|
-
return text("❌ Informe team_id para consultar o backlog.")
|
|
425
|
-
items = await self._service.work_backlog(team_id=payload.team_id)
|
|
417
|
+
items = await self._service.work_backlog()
|
|
426
418
|
if not items:
|
|
427
|
-
return text("📋 Backlog
|
|
419
|
+
return text("📋 Backlog empty.")
|
|
428
420
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
429
421
|
return text(f"📋 **Backlog ({len(items)}):**\n\n{body}")
|
|
430
422
|
|
|
431
423
|
if action is KnowledgeAction.WORK_SEARCH:
|
|
432
|
-
|
|
433
|
-
|
|
424
|
+
query = sanitize_null(payload.query)
|
|
425
|
+
if not query:
|
|
426
|
+
return text("❌ Provide query to search work items.")
|
|
434
427
|
items = await self._service.work_search(
|
|
435
|
-
query=
|
|
436
|
-
team_id=payload.team_id,
|
|
428
|
+
query=query,
|
|
437
429
|
limit=payload.limit,
|
|
438
430
|
)
|
|
439
431
|
if not items:
|
|
440
|
-
return text("🔍
|
|
432
|
+
return text("🔍 No work items found.")
|
|
441
433
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
442
|
-
return text(f"🔍 **
|
|
434
|
+
return text(f"🔍 **Results ({len(items)}):**\n\n{body}")
|
|
443
435
|
|
|
444
436
|
if action is KnowledgeAction.WORK_ANALYTICS:
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
analytics = await self._service.work_analytics(team_id=payload.team_id)
|
|
448
|
-
lines = ["📊 **Analytics de Work Items**"]
|
|
437
|
+
analytics = await self._service.work_analytics()
|
|
438
|
+
lines = ["📊 **Work Items Analytics**"]
|
|
449
439
|
for key, value in analytics.items():
|
|
450
440
|
lines.append(f"- {key}: {value}")
|
|
451
441
|
return text("\n".join(lines))
|
|
452
442
|
|
|
453
443
|
if action is KnowledgeAction.WORK_BY_BOARD:
|
|
454
444
|
if not payload.board_id:
|
|
455
|
-
return text("❌
|
|
445
|
+
return text("❌ Provide board_id to list items.")
|
|
456
446
|
items = await self._service.work_by_board(board_id=payload.board_id)
|
|
457
447
|
if not items:
|
|
458
|
-
return text("🗂️
|
|
448
|
+
return text("🗂️ No work items for the specified board.")
|
|
459
449
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
460
|
-
return text(f"🗂️ **
|
|
450
|
+
return text(f"🗂️ **Board items ({len(items)}):**\n\n{body}")
|
|
461
451
|
|
|
462
452
|
if action is KnowledgeAction.WORK_BY_SPRINT:
|
|
463
453
|
if not payload.sprint_id:
|
|
464
|
-
return text("❌
|
|
454
|
+
return text("❌ Provide sprint_id to list items.")
|
|
465
455
|
items = await self._service.work_by_sprint(sprint_id=payload.sprint_id)
|
|
466
456
|
if not items:
|
|
467
|
-
return text("🏃
|
|
457
|
+
return text("🏃 No items linked to the specified sprint.")
|
|
468
458
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
469
|
-
return text(f"🏃 **
|
|
459
|
+
return text(f"🏃 **Sprint work items ({len(items)}):**\n\n{body}")
|
|
470
460
|
|
|
471
461
|
if action is KnowledgeAction.WORK_BY_EPIC:
|
|
472
462
|
if not payload.epic_id:
|
|
473
|
-
return text("❌
|
|
463
|
+
return text("❌ Provide epic_id to list items.")
|
|
474
464
|
items = await self._service.work_by_epic(epic_id=payload.epic_id)
|
|
475
465
|
if not items:
|
|
476
|
-
return text("📦
|
|
466
|
+
return text("📦 No items linked to the specified epic.")
|
|
477
467
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
478
|
-
return text(f"📦 **
|
|
468
|
+
return text(f"📦 **Epic work items ({len(items)}):**\n\n{body}")
|
|
479
469
|
|
|
480
470
|
if action is KnowledgeAction.WORK_CHILDREN:
|
|
481
471
|
if not payload.id:
|
|
482
|
-
return text("❌
|
|
472
|
+
return text("❌ Provide the parent work item ID.")
|
|
483
473
|
items = await self._service.work_children(payload.id)
|
|
484
474
|
if not items:
|
|
485
|
-
return text("👶
|
|
475
|
+
return text("👶 No child items found.")
|
|
486
476
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
487
|
-
return text(f"👶 **
|
|
477
|
+
return text(f"👶 **Child work items ({len(items)}):**\n\n{body}")
|
|
488
478
|
|
|
489
479
|
if action is KnowledgeAction.WORK_STATUS_UPDATE:
|
|
490
480
|
if not payload.id:
|
|
491
|
-
return text("❌
|
|
481
|
+
return text("❌ Provide the work item ID.")
|
|
492
482
|
if not payload.work_status:
|
|
493
|
-
return text("❌
|
|
483
|
+
return text("❌ Provide work_status to update.")
|
|
494
484
|
work = await self._service.work_update_status(
|
|
495
485
|
payload.id,
|
|
496
486
|
{"status": payload.work_status},
|
|
497
487
|
)
|
|
498
|
-
return text(_format_work(work, header="✅ Status
|
|
488
|
+
return text(_format_work(work, header="✅ Status updated"))
|
|
499
489
|
|
|
500
490
|
if action is KnowledgeAction.WORK_ASSIGN_SPRINT:
|
|
501
491
|
if not payload.sprint_id:
|
|
502
|
-
return text("❌
|
|
492
|
+
return text("❌ Provide sprint_id to assign items.")
|
|
503
493
|
if not payload.work_item_ids:
|
|
504
|
-
return text("❌
|
|
494
|
+
return text("❌ Provide work_item_ids with the list of IDs.")
|
|
505
495
|
await self._service.work_assign_to_sprint(
|
|
506
496
|
{
|
|
507
497
|
"sprint_id": payload.sprint_id,
|
|
@@ -509,10 +499,10 @@ class KnowledgeTool(Tool):
|
|
|
509
499
|
}
|
|
510
500
|
)
|
|
511
501
|
count = len(payload.work_item_ids)
|
|
512
|
-
return text(f"✅ {count} work item(s)
|
|
502
|
+
return text(f"✅ {count} work item(s) assigned to sprint.")
|
|
513
503
|
|
|
514
504
|
return text(
|
|
515
|
-
"❌
|
|
505
|
+
"❌ Unsupported work item action.\n\nChoose one of the values:\n"
|
|
516
506
|
+ "\n".join(
|
|
517
507
|
f"- `{value}`"
|
|
518
508
|
for value in KnowledgeAction.choices()
|
|
@@ -530,46 +520,44 @@ class KnowledgeTool(Tool):
|
|
|
530
520
|
limit=payload.limit, offset=payload.offset
|
|
531
521
|
)
|
|
532
522
|
if not boards:
|
|
533
|
-
return text("🗂️
|
|
523
|
+
return text("🗂️ No boards found.")
|
|
534
524
|
body = "\n\n".join(_format_board(board) for board in boards)
|
|
535
525
|
return text(f"🗂️ **Boards ({len(boards)}):**\n\n{body}")
|
|
536
526
|
|
|
537
527
|
if action is KnowledgeAction.BOARD_BY_TEAM:
|
|
538
|
-
|
|
539
|
-
return text("❌ Informe team_id para listar boards do time.")
|
|
540
|
-
boards = await self._service.board_list_by_team(payload.team_id)
|
|
528
|
+
boards = await self._service.board_list_by_team()
|
|
541
529
|
if not boards:
|
|
542
|
-
return text("🗂️
|
|
530
|
+
return text("🗂️ No boards registered for the team.")
|
|
543
531
|
body = "\n\n".join(_format_board(board) for board in boards)
|
|
544
|
-
return text(f"🗂️ **
|
|
532
|
+
return text(f"🗂️ **Team boards ({len(boards)}):**\n\n{body}")
|
|
545
533
|
|
|
546
534
|
if action is KnowledgeAction.BOARD_FAVORITES:
|
|
547
535
|
boards = await self._service.board_favorites()
|
|
548
536
|
if not boards:
|
|
549
|
-
return text("⭐
|
|
537
|
+
return text("⭐ No favorite boards registered.")
|
|
550
538
|
body = "\n\n".join(_format_board(board) for board in boards)
|
|
551
|
-
return text(f"⭐ **
|
|
539
|
+
return text(f"⭐ **Favorite boards ({len(boards)}):**\n\n{body}")
|
|
552
540
|
|
|
553
541
|
if action is KnowledgeAction.BOARD_GET:
|
|
554
542
|
if not payload.board_id:
|
|
555
|
-
return text("❌
|
|
543
|
+
return text("❌ Provide board_id to get details.")
|
|
556
544
|
board = await self._service.board_get(payload.board_id)
|
|
557
|
-
return text(_format_board(board, header="🗂️
|
|
545
|
+
return text(_format_board(board, header="🗂️ Board details"))
|
|
558
546
|
|
|
559
547
|
if action is KnowledgeAction.BOARD_COLUMNS:
|
|
560
548
|
if not payload.board_id:
|
|
561
|
-
return text("❌
|
|
549
|
+
return text("❌ Provide board_id to list columns.")
|
|
562
550
|
columns = await self._service.board_columns(payload.board_id)
|
|
563
551
|
if not columns:
|
|
564
|
-
return text("📊 Board
|
|
552
|
+
return text("📊 Board has no columns registered.")
|
|
565
553
|
body = "\n".join(
|
|
566
|
-
f"- {col.get('name', '
|
|
554
|
+
f"- {col.get('name', 'Unnamed')} (ID: {col.get('id')})"
|
|
567
555
|
for col in columns
|
|
568
556
|
)
|
|
569
|
-
return text(f"📊 **
|
|
557
|
+
return text(f"📊 **Board columns:**\n{body}")
|
|
570
558
|
|
|
571
559
|
return text(
|
|
572
|
-
"❌
|
|
560
|
+
"❌ Unsupported board action.\n\nChoose one of the values:\n"
|
|
573
561
|
+ "\n".join(
|
|
574
562
|
f"- `{value}`"
|
|
575
563
|
for value in KnowledgeAction.choices()
|
|
@@ -587,44 +575,40 @@ class KnowledgeTool(Tool):
|
|
|
587
575
|
limit=payload.limit, offset=payload.offset
|
|
588
576
|
)
|
|
589
577
|
if not sprints:
|
|
590
|
-
return text("🏃
|
|
578
|
+
return text("🏃 No sprints found.")
|
|
591
579
|
body = "\n\n".join(_format_sprint(sprint) for sprint in sprints)
|
|
592
580
|
return text(f"🏃 **Sprints ({len(sprints)}):**\n\n{body}")
|
|
593
581
|
|
|
594
582
|
if action is KnowledgeAction.SPRINT_BY_TEAM:
|
|
595
|
-
|
|
596
|
-
return text("❌ Informe team_id para listar sprints do time.")
|
|
597
|
-
sprints = await self._service.sprint_list_by_team(payload.team_id)
|
|
583
|
+
sprints = await self._service.sprint_list_by_team()
|
|
598
584
|
if not sprints:
|
|
599
|
-
return text("🏃
|
|
585
|
+
return text("🏃 No sprints registered for the team.")
|
|
600
586
|
body = "\n\n".join(_format_sprint(sprint) for sprint in sprints)
|
|
601
|
-
return text(f"🏃 **
|
|
587
|
+
return text(f"🏃 **Team sprints ({len(sprints)}):**\n\n{body}")
|
|
602
588
|
|
|
603
589
|
if action is KnowledgeAction.SPRINT_ACTIVE:
|
|
604
|
-
|
|
605
|
-
return text("❌ Informe team_id para consultar o sprint ativo.")
|
|
606
|
-
sprint = await self._service.sprint_active(payload.team_id)
|
|
590
|
+
sprint = await self._service.sprint_active()
|
|
607
591
|
if not sprint:
|
|
608
|
-
return text("⏳
|
|
609
|
-
return text(_format_sprint(sprint, header="⏳
|
|
592
|
+
return text("⏳ No active sprint at the moment.")
|
|
593
|
+
return text(_format_sprint(sprint, header="⏳ Active sprint"))
|
|
610
594
|
|
|
611
595
|
if action is KnowledgeAction.SPRINT_GET:
|
|
612
596
|
if not payload.sprint_id:
|
|
613
|
-
return text("❌
|
|
597
|
+
return text("❌ Provide sprint_id to get details.")
|
|
614
598
|
sprint = await self._service.sprint_get(payload.sprint_id)
|
|
615
|
-
return text(_format_sprint(sprint, header="🏃
|
|
599
|
+
return text(_format_sprint(sprint, header="🏃 Sprint details"))
|
|
616
600
|
|
|
617
601
|
if action is KnowledgeAction.SPRINT_WORK_ITEMS:
|
|
618
602
|
if not payload.sprint_id:
|
|
619
|
-
return text("❌
|
|
603
|
+
return text("❌ Provide sprint_id to list items.")
|
|
620
604
|
items = await self._service.sprint_work_items(payload.sprint_id)
|
|
621
605
|
if not items:
|
|
622
|
-
return text("🏃
|
|
606
|
+
return text("🏃 No items linked to the specified sprint.")
|
|
623
607
|
body = "\n\n".join(_format_work(item) for item in items)
|
|
624
|
-
return text(f"🏃 **
|
|
608
|
+
return text(f"🏃 **Sprint items ({len(items)}):**\n\n{body}")
|
|
625
609
|
|
|
626
610
|
return text(
|
|
627
|
-
"❌
|
|
611
|
+
"❌ Unsupported sprint action.\n\nChoose one of the values:\n"
|
|
628
612
|
+ "\n".join(
|
|
629
613
|
f"- `{value}`"
|
|
630
614
|
for value in KnowledgeAction.choices()
|
|
@@ -639,7 +623,7 @@ class KnowledgeTool(Tool):
|
|
|
639
623
|
action = payload.action
|
|
640
624
|
if action is KnowledgeAction.MODE_CREATE:
|
|
641
625
|
if not payload.mode_name:
|
|
642
|
-
return text("❌
|
|
626
|
+
return text("❌ Provide mode_name to create the mode.")
|
|
643
627
|
mode = await self._service.mode_create(
|
|
644
628
|
{
|
|
645
629
|
"name": payload.mode_name,
|
|
@@ -649,7 +633,7 @@ class KnowledgeTool(Tool):
|
|
|
649
633
|
"metadata": payload.mode_metadata,
|
|
650
634
|
}
|
|
651
635
|
)
|
|
652
|
-
return text(_format_mode(mode, header="✅
|
|
636
|
+
return text(_format_mode(mode, header="✅ Mode created"))
|
|
653
637
|
|
|
654
638
|
if action is KnowledgeAction.MODE_LIST:
|
|
655
639
|
modes = await self._service.mode_list(
|
|
@@ -658,23 +642,23 @@ class KnowledgeTool(Tool):
|
|
|
658
642
|
return_metadata=payload.return_metadata,
|
|
659
643
|
)
|
|
660
644
|
if not modes:
|
|
661
|
-
return text("🎭
|
|
645
|
+
return text("🎭 No modes found.")
|
|
662
646
|
body = "\n\n".join(_format_mode(mode) for mode in modes)
|
|
663
647
|
return text(f"🎭 **Modes ({len(modes)}):**\n\n{body}")
|
|
664
648
|
|
|
665
649
|
if action is KnowledgeAction.MODE_GET:
|
|
666
650
|
if not payload.mode_id:
|
|
667
|
-
return text("❌
|
|
651
|
+
return text("❌ Provide mode_id to get details.")
|
|
668
652
|
mode = await self._service.mode_get(
|
|
669
653
|
payload.mode_id,
|
|
670
654
|
return_description=payload.return_description,
|
|
671
655
|
return_metadata=payload.return_metadata,
|
|
672
656
|
)
|
|
673
|
-
return text(_format_mode(mode, header="🎭
|
|
657
|
+
return text(_format_mode(mode, header="🎭 Mode details"))
|
|
674
658
|
|
|
675
659
|
if action is KnowledgeAction.MODE_UPDATE:
|
|
676
660
|
if not payload.mode_id:
|
|
677
|
-
return text("❌
|
|
661
|
+
return text("❌ Provide mode_id to update.")
|
|
678
662
|
mode = await self._service.mode_update(
|
|
679
663
|
payload.mode_id,
|
|
680
664
|
{
|
|
@@ -685,53 +669,53 @@ class KnowledgeTool(Tool):
|
|
|
685
669
|
"metadata": payload.mode_metadata,
|
|
686
670
|
},
|
|
687
671
|
)
|
|
688
|
-
return text(_format_mode(mode, header="✅
|
|
672
|
+
return text(_format_mode(mode, header="✅ Mode updated"))
|
|
689
673
|
|
|
690
674
|
if action is KnowledgeAction.MODE_DELETE:
|
|
691
675
|
if not payload.mode_id:
|
|
692
|
-
return text("❌
|
|
676
|
+
return text("❌ Provide mode_id to remove.")
|
|
693
677
|
await self._service.mode_delete(payload.mode_id)
|
|
694
|
-
return text(f"🗑️
|
|
678
|
+
return text(f"🗑️ Mode {payload.mode_id} removed.")
|
|
695
679
|
|
|
696
680
|
if action is KnowledgeAction.MODE_RULE_ADD:
|
|
697
681
|
if not payload.mode_id or not payload.rule_id:
|
|
698
|
-
return text("❌
|
|
682
|
+
return text("❌ Provide mode_id and rule_id to associate.")
|
|
699
683
|
link = await self._service.mode_rule_add(payload.mode_id, payload.rule_id)
|
|
700
684
|
return text(
|
|
701
685
|
"\n".join(
|
|
702
686
|
[
|
|
703
|
-
"🔗 **
|
|
704
|
-
f"
|
|
705
|
-
f"
|
|
687
|
+
"🔗 **Rule associated with mode!**",
|
|
688
|
+
f"Mode: {link.get('modeId', payload.mode_id)}",
|
|
689
|
+
f"Rule: {link.get('ruleId', payload.rule_id)}",
|
|
706
690
|
]
|
|
707
691
|
)
|
|
708
692
|
)
|
|
709
693
|
|
|
710
694
|
if action is KnowledgeAction.MODE_RULE_REMOVE:
|
|
711
695
|
if not payload.mode_id or not payload.rule_id:
|
|
712
|
-
return text("❌
|
|
696
|
+
return text("❌ Provide mode_id and rule_id to remove the association.")
|
|
713
697
|
await self._service.mode_rule_remove(payload.mode_id, payload.rule_id)
|
|
714
|
-
return text("🔗
|
|
698
|
+
return text("🔗 Association removed.")
|
|
715
699
|
|
|
716
700
|
if action is KnowledgeAction.MODE_RULES:
|
|
717
701
|
if payload.mode_id:
|
|
718
702
|
rules = await self._service.mode_rules(payload.mode_id)
|
|
719
|
-
context_label = f"
|
|
703
|
+
context_label = f"mode {payload.mode_id}"
|
|
720
704
|
elif payload.rule_id:
|
|
721
705
|
rules = await self._service.mode_rules_for_rule(payload.rule_id)
|
|
722
|
-
context_label = f"
|
|
706
|
+
context_label = f"rule {payload.rule_id}"
|
|
723
707
|
else:
|
|
724
|
-
return text("❌
|
|
708
|
+
return text("❌ Provide mode_id or rule_id to list associations.")
|
|
725
709
|
if not rules:
|
|
726
|
-
return text("🔗
|
|
710
|
+
return text("🔗 No associations found.")
|
|
727
711
|
body = "\n".join(
|
|
728
|
-
f"- {item.get('name', '
|
|
712
|
+
f"- {item.get('name', 'Unnamed')} (ID: {item.get('id')})"
|
|
729
713
|
for item in rules
|
|
730
714
|
)
|
|
731
|
-
return text(f"🔗 **
|
|
715
|
+
return text(f"🔗 **Associations for {context_label}:**\n{body}")
|
|
732
716
|
|
|
733
717
|
return text(
|
|
734
|
-
"❌
|
|
718
|
+
"❌ Unsupported mode action.\n\nChoose one of the values:\n"
|
|
735
719
|
+ "\n".join(
|
|
736
720
|
f"- `{value}`"
|
|
737
721
|
for value in KnowledgeAction.choices()
|
|
@@ -746,7 +730,7 @@ class KnowledgeTool(Tool):
|
|
|
746
730
|
action = payload.action
|
|
747
731
|
if action is KnowledgeAction.RULE_CREATE:
|
|
748
732
|
if not payload.rule_name or not payload.rule_content:
|
|
749
|
-
return text("❌
|
|
733
|
+
return text("❌ Provide rule_name and rule_content.")
|
|
750
734
|
rule = await self._service.rule_create(
|
|
751
735
|
{
|
|
752
736
|
"name": payload.rule_name,
|
|
@@ -756,7 +740,7 @@ class KnowledgeTool(Tool):
|
|
|
756
740
|
"metadata": payload.rule_metadata,
|
|
757
741
|
}
|
|
758
742
|
)
|
|
759
|
-
return text(_format_rule(rule, header="✅
|
|
743
|
+
return text(_format_rule(rule, header="✅ Rule created"))
|
|
760
744
|
|
|
761
745
|
if action is KnowledgeAction.RULE_LIST:
|
|
762
746
|
rules = await self._service.rule_list(
|
|
@@ -765,24 +749,24 @@ class KnowledgeTool(Tool):
|
|
|
765
749
|
return_modes=payload.return_metadata,
|
|
766
750
|
)
|
|
767
751
|
if not rules:
|
|
768
|
-
return text("📋
|
|
752
|
+
return text("📋 No rules found.")
|
|
769
753
|
body = "\n\n".join(_format_rule(rule) for rule in rules)
|
|
770
|
-
return text(f"📋 **
|
|
754
|
+
return text(f"📋 **Rules ({len(rules)}):**\n\n{body}")
|
|
771
755
|
|
|
772
756
|
if action is KnowledgeAction.RULE_GET:
|
|
773
757
|
if not payload.rule_id:
|
|
774
|
-
return text("❌
|
|
758
|
+
return text("❌ Provide rule_id to get details.")
|
|
775
759
|
rule = await self._service.rule_get(
|
|
776
760
|
payload.rule_id,
|
|
777
761
|
return_description=payload.return_description,
|
|
778
762
|
return_metadata=payload.return_metadata,
|
|
779
763
|
return_modes=payload.return_metadata,
|
|
780
764
|
)
|
|
781
|
-
return text(_format_rule(rule, header="📋
|
|
765
|
+
return text(_format_rule(rule, header="📋 Rule details"))
|
|
782
766
|
|
|
783
767
|
if action is KnowledgeAction.RULE_UPDATE:
|
|
784
768
|
if not payload.rule_id:
|
|
785
|
-
return text("❌
|
|
769
|
+
return text("❌ Provide rule_id to update.")
|
|
786
770
|
rule = await self._service.rule_update(
|
|
787
771
|
payload.rule_id,
|
|
788
772
|
{
|
|
@@ -793,16 +777,16 @@ class KnowledgeTool(Tool):
|
|
|
793
777
|
"metadata": payload.rule_metadata,
|
|
794
778
|
},
|
|
795
779
|
)
|
|
796
|
-
return text(_format_rule(rule, header="✅
|
|
780
|
+
return text(_format_rule(rule, header="✅ Rule updated"))
|
|
797
781
|
|
|
798
782
|
if action is KnowledgeAction.RULE_DELETE:
|
|
799
783
|
if not payload.rule_id:
|
|
800
|
-
return text("❌
|
|
784
|
+
return text("❌ Provide rule_id to remove.")
|
|
801
785
|
await self._service.rule_delete(payload.rule_id)
|
|
802
|
-
return text(f"🗑️
|
|
786
|
+
return text(f"🗑️ Rule {payload.rule_id} removed.")
|
|
803
787
|
|
|
804
788
|
return text(
|
|
805
|
-
"❌
|
|
789
|
+
"❌ Unsupported rule action.\n\nChoose one of the values:\n"
|
|
806
790
|
+ "\n".join(
|
|
807
791
|
f"- `{value}`"
|
|
808
792
|
for value in KnowledgeAction.choices()
|
|
@@ -817,33 +801,32 @@ class KnowledgeTool(Tool):
|
|
|
817
801
|
action = payload.action
|
|
818
802
|
if action is KnowledgeAction.DOC_CREATE:
|
|
819
803
|
if not payload.doc_title:
|
|
820
|
-
return text("❌
|
|
804
|
+
return text("❌ Provide doc_title to create the documentation.")
|
|
821
805
|
if payload.doc_type and payload.doc_type not in _ALLOWED_DOC_TYPES:
|
|
822
806
|
allowed = ", ".join(sorted(_ALLOWED_DOC_TYPES))
|
|
823
807
|
return text(
|
|
824
|
-
"❌ doc_type
|
|
808
|
+
"❌ Invalid doc_type. Use one of the supported values: " + allowed
|
|
825
809
|
)
|
|
826
810
|
doc = await self._service.doc_create(
|
|
827
811
|
{
|
|
828
812
|
"title": payload.doc_title,
|
|
829
|
-
"description": payload.doc_description,
|
|
830
|
-
"content": payload.doc_content,
|
|
831
|
-
"status": payload.doc_status,
|
|
832
|
-
"doc_type": payload.doc_type,
|
|
833
|
-
"language": payload.doc_language,
|
|
834
|
-
"parent_id": payload.doc_parent_id,
|
|
835
|
-
"
|
|
836
|
-
"
|
|
837
|
-
"
|
|
838
|
-
"
|
|
839
|
-
"
|
|
840
|
-
"
|
|
841
|
-
"
|
|
842
|
-
"keywords": payload.doc_keywords,
|
|
813
|
+
"description": sanitize_null(payload.doc_description),
|
|
814
|
+
"content": sanitize_null(payload.doc_content),
|
|
815
|
+
"status": sanitize_null(payload.doc_status),
|
|
816
|
+
"doc_type": sanitize_null(payload.doc_type),
|
|
817
|
+
"language": sanitize_null(payload.doc_language),
|
|
818
|
+
"parent_id": sanitize_null(payload.doc_parent_id),
|
|
819
|
+
"owner_user_id": sanitize_null(payload.doc_owner_id),
|
|
820
|
+
"reviewer_user_id": sanitize_null(payload.doc_reviewer_id),
|
|
821
|
+
"version": sanitize_null(payload.doc_version),
|
|
822
|
+
"category": sanitize_null(payload.doc_category),
|
|
823
|
+
"tags": sanitize_null_list(payload.doc_tags),
|
|
824
|
+
"emoji": sanitize_null(payload.doc_emoji or payload.doc_emote),
|
|
825
|
+
"keywords": sanitize_null_list(payload.doc_keywords),
|
|
843
826
|
"is_public": payload.doc_is_public,
|
|
844
827
|
}
|
|
845
828
|
)
|
|
846
|
-
return text(_format_doc(doc, header="✅
|
|
829
|
+
return text(_format_doc(doc, header="✅ Documentation created"))
|
|
847
830
|
|
|
848
831
|
if action is KnowledgeAction.DOC_LIST:
|
|
849
832
|
docs = await self._service.doc_list(
|
|
@@ -852,156 +835,140 @@ class KnowledgeTool(Tool):
|
|
|
852
835
|
returnContent=payload.return_content,
|
|
853
836
|
)
|
|
854
837
|
if not docs:
|
|
855
|
-
return text("📄
|
|
838
|
+
return text("📄 No documentation found.")
|
|
856
839
|
body = "\n\n".join(_format_doc(doc) for doc in docs)
|
|
857
|
-
return text(f"📄 **
|
|
840
|
+
return text(f"📄 **Documents ({len(docs)}):**\n\n{body}")
|
|
858
841
|
|
|
859
842
|
if action is KnowledgeAction.DOC_GET:
|
|
860
843
|
if not payload.id:
|
|
861
|
-
return text("❌
|
|
844
|
+
return text("❌ Provide the documentation ID.")
|
|
862
845
|
doc = await self._service.doc_get(
|
|
863
846
|
payload.id,
|
|
864
847
|
returnContent=payload.return_content,
|
|
865
848
|
)
|
|
866
|
-
return text(_format_doc(doc, header="📄
|
|
849
|
+
return text(_format_doc(doc, header="📄 Documentation details"))
|
|
867
850
|
|
|
868
851
|
if action is KnowledgeAction.DOC_UPDATE:
|
|
869
852
|
if not payload.id:
|
|
870
|
-
return text("❌
|
|
853
|
+
return text("❌ Provide the documentation ID.")
|
|
871
854
|
if payload.doc_type and payload.doc_type not in _ALLOWED_DOC_TYPES:
|
|
872
855
|
allowed = ", ".join(sorted(_ALLOWED_DOC_TYPES))
|
|
873
856
|
return text(
|
|
874
|
-
"❌ doc_type
|
|
857
|
+
"❌ Invalid doc_type. Use one of the supported values: " + allowed
|
|
875
858
|
)
|
|
876
859
|
doc = await self._service.doc_update(
|
|
877
860
|
payload.id,
|
|
878
861
|
{
|
|
879
|
-
"title": payload.doc_title,
|
|
880
|
-
"description": payload.doc_description,
|
|
881
|
-
"content": payload.doc_content,
|
|
882
|
-
"status": payload.doc_status,
|
|
883
|
-
"doc_type": payload.doc_type,
|
|
884
|
-
"language": payload.doc_language,
|
|
885
|
-
"parent_id": payload.doc_parent_id,
|
|
886
|
-
"
|
|
887
|
-
"
|
|
888
|
-
"
|
|
889
|
-
"
|
|
890
|
-
"
|
|
891
|
-
"
|
|
892
|
-
"
|
|
893
|
-
"keywords": payload.doc_keywords,
|
|
862
|
+
"title": sanitize_null(payload.doc_title),
|
|
863
|
+
"description": sanitize_null(payload.doc_description),
|
|
864
|
+
"content": sanitize_null(payload.doc_content),
|
|
865
|
+
"status": sanitize_null(payload.doc_status),
|
|
866
|
+
"doc_type": sanitize_null(payload.doc_type),
|
|
867
|
+
"language": sanitize_null(payload.doc_language),
|
|
868
|
+
"parent_id": sanitize_null(payload.doc_parent_id),
|
|
869
|
+
"owner_user_id": sanitize_null(payload.doc_owner_id),
|
|
870
|
+
"reviewer_user_id": sanitize_null(payload.doc_reviewer_id),
|
|
871
|
+
"version": sanitize_null(payload.doc_version),
|
|
872
|
+
"category": sanitize_null(payload.doc_category),
|
|
873
|
+
"tags": sanitize_null_list(payload.doc_tags),
|
|
874
|
+
"emoji": sanitize_null(payload.doc_emoji or payload.doc_emote),
|
|
875
|
+
"keywords": sanitize_null_list(payload.doc_keywords),
|
|
894
876
|
"is_public": payload.doc_is_public,
|
|
895
877
|
},
|
|
896
878
|
)
|
|
897
|
-
return text(_format_doc(doc, header="✅
|
|
879
|
+
return text(_format_doc(doc, header="✅ Documentation updated"))
|
|
898
880
|
|
|
899
881
|
if action is KnowledgeAction.DOC_DELETE:
|
|
900
882
|
if not payload.id:
|
|
901
|
-
return text("❌
|
|
883
|
+
return text("❌ Provide the documentation ID.")
|
|
902
884
|
await self._service.doc_delete(payload.id)
|
|
903
|
-
return text(f"🗑️
|
|
885
|
+
return text(f"🗑️ Documentation {payload.id} removed.")
|
|
904
886
|
|
|
905
887
|
if action is KnowledgeAction.DOC_SEARCH:
|
|
906
|
-
|
|
907
|
-
|
|
888
|
+
query = sanitize_null(payload.query)
|
|
889
|
+
if not query:
|
|
890
|
+
return text("❌ Provide query to search documentation.")
|
|
908
891
|
docs = await self._service.doc_search(
|
|
909
|
-
query=
|
|
910
|
-
team_id=payload.doc_team_id or payload.team_id,
|
|
892
|
+
query=query,
|
|
911
893
|
limit=payload.limit,
|
|
912
894
|
)
|
|
913
895
|
if not docs:
|
|
914
|
-
return text(
|
|
915
|
-
"🔍 Nenhum documento encontrado para os filtros informados."
|
|
916
|
-
)
|
|
896
|
+
return text("🔍 No documents found for the specified filters.")
|
|
917
897
|
body = "\n\n".join(_format_doc(doc) for doc in docs)
|
|
918
|
-
return text(f"🔍 **
|
|
898
|
+
return text(f"🔍 **Results ({len(docs)}):**\n\n{body}")
|
|
919
899
|
|
|
920
900
|
if action is KnowledgeAction.DOC_ROOTS:
|
|
921
|
-
|
|
922
|
-
return text("❌ Informe team_id para listar raízes.")
|
|
923
|
-
docs = await self._service.doc_roots(
|
|
924
|
-
team_id=payload.doc_team_id or payload.team_id
|
|
925
|
-
)
|
|
901
|
+
docs = await self._service.doc_roots()
|
|
926
902
|
if not docs:
|
|
927
|
-
return text("📚
|
|
903
|
+
return text("📚 No roots found.")
|
|
928
904
|
body = "\n".join(
|
|
929
|
-
f"- {doc.get('title', '
|
|
905
|
+
f"- {doc.get('title', 'Untitled')} (ID: {doc.get('id')})"
|
|
930
906
|
for doc in docs
|
|
931
907
|
)
|
|
932
|
-
return text(f"📚 **
|
|
908
|
+
return text(f"📚 **Documentation roots:**\n{body}")
|
|
933
909
|
|
|
934
910
|
if action is KnowledgeAction.DOC_RECENT:
|
|
935
|
-
if not (payload.doc_team_id or payload.team_id):
|
|
936
|
-
return text("❌ Informe team_id para listar documentos recentes.")
|
|
937
911
|
docs = await self._service.doc_recent(
|
|
938
|
-
team_id=payload.doc_team_id or payload.team_id,
|
|
939
912
|
limit=payload.limit,
|
|
940
913
|
)
|
|
941
914
|
if not docs:
|
|
942
|
-
return text("🕒
|
|
915
|
+
return text("🕒 No recent documentation found.")
|
|
943
916
|
body = "\n\n".join(_format_doc(doc) for doc in docs)
|
|
944
|
-
return text(f"🕒 **
|
|
917
|
+
return text(f"🕒 **Recent documents ({len(docs)}):**\n\n{body}")
|
|
945
918
|
|
|
946
919
|
if action is KnowledgeAction.DOC_ANALYTICS:
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
analytics = await self._service.doc_analytics(
|
|
950
|
-
team_id=payload.doc_team_id or payload.team_id
|
|
951
|
-
)
|
|
952
|
-
lines = ["📊 **Analytics de Documentação**"]
|
|
920
|
+
analytics = await self._service.doc_analytics()
|
|
921
|
+
lines = ["📊 **Documentation Analytics**"]
|
|
953
922
|
for key, value in analytics.items():
|
|
954
923
|
lines.append(f"- {key}: {value}")
|
|
955
924
|
return text("\n".join(lines))
|
|
956
925
|
|
|
957
926
|
if action is KnowledgeAction.DOC_CHILDREN:
|
|
958
927
|
if not payload.id:
|
|
959
|
-
return text("❌
|
|
928
|
+
return text("❌ Provide the documentation ID.")
|
|
960
929
|
docs = await self._service.doc_children(payload.id)
|
|
961
930
|
if not docs:
|
|
962
|
-
return text("📄
|
|
931
|
+
return text("📄 No children registered for the specified document.")
|
|
963
932
|
body = "\n".join(
|
|
964
|
-
f"- {doc.get('title', '
|
|
933
|
+
f"- {doc.get('title', 'Untitled')} (ID: {doc.get('id')})"
|
|
965
934
|
for doc in docs
|
|
966
935
|
)
|
|
967
|
-
return text(f"📄 **
|
|
936
|
+
return text(f"📄 **Children:**\n{body}")
|
|
968
937
|
|
|
969
938
|
if action is KnowledgeAction.DOC_TREE:
|
|
970
939
|
if not payload.id:
|
|
971
|
-
return text("❌
|
|
940
|
+
return text("❌ Provide the documentation ID.")
|
|
972
941
|
tree = await self._service.doc_tree(payload.id)
|
|
973
|
-
return text(f"🌳
|
|
942
|
+
return text(f"🌳 **Documentation tree for {payload.id}:**\n{tree}")
|
|
974
943
|
|
|
975
944
|
if action is KnowledgeAction.DOC_FULL_TREE:
|
|
976
945
|
tree = await self._service.doc_full_tree()
|
|
977
|
-
return text(f"🌳
|
|
946
|
+
return text(f"🌳 **Full documentation tree:**\n{tree}")
|
|
978
947
|
|
|
979
948
|
if action is KnowledgeAction.DOC_MOVE:
|
|
980
949
|
if not payload.id:
|
|
981
|
-
return text("❌
|
|
950
|
+
return text("❌ Provide the documentation ID.")
|
|
982
951
|
if payload.doc_parent_id is None and payload.doc_position is None:
|
|
983
|
-
return text(
|
|
984
|
-
"❌ Informe doc_parent_id, doc_position ou ambos para mover."
|
|
985
|
-
)
|
|
952
|
+
return text("❌ Provide doc_parent_id, doc_position or both to move.")
|
|
986
953
|
move_payload = {
|
|
987
954
|
"new_parent_id": payload.doc_parent_id,
|
|
988
955
|
"new_position": payload.doc_position,
|
|
989
956
|
}
|
|
990
957
|
doc = await self._service.doc_move(payload.id, move_payload)
|
|
991
|
-
return text(_format_doc(doc, header="📦
|
|
958
|
+
return text(_format_doc(doc, header="📦 Documentation moved"))
|
|
992
959
|
|
|
993
960
|
if action is KnowledgeAction.DOC_PUBLISH:
|
|
994
961
|
if not payload.id:
|
|
995
|
-
return text("❌
|
|
962
|
+
return text("❌ Provide the documentation ID.")
|
|
996
963
|
result = await self._service.doc_publish(payload.id)
|
|
997
|
-
return text(f"🗞️
|
|
964
|
+
return text(f"🗞️ Document published: {result}")
|
|
998
965
|
|
|
999
966
|
if action is KnowledgeAction.DOC_VERSION:
|
|
1000
967
|
if not payload.id:
|
|
1001
|
-
return text("❌
|
|
968
|
+
return text("❌ Provide the documentation ID.")
|
|
1002
969
|
if not payload.doc_version:
|
|
1003
970
|
return text(
|
|
1004
|
-
"❌
|
|
971
|
+
"❌ Provide doc_version with the version number/identifier."
|
|
1005
972
|
)
|
|
1006
973
|
version_payload = {
|
|
1007
974
|
"title": payload.doc_title or f"Version {payload.doc_version}",
|
|
@@ -1009,24 +976,23 @@ class KnowledgeTool(Tool):
|
|
|
1009
976
|
"content": payload.doc_content,
|
|
1010
977
|
}
|
|
1011
978
|
doc = await self._service.doc_version(payload.id, version_payload)
|
|
1012
|
-
return text(_format_doc(doc, header="🗞️
|
|
979
|
+
return text(_format_doc(doc, header="🗞️ New version created"))
|
|
1013
980
|
|
|
1014
981
|
if action is KnowledgeAction.DOC_DUPLICATE:
|
|
1015
982
|
if not payload.id:
|
|
1016
|
-
return text("❌
|
|
983
|
+
return text("❌ Provide the documentation ID.")
|
|
1017
984
|
if not payload.doc_title:
|
|
1018
|
-
return text("❌
|
|
985
|
+
return text("❌ Provide doc_title to name the copy.")
|
|
1019
986
|
doc = await self._service.doc_duplicate(
|
|
1020
987
|
payload.id,
|
|
1021
988
|
{
|
|
1022
989
|
"title": payload.doc_title,
|
|
1023
|
-
"team_id": payload.doc_team_id or payload.team_id,
|
|
1024
990
|
},
|
|
1025
991
|
)
|
|
1026
|
-
return text(_format_doc(doc, header="🗂️
|
|
992
|
+
return text(_format_doc(doc, header="🗂️ Document duplicated"))
|
|
1027
993
|
|
|
1028
994
|
return text(
|
|
1029
|
-
"❌
|
|
995
|
+
"❌ Unsupported documentation action.\n\nChoose one of the values:\n"
|
|
1030
996
|
+ "\n".join(
|
|
1031
997
|
f"- `{value}`"
|
|
1032
998
|
for value in KnowledgeAction.choices()
|
|
@@ -1036,7 +1002,7 @@ class KnowledgeTool(Tool):
|
|
|
1036
1002
|
|
|
1037
1003
|
async def _handle_help(self):
|
|
1038
1004
|
return text(
|
|
1039
|
-
"📚 **
|
|
1005
|
+
"📚 **Available actions for knowledge**\n\n"
|
|
1040
1006
|
+ KnowledgeAction.formatted_help()
|
|
1041
1007
|
)
|
|
1042
1008
|
|
|
@@ -1048,13 +1014,13 @@ def _format_work(item: Dict[str, Any], *, header: Optional[str] = None) -> str:
|
|
|
1048
1014
|
lines.append("")
|
|
1049
1015
|
|
|
1050
1016
|
# Extract title
|
|
1051
|
-
title = item.get("title") or item.get("name") or "
|
|
1017
|
+
title = item.get("title") or item.get("name") or "Untitled"
|
|
1052
1018
|
|
|
1053
1019
|
# Extract status
|
|
1054
|
-
status = item.get("status") or item.get("state") or "
|
|
1020
|
+
status = item.get("status") or item.get("state") or "unknown"
|
|
1055
1021
|
|
|
1056
1022
|
# Extract priority
|
|
1057
|
-
priority = item.get("priority") or item.get("priority_level") or "
|
|
1023
|
+
priority = item.get("priority") or item.get("priority_level") or "undefined"
|
|
1058
1024
|
|
|
1059
1025
|
# Extract ID
|
|
1060
1026
|
item_id = item.get("id", "N/A")
|
|
@@ -1073,13 +1039,13 @@ def _format_work(item: Dict[str, Any], *, header: Optional[str] = None) -> str:
|
|
|
1073
1039
|
f"🎯 **{title}**",
|
|
1074
1040
|
f"ID: {item_id}",
|
|
1075
1041
|
f"Status: {status}",
|
|
1076
|
-
f"
|
|
1077
|
-
f"
|
|
1042
|
+
f"Priority: {priority}",
|
|
1043
|
+
f"Assignee: {assignee}",
|
|
1078
1044
|
]
|
|
1079
1045
|
)
|
|
1080
1046
|
if item.get("due_date") or item.get("dueDate"):
|
|
1081
1047
|
lines.append(
|
|
1082
|
-
f"
|
|
1048
|
+
f"Due date: {_format_date(item.get('due_date') or item.get('dueDate'))}"
|
|
1083
1049
|
)
|
|
1084
1050
|
if item.get("tags"):
|
|
1085
1051
|
tags = item.get("tags", [])
|
|
@@ -1095,10 +1061,10 @@ def _format_board(board: Dict[str, Any], header: Optional[str] = None) -> str:
|
|
|
1095
1061
|
lines.append("")
|
|
1096
1062
|
lines.extend(
|
|
1097
1063
|
[
|
|
1098
|
-
f"🗂️ **{board.get('name', '
|
|
1064
|
+
f"🗂️ **{board.get('name', 'Unnamed')}**",
|
|
1099
1065
|
f"ID: {board.get('id', 'N/A')}",
|
|
1100
|
-
f"
|
|
1101
|
-
f"
|
|
1066
|
+
f"Team: {board.get('team_id', 'N/A')}",
|
|
1067
|
+
f"Columns: {len(board.get('columns', []))}",
|
|
1102
1068
|
]
|
|
1103
1069
|
)
|
|
1104
1070
|
return "\n".join(lines)
|
|
@@ -1111,19 +1077,19 @@ def _format_sprint(sprint: Dict[str, Any], header: Optional[str] = None) -> str:
|
|
|
1111
1077
|
lines.append("")
|
|
1112
1078
|
lines.extend(
|
|
1113
1079
|
[
|
|
1114
|
-
f"🏃 **{sprint.get('name', '
|
|
1080
|
+
f"🏃 **{sprint.get('name', 'Unnamed')}**",
|
|
1115
1081
|
f"ID: {sprint.get('id', 'N/A')}",
|
|
1116
1082
|
f"Status: {sprint.get('status', 'N/A')}",
|
|
1117
|
-
f"
|
|
1083
|
+
f"Team: {sprint.get('team_id', 'N/A')}",
|
|
1118
1084
|
]
|
|
1119
1085
|
)
|
|
1120
1086
|
if sprint.get("start_date") or sprint.get("startDate"):
|
|
1121
1087
|
lines.append(
|
|
1122
|
-
f"
|
|
1088
|
+
f"Start: {_format_date(sprint.get('start_date') or sprint.get('startDate'))}"
|
|
1123
1089
|
)
|
|
1124
1090
|
if sprint.get("end_date") or sprint.get("endDate"):
|
|
1125
1091
|
lines.append(
|
|
1126
|
-
f"
|
|
1092
|
+
f"End: {_format_date(sprint.get('end_date') or sprint.get('endDate'))}"
|
|
1127
1093
|
)
|
|
1128
1094
|
return "\n".join(lines)
|
|
1129
1095
|
|
|
@@ -1135,13 +1101,13 @@ def _format_mode(mode: Dict[str, Any], header: Optional[str] = None) -> str:
|
|
|
1135
1101
|
lines.append("")
|
|
1136
1102
|
lines.extend(
|
|
1137
1103
|
[
|
|
1138
|
-
f"🎭 **{mode.get('name', '
|
|
1104
|
+
f"🎭 **{mode.get('name', 'Unnamed')}**",
|
|
1139
1105
|
f"ID: {mode.get('id', 'N/A')}",
|
|
1140
|
-
f"
|
|
1106
|
+
f"Default: {mode.get('is_default', False)}",
|
|
1141
1107
|
]
|
|
1142
1108
|
)
|
|
1143
1109
|
if mode.get("description"):
|
|
1144
|
-
lines.append(f"
|
|
1110
|
+
lines.append(f"Description: {mode['description']}")
|
|
1145
1111
|
return "\n".join(lines)
|
|
1146
1112
|
|
|
1147
1113
|
|
|
@@ -1152,13 +1118,13 @@ def _format_rule(rule: Dict[str, Any], header: Optional[str] = None) -> str:
|
|
|
1152
1118
|
lines.append("")
|
|
1153
1119
|
lines.extend(
|
|
1154
1120
|
[
|
|
1155
|
-
f"📋 **{rule.get('name', '
|
|
1121
|
+
f"📋 **{rule.get('name', 'Unnamed')}**",
|
|
1156
1122
|
f"ID: {rule.get('id', 'N/A')}",
|
|
1157
|
-
f"
|
|
1123
|
+
f"Default: {rule.get('is_default', False)}",
|
|
1158
1124
|
]
|
|
1159
1125
|
)
|
|
1160
1126
|
if rule.get("description"):
|
|
1161
|
-
lines.append(f"
|
|
1127
|
+
lines.append(f"Description: {rule['description']}")
|
|
1162
1128
|
return "\n".join(lines)
|
|
1163
1129
|
|
|
1164
1130
|
|
|
@@ -1169,15 +1135,15 @@ def _format_doc(doc: Dict[str, Any], header: Optional[str] = None) -> str:
|
|
|
1169
1135
|
lines.append("")
|
|
1170
1136
|
lines.extend(
|
|
1171
1137
|
[
|
|
1172
|
-
f"📄 **{doc.get('title') or doc.get('name', '
|
|
1138
|
+
f"📄 **{doc.get('title') or doc.get('name', 'Untitled')}**",
|
|
1173
1139
|
f"ID: {doc.get('id', 'N/A')}",
|
|
1174
1140
|
f"Status: {doc.get('status', 'N/A')}",
|
|
1175
|
-
f"
|
|
1141
|
+
f"Team: {doc.get('team_id', 'N/A')}",
|
|
1176
1142
|
]
|
|
1177
1143
|
)
|
|
1178
1144
|
if doc.get("updated_at") or doc.get("updatedAt"):
|
|
1179
1145
|
lines.append(
|
|
1180
|
-
f"
|
|
1146
|
+
f"Updated at: {_format_date(doc.get('updated_at') or doc.get('updatedAt'))}"
|
|
1181
1147
|
)
|
|
1182
1148
|
return "\n".join(lines)
|
|
1183
1149
|
|