better-notion 1.0.0__py3-none-any.whl → 1.1.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.
- better_notion/_sdk/client.py +106 -3
- better_notion/_sdk/plugins.py +180 -0
- better_notion/plugins/base.py +99 -1
- better_notion/plugins/loader.py +51 -1
- better_notion/plugins/official/agents.py +150 -5
- better_notion/plugins/official/agents_cli.py +1767 -0
- better_notion/plugins/official/agents_sdk/__init__.py +30 -0
- better_notion/plugins/official/agents_sdk/managers.py +973 -0
- better_notion/plugins/official/agents_sdk/models.py +2256 -0
- better_notion/plugins/official/agents_sdk/plugin.py +146 -0
- {better_notion-1.0.0.dist-info → better_notion-1.1.0.dist-info}/METADATA +2 -2
- {better_notion-1.0.0.dist-info → better_notion-1.1.0.dist-info}/RECORD +15 -9
- {better_notion-1.0.0.dist-info → better_notion-1.1.0.dist-info}/WHEEL +0 -0
- {better_notion-1.0.0.dist-info → better_notion-1.1.0.dist-info}/entry_points.txt +0 -0
- {better_notion-1.0.0.dist-info → better_notion-1.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1767 @@
|
|
|
1
|
+
"""CLI commands for workflow entities.
|
|
2
|
+
|
|
3
|
+
This module provides CRUD commands for all workflow entities including
|
|
4
|
+
Organizations, Projects, Versions, Tasks, Ideas, Work Issues, and Incidents.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
|
|
14
|
+
from better_notion._cli.response import format_error, format_success
|
|
15
|
+
from better_notion._sdk.client import NotionClient
|
|
16
|
+
from better_notion.utils.agents import ProjectContext, get_or_create_agent_id
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_client() -> NotionClient:
|
|
20
|
+
"""Get authenticated Notion client."""
|
|
21
|
+
from better_notion._cli.config import Config
|
|
22
|
+
|
|
23
|
+
config = Config.load()
|
|
24
|
+
return NotionClient(auth=config.token, timeout=config.timeout)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_workspace_config() -> dict:
|
|
28
|
+
"""Get workspace configuration."""
|
|
29
|
+
import json
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
config_path = Path.home() / ".notion" / "workspace.json"
|
|
33
|
+
if not config_path.exists():
|
|
34
|
+
return {}
|
|
35
|
+
|
|
36
|
+
with open(config_path) as f:
|
|
37
|
+
return json.load(f)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ===== ORGANIZATIONS =====
|
|
41
|
+
|
|
42
|
+
def orgs_list() -> str:
|
|
43
|
+
"""
|
|
44
|
+
List all organizations.
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
$ notion orgs list
|
|
48
|
+
"""
|
|
49
|
+
async def _list() -> str:
|
|
50
|
+
try:
|
|
51
|
+
client = get_client()
|
|
52
|
+
|
|
53
|
+
# Register SDK plugin
|
|
54
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
55
|
+
plugin = AgentsSDKPlugin()
|
|
56
|
+
plugin.initialize(client)
|
|
57
|
+
client.register_sdk_plugin(plugin)
|
|
58
|
+
|
|
59
|
+
# Get manager
|
|
60
|
+
manager = client.plugin_manager("organizations")
|
|
61
|
+
orgs = await manager.list()
|
|
62
|
+
|
|
63
|
+
return format_success({
|
|
64
|
+
"organizations": [
|
|
65
|
+
{
|
|
66
|
+
"id": org.id,
|
|
67
|
+
"name": org.name,
|
|
68
|
+
"slug": org.slug,
|
|
69
|
+
"status": org.status,
|
|
70
|
+
}
|
|
71
|
+
for org in orgs
|
|
72
|
+
],
|
|
73
|
+
"total": len(orgs),
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
return format_error("LIST_ORGS_ERROR", str(e), retry=False)
|
|
78
|
+
|
|
79
|
+
return asyncio.run(_list())
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def orgs_get(org_id: str) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Get an organization by ID.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
org_id: Organization page ID
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
$ notion orgs get org_123
|
|
91
|
+
"""
|
|
92
|
+
async def _get() -> str:
|
|
93
|
+
try:
|
|
94
|
+
client = get_client()
|
|
95
|
+
|
|
96
|
+
# Register SDK plugin
|
|
97
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
98
|
+
plugin = AgentsSDKPlugin()
|
|
99
|
+
plugin.initialize(client)
|
|
100
|
+
client.register_sdk_plugin(plugin)
|
|
101
|
+
|
|
102
|
+
# Get organization
|
|
103
|
+
manager = client.plugin_manager("organizations")
|
|
104
|
+
org = await manager.get(org_id)
|
|
105
|
+
|
|
106
|
+
return format_success({
|
|
107
|
+
"id": org.id,
|
|
108
|
+
"name": org.name,
|
|
109
|
+
"slug": org.slug,
|
|
110
|
+
"description": org.description,
|
|
111
|
+
"repository_url": org.repository_url,
|
|
112
|
+
"status": org.status,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
return format_error("GET_ORG_ERROR", str(e), retry=False)
|
|
117
|
+
|
|
118
|
+
return asyncio.run(_get())
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def orgs_create(
|
|
122
|
+
name: str,
|
|
123
|
+
slug: Optional[str] = None,
|
|
124
|
+
description: Optional[str] = None,
|
|
125
|
+
repository_url: Optional[str] = None,
|
|
126
|
+
status: str = "Active",
|
|
127
|
+
) -> str:
|
|
128
|
+
"""
|
|
129
|
+
Create a new organization.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
name: Organization name
|
|
133
|
+
slug: URL-safe identifier (optional)
|
|
134
|
+
description: Organization description (optional)
|
|
135
|
+
repository_url: Code repository URL (optional)
|
|
136
|
+
status: Organization status (default: Active)
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
$ notion orgs create "My Organization" --slug "my-org"
|
|
140
|
+
"""
|
|
141
|
+
async def _create() -> str:
|
|
142
|
+
try:
|
|
143
|
+
client = get_client()
|
|
144
|
+
|
|
145
|
+
# Register SDK plugin
|
|
146
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
147
|
+
plugin = AgentsSDKPlugin()
|
|
148
|
+
plugin.initialize(client)
|
|
149
|
+
client.register_sdk_plugin(plugin)
|
|
150
|
+
|
|
151
|
+
# Create organization
|
|
152
|
+
manager = client.plugin_manager("organizations")
|
|
153
|
+
org = await manager.create(
|
|
154
|
+
name=name,
|
|
155
|
+
slug=slug,
|
|
156
|
+
description=description,
|
|
157
|
+
repository_url=repository_url,
|
|
158
|
+
status=status,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return format_success({
|
|
162
|
+
"message": "Organization created successfully",
|
|
163
|
+
"id": org.id,
|
|
164
|
+
"name": org.name,
|
|
165
|
+
"slug": org.slug,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
return format_error("CREATE_ORG_ERROR", str(e), retry=False)
|
|
170
|
+
|
|
171
|
+
return asyncio.run(_create())
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# ===== PROJECTS =====
|
|
175
|
+
|
|
176
|
+
def projects_list(
|
|
177
|
+
org_id: Optional[str] = typer.Option(None, "--org-id", "-o", help="Filter by organization ID"),
|
|
178
|
+
) -> str:
|
|
179
|
+
"""
|
|
180
|
+
List all projects, optionally filtered by organization.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
org_id: Filter by organization ID (optional)
|
|
184
|
+
|
|
185
|
+
Example:
|
|
186
|
+
$ notion projects list
|
|
187
|
+
$ notion projects list --org-id org_123
|
|
188
|
+
"""
|
|
189
|
+
async def _list() -> str:
|
|
190
|
+
try:
|
|
191
|
+
client = get_client()
|
|
192
|
+
|
|
193
|
+
# Register SDK plugin
|
|
194
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
195
|
+
plugin = AgentsSDKPlugin()
|
|
196
|
+
plugin.initialize(client)
|
|
197
|
+
client.register_sdk_plugin(plugin)
|
|
198
|
+
|
|
199
|
+
# Get manager
|
|
200
|
+
manager = client.plugin_manager("projects")
|
|
201
|
+
projects = await manager.list(organization_id=org_id)
|
|
202
|
+
|
|
203
|
+
return format_success({
|
|
204
|
+
"projects": [
|
|
205
|
+
{
|
|
206
|
+
"id": p.id,
|
|
207
|
+
"name": p.name,
|
|
208
|
+
"slug": p.slug,
|
|
209
|
+
"status": p.status,
|
|
210
|
+
"role": p.role,
|
|
211
|
+
"organization_id": p.organization_id,
|
|
212
|
+
}
|
|
213
|
+
for p in projects
|
|
214
|
+
],
|
|
215
|
+
"total": len(projects),
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
except Exception as e:
|
|
219
|
+
return format_error("LIST_PROJECTS_ERROR", str(e), retry=False)
|
|
220
|
+
|
|
221
|
+
return asyncio.run(_list())
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def projects_get(project_id: str) -> str:
|
|
225
|
+
"""
|
|
226
|
+
Get a project by ID.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
project_id: Project page ID
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
$ notion projects get proj_123
|
|
233
|
+
"""
|
|
234
|
+
async def _get() -> str:
|
|
235
|
+
try:
|
|
236
|
+
client = get_client()
|
|
237
|
+
|
|
238
|
+
# Register SDK plugin
|
|
239
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
240
|
+
plugin = AgentsSDKPlugin()
|
|
241
|
+
plugin.initialize(client)
|
|
242
|
+
client.register_sdk_plugin(plugin)
|
|
243
|
+
|
|
244
|
+
# Get project
|
|
245
|
+
manager = client.plugin_manager("projects")
|
|
246
|
+
project = await manager.get(project_id)
|
|
247
|
+
|
|
248
|
+
return format_success({
|
|
249
|
+
"id": project.id,
|
|
250
|
+
"name": project.name,
|
|
251
|
+
"slug": project.slug,
|
|
252
|
+
"description": project.description,
|
|
253
|
+
"repository": project.repository,
|
|
254
|
+
"status": project.status,
|
|
255
|
+
"tech_stack": project.tech_stack,
|
|
256
|
+
"role": project.role,
|
|
257
|
+
"organization_id": project.organization_id,
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
return format_error("GET_PROJECT_ERROR", str(e), retry=False)
|
|
262
|
+
|
|
263
|
+
return asyncio.run(_get())
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def projects_create(
|
|
267
|
+
name: str,
|
|
268
|
+
organization_id: str,
|
|
269
|
+
slug: Optional[str] = typer.Option(None, "--slug", "-s", help="URL-safe identifier"),
|
|
270
|
+
description: Optional[str] = typer.Option(None, "--description", "-d", help="Project description"),
|
|
271
|
+
repository: Optional[str] = typer.Option(None, "--repository", "-r", help="Git repository URL"),
|
|
272
|
+
status: str = typer.Option("Active", "--status", help="Project status"),
|
|
273
|
+
tech_stack: Optional[str] = typer.Option(None, "--tech-stack", "-t", help="Comma-separated tech stack"),
|
|
274
|
+
role: str = typer.Option("Developer", "--role", help="Project role"),
|
|
275
|
+
) -> str:
|
|
276
|
+
"""
|
|
277
|
+
Create a new project.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
name: Project name
|
|
281
|
+
organization_id: Parent organization ID
|
|
282
|
+
slug: URL-safe identifier (optional)
|
|
283
|
+
description: Project description (optional)
|
|
284
|
+
repository: Git repository URL (optional)
|
|
285
|
+
status: Project status (default: Active)
|
|
286
|
+
tech_stack: Comma-separated technologies (optional)
|
|
287
|
+
role: Project role (default: Developer)
|
|
288
|
+
|
|
289
|
+
Example:
|
|
290
|
+
$ notion projects create "My Project" org_123 --tech-stack "Python,React"
|
|
291
|
+
"""
|
|
292
|
+
async def _create() -> str:
|
|
293
|
+
try:
|
|
294
|
+
client = get_client()
|
|
295
|
+
|
|
296
|
+
# Register SDK plugin
|
|
297
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
298
|
+
plugin = AgentsSDKPlugin()
|
|
299
|
+
plugin.initialize(client)
|
|
300
|
+
client.register_sdk_plugin(plugin)
|
|
301
|
+
|
|
302
|
+
# Parse tech stack
|
|
303
|
+
tech_stack_list = tech_stack.split(",") if tech_stack else None
|
|
304
|
+
|
|
305
|
+
# Create project
|
|
306
|
+
manager = client.plugin_manager("projects")
|
|
307
|
+
project = await manager.create(
|
|
308
|
+
name=name,
|
|
309
|
+
organization_id=organization_id,
|
|
310
|
+
slug=slug,
|
|
311
|
+
description=description,
|
|
312
|
+
repository=repository,
|
|
313
|
+
status=status,
|
|
314
|
+
tech_stack=tech_stack_list,
|
|
315
|
+
role=role,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
return format_success({
|
|
319
|
+
"message": "Project created successfully",
|
|
320
|
+
"id": project.id,
|
|
321
|
+
"name": project.name,
|
|
322
|
+
"slug": project.slug,
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
except Exception as e:
|
|
326
|
+
return format_error("CREATE_PROJECT_ERROR", str(e), retry=False)
|
|
327
|
+
|
|
328
|
+
return asyncio.run(_create())
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
# ===== VERSIONS =====
|
|
332
|
+
|
|
333
|
+
def versions_list(
|
|
334
|
+
project_id: Optional[str] = typer.Option(None, "--project-id", "-p", help="Filter by project ID"),
|
|
335
|
+
) -> str:
|
|
336
|
+
"""
|
|
337
|
+
List all versions, optionally filtered by project.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
project_id: Filter by project ID (optional)
|
|
341
|
+
|
|
342
|
+
Example:
|
|
343
|
+
$ notion versions list
|
|
344
|
+
$ notion versions list --project-id proj_123
|
|
345
|
+
"""
|
|
346
|
+
async def _list() -> str:
|
|
347
|
+
try:
|
|
348
|
+
client = get_client()
|
|
349
|
+
|
|
350
|
+
# Register SDK plugin
|
|
351
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
352
|
+
plugin = AgentsSDKPlugin()
|
|
353
|
+
plugin.initialize(client)
|
|
354
|
+
client.register_sdk_plugin(plugin)
|
|
355
|
+
|
|
356
|
+
# Get manager
|
|
357
|
+
manager = client.plugin_manager("versions")
|
|
358
|
+
versions = await manager.list(project_id=project_id)
|
|
359
|
+
|
|
360
|
+
return format_success({
|
|
361
|
+
"versions": [
|
|
362
|
+
{
|
|
363
|
+
"id": v.id,
|
|
364
|
+
"name": v.name,
|
|
365
|
+
"status": v.status,
|
|
366
|
+
"type": v.version_type,
|
|
367
|
+
"branch_name": v.branch_name,
|
|
368
|
+
"progress": v.progress,
|
|
369
|
+
"project_id": v.project_id,
|
|
370
|
+
}
|
|
371
|
+
for v in versions
|
|
372
|
+
],
|
|
373
|
+
"total": len(versions),
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
except Exception as e:
|
|
377
|
+
return format_error("LIST_VERSIONS_ERROR", str(e), retry=False)
|
|
378
|
+
|
|
379
|
+
return asyncio.run(_list())
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def versions_get(version_id: str) -> str:
|
|
383
|
+
"""
|
|
384
|
+
Get a version by ID.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
version_id: Version page ID
|
|
388
|
+
|
|
389
|
+
Example:
|
|
390
|
+
$ notion versions get ver_123
|
|
391
|
+
"""
|
|
392
|
+
async def _get() -> str:
|
|
393
|
+
try:
|
|
394
|
+
client = get_client()
|
|
395
|
+
|
|
396
|
+
# Register SDK plugin
|
|
397
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
398
|
+
plugin = AgentsSDKPlugin()
|
|
399
|
+
plugin.initialize(client)
|
|
400
|
+
client.register_sdk_plugin(plugin)
|
|
401
|
+
|
|
402
|
+
# Get version
|
|
403
|
+
manager = client.plugin_manager("versions")
|
|
404
|
+
version = await manager.get(version_id)
|
|
405
|
+
|
|
406
|
+
return format_success({
|
|
407
|
+
"id": version.id,
|
|
408
|
+
"name": version.name,
|
|
409
|
+
"status": version.status,
|
|
410
|
+
"type": version.version_type,
|
|
411
|
+
"branch_name": version.branch_name,
|
|
412
|
+
"progress": version.progress,
|
|
413
|
+
"project_id": version.project_id,
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
except Exception as e:
|
|
417
|
+
return format_error("GET_VERSION_ERROR", str(e), retry=False)
|
|
418
|
+
|
|
419
|
+
return asyncio.run(_get())
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def versions_create(
|
|
423
|
+
name: str,
|
|
424
|
+
project_id: str,
|
|
425
|
+
status: str = typer.Option("Planning", "--status", help="Version status"),
|
|
426
|
+
version_type: str = typer.Option("Minor", "--type", help="Version type"),
|
|
427
|
+
branch_name: Optional[str] = typer.Option(None, "--branch", "-b", help="Git branch name"),
|
|
428
|
+
progress: int = typer.Option(0, "--progress", "-p", help="Progress percentage (0-100)"),
|
|
429
|
+
) -> str:
|
|
430
|
+
"""
|
|
431
|
+
Create a new version.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
name: Version name (e.g., v1.0.0)
|
|
435
|
+
project_id: Parent project ID
|
|
436
|
+
status: Version status (default: Planning)
|
|
437
|
+
version_type: Version type (default: Minor)
|
|
438
|
+
branch_name: Git branch name (optional)
|
|
439
|
+
progress: Progress percentage 0-100 (default: 0)
|
|
440
|
+
|
|
441
|
+
Example:
|
|
442
|
+
$ notion versions create "v1.0.0" proj_123 --type Major
|
|
443
|
+
"""
|
|
444
|
+
async def _create() -> str:
|
|
445
|
+
try:
|
|
446
|
+
client = get_client()
|
|
447
|
+
|
|
448
|
+
# Register SDK plugin
|
|
449
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
450
|
+
plugin = AgentsSDKPlugin()
|
|
451
|
+
plugin.initialize(client)
|
|
452
|
+
client.register_sdk_plugin(plugin)
|
|
453
|
+
|
|
454
|
+
# Create version
|
|
455
|
+
manager = client.plugin_manager("versions")
|
|
456
|
+
version = await manager.create(
|
|
457
|
+
name=name,
|
|
458
|
+
project_id=project_id,
|
|
459
|
+
status=status,
|
|
460
|
+
version_type=version_type,
|
|
461
|
+
branch_name=branch_name,
|
|
462
|
+
progress=progress,
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
return format_success({
|
|
466
|
+
"message": "Version created successfully",
|
|
467
|
+
"id": version.id,
|
|
468
|
+
"name": version.name,
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
except Exception as e:
|
|
472
|
+
return format_error("CREATE_VERSION_ERROR", str(e), retry=False)
|
|
473
|
+
|
|
474
|
+
return asyncio.run(_create())
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
# ===== TASKS =====
|
|
478
|
+
|
|
479
|
+
def tasks_list(
|
|
480
|
+
version_id: Optional[str] = typer.Option(None, "--version-id", "-v", help="Filter by version ID"),
|
|
481
|
+
status: Optional[str] = typer.Option(None, "--status", "-s", help="Filter by status"),
|
|
482
|
+
) -> str:
|
|
483
|
+
"""
|
|
484
|
+
List all tasks, optionally filtered.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
version_id: Filter by version ID (optional)
|
|
488
|
+
status: Filter by status (optional)
|
|
489
|
+
|
|
490
|
+
Example:
|
|
491
|
+
$ notion tasks list
|
|
492
|
+
$ notion tasks list --version-id ver_123 --status Backlog
|
|
493
|
+
"""
|
|
494
|
+
async def _list() -> str:
|
|
495
|
+
try:
|
|
496
|
+
client = get_client()
|
|
497
|
+
|
|
498
|
+
# Register SDK plugin
|
|
499
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
500
|
+
plugin = AgentsSDKPlugin()
|
|
501
|
+
plugin.initialize(client)
|
|
502
|
+
client.register_sdk_plugin(plugin)
|
|
503
|
+
|
|
504
|
+
# Get manager
|
|
505
|
+
manager = client.plugin_manager("tasks")
|
|
506
|
+
tasks = await manager.list(version_id=version_id, status=status)
|
|
507
|
+
|
|
508
|
+
return format_success({
|
|
509
|
+
"tasks": [
|
|
510
|
+
{
|
|
511
|
+
"id": t.id,
|
|
512
|
+
"title": t.title,
|
|
513
|
+
"status": t.status,
|
|
514
|
+
"type": t.task_type,
|
|
515
|
+
"priority": t.priority,
|
|
516
|
+
"version_id": t.version_id,
|
|
517
|
+
"estimated_hours": t.estimated_hours,
|
|
518
|
+
}
|
|
519
|
+
for t in tasks
|
|
520
|
+
],
|
|
521
|
+
"total": len(tasks),
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
except Exception as e:
|
|
525
|
+
return format_error("LIST_TASKS_ERROR", str(e), retry=False)
|
|
526
|
+
|
|
527
|
+
return asyncio.run(_list())
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def tasks_get(task_id: str) -> str:
|
|
531
|
+
"""
|
|
532
|
+
Get a task by ID.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
task_id: Task page ID
|
|
536
|
+
|
|
537
|
+
Example:
|
|
538
|
+
$ notion tasks get task_123
|
|
539
|
+
"""
|
|
540
|
+
async def _get() -> str:
|
|
541
|
+
try:
|
|
542
|
+
client = get_client()
|
|
543
|
+
|
|
544
|
+
# Register SDK plugin
|
|
545
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
546
|
+
plugin = AgentsSDKPlugin()
|
|
547
|
+
plugin.initialize(client)
|
|
548
|
+
client.register_sdk_plugin(plugin)
|
|
549
|
+
|
|
550
|
+
# Get task
|
|
551
|
+
manager = client.plugin_manager("tasks")
|
|
552
|
+
task = await manager.get(task_id)
|
|
553
|
+
|
|
554
|
+
return format_success({
|
|
555
|
+
"id": task.id,
|
|
556
|
+
"title": task.title,
|
|
557
|
+
"status": task.status,
|
|
558
|
+
"type": task.task_type,
|
|
559
|
+
"priority": task.priority,
|
|
560
|
+
"version_id": task.version_id,
|
|
561
|
+
"dependency_ids": task.dependency_ids,
|
|
562
|
+
"estimated_hours": task.estimated_hours,
|
|
563
|
+
"actual_hours": task.actual_hours,
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
except Exception as e:
|
|
567
|
+
return format_error("GET_TASK_ERROR", str(e), retry=False)
|
|
568
|
+
|
|
569
|
+
return asyncio.run(_get())
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def tasks_create(
|
|
573
|
+
title: str,
|
|
574
|
+
version_id: str,
|
|
575
|
+
status: str = typer.Option("Backlog", "--status", help="Task status"),
|
|
576
|
+
task_type: str = typer.Option("New Feature", "--type", help="Task type"),
|
|
577
|
+
priority: str = typer.Option("Medium", "--priority", "-p", help="Task priority"),
|
|
578
|
+
dependencies: Optional[str] = typer.Option(None, "--dependencies", "-d", help="Comma-separated dependency task IDs"),
|
|
579
|
+
estimated_hours: Optional[int] = typer.Option(None, "--estimate", "-e", help="Estimated hours"),
|
|
580
|
+
) -> str:
|
|
581
|
+
"""
|
|
582
|
+
Create a new task.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
title: Task title
|
|
586
|
+
version_id: Parent version ID
|
|
587
|
+
status: Task status (default: Backlog)
|
|
588
|
+
task_type: Task type (default: New Feature)
|
|
589
|
+
priority: Task priority (default: Medium)
|
|
590
|
+
dependencies: Comma-separated dependency task IDs (optional)
|
|
591
|
+
estimated_hours: Estimated hours (optional)
|
|
592
|
+
|
|
593
|
+
Example:
|
|
594
|
+
$ notion tasks create "Fix authentication bug" ver_123 --priority High --type "Bug Fix"
|
|
595
|
+
"""
|
|
596
|
+
async def _create() -> str:
|
|
597
|
+
try:
|
|
598
|
+
client = get_client()
|
|
599
|
+
|
|
600
|
+
# Register SDK plugin
|
|
601
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
602
|
+
plugin = AgentsSDKPlugin()
|
|
603
|
+
plugin.initialize(client)
|
|
604
|
+
client.register_sdk_plugin(plugin)
|
|
605
|
+
|
|
606
|
+
# Parse dependencies
|
|
607
|
+
dependency_ids = dependencies.split(",") if dependencies else None
|
|
608
|
+
|
|
609
|
+
# Create task
|
|
610
|
+
manager = client.plugin_manager("tasks")
|
|
611
|
+
task = await manager.create(
|
|
612
|
+
title=title,
|
|
613
|
+
version_id=version_id,
|
|
614
|
+
status=status,
|
|
615
|
+
task_type=task_type,
|
|
616
|
+
priority=priority,
|
|
617
|
+
dependency_ids=dependency_ids,
|
|
618
|
+
estimated_hours=estimated_hours,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
return format_success({
|
|
622
|
+
"message": "Task created successfully",
|
|
623
|
+
"id": task.id,
|
|
624
|
+
"title": task.title,
|
|
625
|
+
"status": task.status,
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
except Exception as e:
|
|
629
|
+
return format_error("CREATE_TASK_ERROR", str(e), retry=False)
|
|
630
|
+
|
|
631
|
+
return asyncio.run(_create())
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
# ===== TASK WORKFLOW COMMANDS =====
|
|
635
|
+
|
|
636
|
+
def tasks_next(
|
|
637
|
+
project_id: Optional[str] = typer.Option(None, "--project-id", "-p", help="Filter by project ID"),
|
|
638
|
+
) -> str:
|
|
639
|
+
"""
|
|
640
|
+
Find the next available task to work on.
|
|
641
|
+
|
|
642
|
+
Finds a task that is:
|
|
643
|
+
- In Backlog or Claimed status
|
|
644
|
+
- Has all dependencies completed
|
|
645
|
+
|
|
646
|
+
Args:
|
|
647
|
+
project_id: Filter by project ID (optional)
|
|
648
|
+
|
|
649
|
+
Example:
|
|
650
|
+
$ notion tasks next
|
|
651
|
+
$ notion tasks next --project-id proj_123
|
|
652
|
+
"""
|
|
653
|
+
async def _next() -> str:
|
|
654
|
+
try:
|
|
655
|
+
client = get_client()
|
|
656
|
+
|
|
657
|
+
# Register SDK plugin
|
|
658
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
659
|
+
plugin = AgentsSDKPlugin()
|
|
660
|
+
plugin.initialize(client)
|
|
661
|
+
client.register_sdk_plugin(plugin)
|
|
662
|
+
|
|
663
|
+
# Get manager
|
|
664
|
+
manager = client.plugin_manager("tasks")
|
|
665
|
+
task = await manager.next(project_id=project_id)
|
|
666
|
+
|
|
667
|
+
if not task:
|
|
668
|
+
return format_success({
|
|
669
|
+
"message": "No available tasks found",
|
|
670
|
+
"task": None,
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
return format_success({
|
|
674
|
+
"message": "Found available task",
|
|
675
|
+
"task": {
|
|
676
|
+
"id": task.id,
|
|
677
|
+
"title": task.title,
|
|
678
|
+
"status": task.status,
|
|
679
|
+
"priority": task.priority,
|
|
680
|
+
"version_id": task.version_id,
|
|
681
|
+
"can_start": True,
|
|
682
|
+
},
|
|
683
|
+
})
|
|
684
|
+
|
|
685
|
+
except Exception as e:
|
|
686
|
+
return format_error("FIND_NEXT_TASK_ERROR", str(e), retry=False)
|
|
687
|
+
|
|
688
|
+
return asyncio.run(_next())
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
def tasks_claim(task_id: str) -> str:
|
|
692
|
+
"""
|
|
693
|
+
Claim a task (transition to Claimed status).
|
|
694
|
+
|
|
695
|
+
Args:
|
|
696
|
+
task_id: Task page ID
|
|
697
|
+
|
|
698
|
+
Example:
|
|
699
|
+
$ notion tasks claim task_123
|
|
700
|
+
"""
|
|
701
|
+
async def _claim() -> str:
|
|
702
|
+
try:
|
|
703
|
+
client = get_client()
|
|
704
|
+
|
|
705
|
+
# Register SDK plugin
|
|
706
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
707
|
+
plugin = AgentsSDKPlugin()
|
|
708
|
+
plugin.initialize(client)
|
|
709
|
+
client.register_sdk_plugin(plugin)
|
|
710
|
+
|
|
711
|
+
# Get and claim task
|
|
712
|
+
manager = client.plugin_manager("tasks")
|
|
713
|
+
task = await manager.get(task_id)
|
|
714
|
+
await task.claim()
|
|
715
|
+
|
|
716
|
+
# Get agent ID for tracking
|
|
717
|
+
agent_id = get_or_create_agent_id()
|
|
718
|
+
|
|
719
|
+
return format_success({
|
|
720
|
+
"message": f"Task claimed by agent {agent_id}",
|
|
721
|
+
"task_id": task.id,
|
|
722
|
+
"title": task.title,
|
|
723
|
+
"status": task.status,
|
|
724
|
+
"agent_id": agent_id,
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
except Exception as e:
|
|
728
|
+
return format_error("CLAIM_TASK_ERROR", str(e), retry=False)
|
|
729
|
+
|
|
730
|
+
return asyncio.run(_claim())
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def tasks_start(task_id: str) -> str:
|
|
734
|
+
"""
|
|
735
|
+
Start working on a task (transition to In Progress).
|
|
736
|
+
|
|
737
|
+
Args:
|
|
738
|
+
task_id: Task page ID
|
|
739
|
+
|
|
740
|
+
Example:
|
|
741
|
+
$ notion tasks start task_123
|
|
742
|
+
"""
|
|
743
|
+
async def _start() -> str:
|
|
744
|
+
try:
|
|
745
|
+
client = get_client()
|
|
746
|
+
|
|
747
|
+
# Register SDK plugin
|
|
748
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
749
|
+
plugin = AgentsSDKPlugin()
|
|
750
|
+
plugin.initialize(client)
|
|
751
|
+
client.register_sdk_plugin(plugin)
|
|
752
|
+
|
|
753
|
+
# Get and start task
|
|
754
|
+
manager = client.plugin_manager("tasks")
|
|
755
|
+
task = await manager.get(task_id)
|
|
756
|
+
|
|
757
|
+
# Check if can start
|
|
758
|
+
if not await task.can_start():
|
|
759
|
+
return format_error(
|
|
760
|
+
"TASK_BLOCKED",
|
|
761
|
+
"Task has incomplete dependencies",
|
|
762
|
+
retry=False,
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
await task.start()
|
|
766
|
+
|
|
767
|
+
# Get agent ID for tracking
|
|
768
|
+
agent_id = get_or_create_agent_id()
|
|
769
|
+
|
|
770
|
+
return format_success({
|
|
771
|
+
"message": f"Task started by agent {agent_id}",
|
|
772
|
+
"task_id": task.id,
|
|
773
|
+
"title": task.title,
|
|
774
|
+
"status": task.status,
|
|
775
|
+
"agent_id": agent_id,
|
|
776
|
+
})
|
|
777
|
+
|
|
778
|
+
except Exception as e:
|
|
779
|
+
return format_error("START_TASK_ERROR", str(e), retry=False)
|
|
780
|
+
|
|
781
|
+
return asyncio.run(_start())
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
def tasks_complete(
|
|
785
|
+
task_id: str,
|
|
786
|
+
actual_hours: Optional[int] = typer.Option(None, "--actual-hours", "-a", help="Actual hours spent"),
|
|
787
|
+
) -> str:
|
|
788
|
+
"""
|
|
789
|
+
Complete a task (transition to Completed).
|
|
790
|
+
|
|
791
|
+
Args:
|
|
792
|
+
task_id: Task page ID
|
|
793
|
+
actual_hours: Actual hours spent (optional)
|
|
794
|
+
|
|
795
|
+
Example:
|
|
796
|
+
$ notion tasks complete task_123 --actual-hours 3
|
|
797
|
+
"""
|
|
798
|
+
async def _complete() -> str:
|
|
799
|
+
try:
|
|
800
|
+
client = get_client()
|
|
801
|
+
|
|
802
|
+
# Register SDK plugin
|
|
803
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
804
|
+
plugin = AgentsSDKPlugin()
|
|
805
|
+
plugin.initialize(client)
|
|
806
|
+
client.register_sdk_plugin(plugin)
|
|
807
|
+
|
|
808
|
+
# Get and complete task
|
|
809
|
+
manager = client.plugin_manager("tasks")
|
|
810
|
+
task = await manager.get(task_id)
|
|
811
|
+
await task.complete(actual_hours=actual_hours)
|
|
812
|
+
|
|
813
|
+
# Get agent ID for tracking
|
|
814
|
+
agent_id = get_or_create_agent_id()
|
|
815
|
+
|
|
816
|
+
return format_success({
|
|
817
|
+
"message": f"Task completed by agent {agent_id}",
|
|
818
|
+
"task_id": task.id,
|
|
819
|
+
"title": task.title,
|
|
820
|
+
"status": task.status,
|
|
821
|
+
"actual_hours": task.actual_hours,
|
|
822
|
+
"agent_id": agent_id,
|
|
823
|
+
})
|
|
824
|
+
|
|
825
|
+
except Exception as e:
|
|
826
|
+
return format_error("COMPLETE_TASK_ERROR", str(e), retry=False)
|
|
827
|
+
|
|
828
|
+
return asyncio.run(_complete())
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
def tasks_can_start(task_id: str) -> str:
|
|
832
|
+
"""
|
|
833
|
+
Check if a task can start (all dependencies completed).
|
|
834
|
+
|
|
835
|
+
Args:
|
|
836
|
+
task_id: Task page ID
|
|
837
|
+
|
|
838
|
+
Example:
|
|
839
|
+
$ notion tasks can-start task_123
|
|
840
|
+
"""
|
|
841
|
+
async def _can_start() -> str:
|
|
842
|
+
try:
|
|
843
|
+
client = get_client()
|
|
844
|
+
|
|
845
|
+
# Register SDK plugin
|
|
846
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
847
|
+
plugin = AgentsSDKPlugin()
|
|
848
|
+
plugin.initialize(client)
|
|
849
|
+
client.register_sdk_plugin(plugin)
|
|
850
|
+
|
|
851
|
+
# Get task and check
|
|
852
|
+
manager = client.plugin_manager("tasks")
|
|
853
|
+
task = await manager.get(task_id)
|
|
854
|
+
can_start = await task.can_start()
|
|
855
|
+
|
|
856
|
+
if not can_start:
|
|
857
|
+
# Get incomplete dependencies
|
|
858
|
+
incomplete = []
|
|
859
|
+
for dep in await task.dependencies():
|
|
860
|
+
if dep.status != "Completed":
|
|
861
|
+
incomplete.append({
|
|
862
|
+
"id": dep.id,
|
|
863
|
+
"title": dep.title,
|
|
864
|
+
"status": dep.status,
|
|
865
|
+
})
|
|
866
|
+
|
|
867
|
+
return format_success({
|
|
868
|
+
"task_id": task.id,
|
|
869
|
+
"can_start": False,
|
|
870
|
+
"incomplete_dependencies": incomplete,
|
|
871
|
+
})
|
|
872
|
+
|
|
873
|
+
return format_success({
|
|
874
|
+
"task_id": task.id,
|
|
875
|
+
"can_start": True,
|
|
876
|
+
"message": "All dependencies are completed",
|
|
877
|
+
})
|
|
878
|
+
|
|
879
|
+
except Exception as e:
|
|
880
|
+
return format_error("CAN_START_ERROR", str(e), retry=False)
|
|
881
|
+
|
|
882
|
+
return asyncio.run(_can_start())
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
# ===== IDEAS =====
|
|
886
|
+
|
|
887
|
+
def ideas_list(
|
|
888
|
+
project_id: Optional[str] = None,
|
|
889
|
+
category: Optional[str] = None,
|
|
890
|
+
status: Optional[str] = None,
|
|
891
|
+
effort_estimate: Optional[str] = None,
|
|
892
|
+
) -> str:
|
|
893
|
+
"""
|
|
894
|
+
List ideas with optional filtering.
|
|
895
|
+
|
|
896
|
+
Args:
|
|
897
|
+
project_id: Filter by project ID
|
|
898
|
+
category: Filter by category
|
|
899
|
+
status: Filter by status
|
|
900
|
+
effort_estimate: Filter by effort estimate
|
|
901
|
+
|
|
902
|
+
Example:
|
|
903
|
+
$ notion ideas list --project-id proj_123 --status Proposed
|
|
904
|
+
"""
|
|
905
|
+
async def _list() -> str:
|
|
906
|
+
try:
|
|
907
|
+
client = get_client()
|
|
908
|
+
|
|
909
|
+
# Register SDK plugin
|
|
910
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
911
|
+
plugin = AgentsSDKPlugin()
|
|
912
|
+
plugin.initialize(client)
|
|
913
|
+
client.register_sdk_plugin(plugin)
|
|
914
|
+
|
|
915
|
+
# Get manager and list
|
|
916
|
+
manager = client.plugin_manager("ideas")
|
|
917
|
+
ideas = await manager.list(
|
|
918
|
+
project_id=project_id,
|
|
919
|
+
category=category,
|
|
920
|
+
status=status,
|
|
921
|
+
effort_estimate=effort_estimate,
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
return format_success({
|
|
925
|
+
"ideas": [
|
|
926
|
+
{
|
|
927
|
+
"id": idea.id,
|
|
928
|
+
"title": idea.title,
|
|
929
|
+
"category": idea.category,
|
|
930
|
+
"status": idea.status,
|
|
931
|
+
"effort_estimate": idea.effort_estimate,
|
|
932
|
+
}
|
|
933
|
+
for idea in ideas
|
|
934
|
+
],
|
|
935
|
+
"total": len(ideas),
|
|
936
|
+
})
|
|
937
|
+
|
|
938
|
+
except Exception as e:
|
|
939
|
+
return format_error("LIST_IDEAS_ERROR", str(e), retry=False)
|
|
940
|
+
|
|
941
|
+
return asyncio.run(_list())
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
def ideas_get(idea_id: str) -> str:
|
|
945
|
+
"""
|
|
946
|
+
Get an idea by ID.
|
|
947
|
+
|
|
948
|
+
Args:
|
|
949
|
+
idea_id: Idea page ID
|
|
950
|
+
|
|
951
|
+
Example:
|
|
952
|
+
$ notion ideas get idea_123
|
|
953
|
+
"""
|
|
954
|
+
async def _get() -> str:
|
|
955
|
+
try:
|
|
956
|
+
client = get_client()
|
|
957
|
+
|
|
958
|
+
# Register SDK plugin
|
|
959
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
960
|
+
plugin = AgentsSDKPlugin()
|
|
961
|
+
plugin.initialize(client)
|
|
962
|
+
client.register_sdk_plugin(plugin)
|
|
963
|
+
|
|
964
|
+
# Get idea
|
|
965
|
+
from better_notion.plugins.official.agents_sdk.models import Idea
|
|
966
|
+
idea = await Idea.get(idea_id, client=client)
|
|
967
|
+
|
|
968
|
+
return format_success({
|
|
969
|
+
"id": idea.id,
|
|
970
|
+
"title": idea.title,
|
|
971
|
+
"category": idea.category,
|
|
972
|
+
"status": idea.status,
|
|
973
|
+
"description": idea.description,
|
|
974
|
+
"effort_estimate": idea.effort_estimate,
|
|
975
|
+
"project_id": idea.project_id,
|
|
976
|
+
})
|
|
977
|
+
|
|
978
|
+
except Exception as e:
|
|
979
|
+
return format_error("GET_IDEA_ERROR", str(e), retry=False)
|
|
980
|
+
|
|
981
|
+
return asyncio.run(_get())
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
def ideas_create(
|
|
985
|
+
title: str,
|
|
986
|
+
project_id: str,
|
|
987
|
+
category: str = "enhancement",
|
|
988
|
+
description: str = "",
|
|
989
|
+
proposed_solution: str = "",
|
|
990
|
+
benefits: str = "",
|
|
991
|
+
effort_estimate: str = "M",
|
|
992
|
+
context: str = "",
|
|
993
|
+
) -> str:
|
|
994
|
+
"""
|
|
995
|
+
Create a new idea.
|
|
996
|
+
|
|
997
|
+
Args:
|
|
998
|
+
title: Idea title
|
|
999
|
+
project_id: Project ID
|
|
1000
|
+
category: Idea category (enhancement, feature, bugfix, optimization, research)
|
|
1001
|
+
description: Detailed description
|
|
1002
|
+
proposed_solution: Proposed solution
|
|
1003
|
+
benefits: Expected benefits
|
|
1004
|
+
effort_estimate: Effort estimate (XS, S, M, L, XL)
|
|
1005
|
+
context: Additional context
|
|
1006
|
+
|
|
1007
|
+
Example:
|
|
1008
|
+
$ notion ideas create "Add caching" --project-id proj_123 \\
|
|
1009
|
+
--category enhancement --effort-estimate M
|
|
1010
|
+
"""
|
|
1011
|
+
async def _create() -> str:
|
|
1012
|
+
try:
|
|
1013
|
+
client = get_client()
|
|
1014
|
+
|
|
1015
|
+
# Register SDK plugin
|
|
1016
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1017
|
+
plugin = AgentsSDKPlugin()
|
|
1018
|
+
plugin.initialize(client)
|
|
1019
|
+
client.register_sdk_plugin(plugin)
|
|
1020
|
+
|
|
1021
|
+
# Get workspace config
|
|
1022
|
+
workspace_config = get_workspace_config()
|
|
1023
|
+
database_id = workspace_config.get("Ideas")
|
|
1024
|
+
|
|
1025
|
+
if not database_id:
|
|
1026
|
+
return format_error("NO_DATABASE", "Ideas database not configured", retry=False)
|
|
1027
|
+
|
|
1028
|
+
# Create idea in Notion
|
|
1029
|
+
properties = {
|
|
1030
|
+
"title": {"title": [{"text": {"content": title}}]},
|
|
1031
|
+
"project_id": {"relation": [{"id": project_id}]},
|
|
1032
|
+
"category": {"select": {"name": category}},
|
|
1033
|
+
"status": {"select": {"name": "Proposed"}},
|
|
1034
|
+
"effort_estimate": {"select": {"name": effort_estimate}},
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
if description:
|
|
1038
|
+
properties["description"] = {
|
|
1039
|
+
"rich_text": [{"text": {"content": description}}]
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if proposed_solution:
|
|
1043
|
+
properties["proposed_solution"] = {
|
|
1044
|
+
"rich_text": [{"text": {"content": proposed_solution}}]
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if benefits:
|
|
1048
|
+
properties["benefits"] = {
|
|
1049
|
+
"rich_text": [{"text": {"content": benefits}}]
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
if context:
|
|
1053
|
+
properties["context"] = {
|
|
1054
|
+
"rich_text": [{"text": {"content": context}}]
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
response = await client._api.request(
|
|
1058
|
+
method="POST",
|
|
1059
|
+
path=f"databases/{database_id}",
|
|
1060
|
+
json={"properties": properties},
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
return format_success({
|
|
1064
|
+
"message": "Idea created successfully",
|
|
1065
|
+
"idea_id": response["id"],
|
|
1066
|
+
"title": title,
|
|
1067
|
+
"category": category,
|
|
1068
|
+
})
|
|
1069
|
+
|
|
1070
|
+
except Exception as e:
|
|
1071
|
+
return format_error("CREATE_IDEA_ERROR", str(e), retry=False)
|
|
1072
|
+
|
|
1073
|
+
return asyncio.run(_create())
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
def ideas_review(count: int = 10) -> str:
|
|
1077
|
+
"""
|
|
1078
|
+
Get a batch of ideas for review, prioritized by effort.
|
|
1079
|
+
|
|
1080
|
+
Args:
|
|
1081
|
+
count: Maximum number of ideas to return
|
|
1082
|
+
|
|
1083
|
+
Example:
|
|
1084
|
+
$ notion ideas review --count 5
|
|
1085
|
+
"""
|
|
1086
|
+
async def _review() -> str:
|
|
1087
|
+
try:
|
|
1088
|
+
client = get_client()
|
|
1089
|
+
|
|
1090
|
+
# Register SDK plugin
|
|
1091
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1092
|
+
plugin = AgentsSDKPlugin()
|
|
1093
|
+
plugin.initialize(client)
|
|
1094
|
+
client.register_sdk_plugin(plugin)
|
|
1095
|
+
|
|
1096
|
+
# Get manager and review batch
|
|
1097
|
+
manager = client.plugin_manager("ideas")
|
|
1098
|
+
ideas = await manager.review_batch(count=count)
|
|
1099
|
+
|
|
1100
|
+
return format_success({
|
|
1101
|
+
"ideas_for_review": [
|
|
1102
|
+
{
|
|
1103
|
+
"id": idea.id,
|
|
1104
|
+
"title": idea.title,
|
|
1105
|
+
"category": idea.category,
|
|
1106
|
+
"effort_estimate": idea.effort_estimate,
|
|
1107
|
+
"status": idea.status,
|
|
1108
|
+
}
|
|
1109
|
+
for idea in ideas
|
|
1110
|
+
],
|
|
1111
|
+
"total": len(ideas),
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
except Exception as e:
|
|
1115
|
+
return format_error("REVIEW_IDEAS_ERROR", str(e), retry=False)
|
|
1116
|
+
|
|
1117
|
+
return asyncio.run(_review())
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
def ideas_accept(idea_id: str) -> str:
|
|
1121
|
+
"""
|
|
1122
|
+
Accept an idea (moves to Accepted status).
|
|
1123
|
+
|
|
1124
|
+
Args:
|
|
1125
|
+
idea_id: Idea page ID
|
|
1126
|
+
|
|
1127
|
+
Example:
|
|
1128
|
+
$ notion ideas accept idea_123
|
|
1129
|
+
"""
|
|
1130
|
+
async def _accept() -> str:
|
|
1131
|
+
try:
|
|
1132
|
+
client = get_client()
|
|
1133
|
+
|
|
1134
|
+
# Register SDK plugin
|
|
1135
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1136
|
+
plugin = AgentsSDKPlugin()
|
|
1137
|
+
plugin.initialize(client)
|
|
1138
|
+
client.register_sdk_plugin(plugin)
|
|
1139
|
+
|
|
1140
|
+
# Get idea and accept
|
|
1141
|
+
from better_notion.plugins.official.agents_sdk.models import Idea
|
|
1142
|
+
idea = await Idea.get(idea_id, client=client)
|
|
1143
|
+
await idea.accept()
|
|
1144
|
+
|
|
1145
|
+
return format_success({
|
|
1146
|
+
"message": "Idea accepted successfully",
|
|
1147
|
+
"idea_id": idea.id,
|
|
1148
|
+
"status": idea.status,
|
|
1149
|
+
})
|
|
1150
|
+
|
|
1151
|
+
except Exception as e:
|
|
1152
|
+
return format_error("ACCEPT_IDEA_ERROR", str(e), retry=False)
|
|
1153
|
+
|
|
1154
|
+
return asyncio.run(_accept())
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
def ideas_reject(idea_id: str, reason: str = "") -> str:
|
|
1158
|
+
"""
|
|
1159
|
+
Reject an idea (moves to Rejected status).
|
|
1160
|
+
|
|
1161
|
+
Args:
|
|
1162
|
+
idea_id: Idea page ID
|
|
1163
|
+
reason: Reason for rejection
|
|
1164
|
+
|
|
1165
|
+
Example:
|
|
1166
|
+
$ notion ideas reject idea_123 --reason "Out of scope"
|
|
1167
|
+
"""
|
|
1168
|
+
async def _reject() -> str:
|
|
1169
|
+
try:
|
|
1170
|
+
client = get_client()
|
|
1171
|
+
|
|
1172
|
+
# Register SDK plugin
|
|
1173
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1174
|
+
plugin = AgentsSDKPlugin()
|
|
1175
|
+
plugin.initialize(client)
|
|
1176
|
+
client.register_sdk_plugin(plugin)
|
|
1177
|
+
|
|
1178
|
+
# Get idea and reject
|
|
1179
|
+
from better_notion.plugins.official.agents_sdk.models import Idea
|
|
1180
|
+
idea = await Idea.get(idea_id, client=client)
|
|
1181
|
+
await idea.reject()
|
|
1182
|
+
|
|
1183
|
+
return format_success({
|
|
1184
|
+
"message": "Idea rejected successfully",
|
|
1185
|
+
"idea_id": idea.id,
|
|
1186
|
+
"status": idea.status,
|
|
1187
|
+
"reason": reason,
|
|
1188
|
+
})
|
|
1189
|
+
|
|
1190
|
+
except Exception as e:
|
|
1191
|
+
return format_error("REJECT_IDEA_ERROR", str(e), retry=False)
|
|
1192
|
+
|
|
1193
|
+
return asyncio.run(_reject())
|
|
1194
|
+
|
|
1195
|
+
|
|
1196
|
+
# ===== WORK ISSUES =====
|
|
1197
|
+
|
|
1198
|
+
def work_issues_list(
|
|
1199
|
+
project_id: Optional[str] = None,
|
|
1200
|
+
task_id: Optional[str] = None,
|
|
1201
|
+
type: Optional[str] = None,
|
|
1202
|
+
severity: Optional[str] = None,
|
|
1203
|
+
status: Optional[str] = None,
|
|
1204
|
+
) -> str:
|
|
1205
|
+
"""
|
|
1206
|
+
List work issues with optional filtering.
|
|
1207
|
+
|
|
1208
|
+
Args:
|
|
1209
|
+
project_id: Filter by project ID
|
|
1210
|
+
task_id: Filter by task ID
|
|
1211
|
+
type: Filter by type
|
|
1212
|
+
severity: Filter by severity
|
|
1213
|
+
status: Filter by status
|
|
1214
|
+
|
|
1215
|
+
Example:
|
|
1216
|
+
$ notion work-issues list --project-id proj_123 --severity High
|
|
1217
|
+
"""
|
|
1218
|
+
async def _list() -> str:
|
|
1219
|
+
try:
|
|
1220
|
+
client = get_client()
|
|
1221
|
+
|
|
1222
|
+
# Register SDK plugin
|
|
1223
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1224
|
+
plugin = AgentsSDKPlugin()
|
|
1225
|
+
plugin.initialize(client)
|
|
1226
|
+
client.register_sdk_plugin(plugin)
|
|
1227
|
+
|
|
1228
|
+
# Get manager and list
|
|
1229
|
+
manager = client.plugin_manager("work_issues")
|
|
1230
|
+
issues = await manager.list(
|
|
1231
|
+
project_id=project_id,
|
|
1232
|
+
task_id=task_id,
|
|
1233
|
+
type_=type,
|
|
1234
|
+
severity=severity,
|
|
1235
|
+
status=status,
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
return format_success({
|
|
1239
|
+
"work_issues": [
|
|
1240
|
+
{
|
|
1241
|
+
"id": issue.id,
|
|
1242
|
+
"title": issue.title,
|
|
1243
|
+
"type": issue.type,
|
|
1244
|
+
"severity": issue.severity,
|
|
1245
|
+
"status": issue.status,
|
|
1246
|
+
}
|
|
1247
|
+
for issue in issues
|
|
1248
|
+
],
|
|
1249
|
+
"total": len(issues),
|
|
1250
|
+
})
|
|
1251
|
+
|
|
1252
|
+
except Exception as e:
|
|
1253
|
+
return format_error("LIST_WORK_ISSUES_ERROR", str(e), retry=False)
|
|
1254
|
+
|
|
1255
|
+
return asyncio.run(_list())
|
|
1256
|
+
|
|
1257
|
+
|
|
1258
|
+
def work_issues_get(issue_id: str) -> str:
|
|
1259
|
+
"""
|
|
1260
|
+
Get a work issue by ID.
|
|
1261
|
+
|
|
1262
|
+
Args:
|
|
1263
|
+
issue_id: Work issue page ID
|
|
1264
|
+
|
|
1265
|
+
Example:
|
|
1266
|
+
$ notion work-issues get issue_123
|
|
1267
|
+
"""
|
|
1268
|
+
async def _get() -> str:
|
|
1269
|
+
try:
|
|
1270
|
+
client = get_client()
|
|
1271
|
+
|
|
1272
|
+
# Register SDK plugin
|
|
1273
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1274
|
+
plugin = AgentsSDKPlugin()
|
|
1275
|
+
plugin.initialize(client)
|
|
1276
|
+
client.register_sdk_plugin(plugin)
|
|
1277
|
+
|
|
1278
|
+
# Get issue
|
|
1279
|
+
from better_notion.plugins.official.agents_sdk.models import WorkIssue
|
|
1280
|
+
issue = await WorkIssue.get(issue_id, client=client)
|
|
1281
|
+
|
|
1282
|
+
return format_success({
|
|
1283
|
+
"id": issue.id,
|
|
1284
|
+
"title": issue.title,
|
|
1285
|
+
"type": issue.type,
|
|
1286
|
+
"severity": issue.severity,
|
|
1287
|
+
"status": issue.status,
|
|
1288
|
+
"description": issue.description,
|
|
1289
|
+
"project_id": issue.project_id,
|
|
1290
|
+
"task_id": issue.task_id,
|
|
1291
|
+
})
|
|
1292
|
+
|
|
1293
|
+
except Exception as e:
|
|
1294
|
+
return format_error("GET_WORK_ISSUE_ERROR", str(e), retry=False)
|
|
1295
|
+
|
|
1296
|
+
return asyncio.run(_get())
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
def work_issues_create(
|
|
1300
|
+
title: str,
|
|
1301
|
+
project_id: str,
|
|
1302
|
+
type: str = "Technical",
|
|
1303
|
+
severity: str = "Medium",
|
|
1304
|
+
description: str = "",
|
|
1305
|
+
context: str = "",
|
|
1306
|
+
task_id: Optional[str] = None,
|
|
1307
|
+
) -> str:
|
|
1308
|
+
"""
|
|
1309
|
+
Create a new work issue.
|
|
1310
|
+
|
|
1311
|
+
Args:
|
|
1312
|
+
title: Issue title
|
|
1313
|
+
project_id: Project ID
|
|
1314
|
+
type: Issue type (Technical, Process, Resource, Other)
|
|
1315
|
+
severity: Issue severity (Low, Medium, High, Critical)
|
|
1316
|
+
description: Detailed description
|
|
1317
|
+
context: Additional context
|
|
1318
|
+
task_id: Related task ID (optional)
|
|
1319
|
+
|
|
1320
|
+
Example:
|
|
1321
|
+
$ notion work-issues create "API error" --project-id proj_123 \\
|
|
1322
|
+
--type Technical --severity High
|
|
1323
|
+
"""
|
|
1324
|
+
async def _create() -> str:
|
|
1325
|
+
try:
|
|
1326
|
+
client = get_client()
|
|
1327
|
+
|
|
1328
|
+
# Register SDK plugin
|
|
1329
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1330
|
+
plugin = AgentsSDKPlugin()
|
|
1331
|
+
plugin.initialize(client)
|
|
1332
|
+
client.register_sdk_plugin(plugin)
|
|
1333
|
+
|
|
1334
|
+
# Get workspace config
|
|
1335
|
+
workspace_config = get_workspace_config()
|
|
1336
|
+
database_id = workspace_config.get("Work Issues")
|
|
1337
|
+
|
|
1338
|
+
if not database_id:
|
|
1339
|
+
return format_error("NO_DATABASE", "Work Issues database not configured", retry=False)
|
|
1340
|
+
|
|
1341
|
+
# Create issue in Notion
|
|
1342
|
+
properties = {
|
|
1343
|
+
"title": {"title": [{"text": {"content": title}}]},
|
|
1344
|
+
"project_id": {"relation": [{"id": project_id}]},
|
|
1345
|
+
"type": {"select": {"name": type}},
|
|
1346
|
+
"severity": {"select": {"name": severity}},
|
|
1347
|
+
"status": {"select": {"name": "Open"}},
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
if description:
|
|
1351
|
+
properties["description"] = {
|
|
1352
|
+
"rich_text": [{"text": {"content": description}}]
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
if context:
|
|
1356
|
+
properties["context"] = {
|
|
1357
|
+
"rich_text": [{"text": {"content": context}}]
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
if task_id:
|
|
1361
|
+
properties["task_id"] = {"relation": [{"id": task_id}]}
|
|
1362
|
+
|
|
1363
|
+
response = await client._api.request(
|
|
1364
|
+
method="POST",
|
|
1365
|
+
path=f"databases/{database_id}",
|
|
1366
|
+
json={"properties": properties},
|
|
1367
|
+
)
|
|
1368
|
+
|
|
1369
|
+
return format_success({
|
|
1370
|
+
"message": "Work issue created successfully",
|
|
1371
|
+
"issue_id": response["id"],
|
|
1372
|
+
"title": title,
|
|
1373
|
+
"type": type,
|
|
1374
|
+
"severity": severity,
|
|
1375
|
+
})
|
|
1376
|
+
|
|
1377
|
+
except Exception as e:
|
|
1378
|
+
return format_error("CREATE_WORK_ISSUE_ERROR", str(e), retry=False)
|
|
1379
|
+
|
|
1380
|
+
return asyncio.run(_create())
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
def work_issues_resolve(issue_id: str, resolution: str = "") -> str:
|
|
1384
|
+
"""
|
|
1385
|
+
Resolve a work issue.
|
|
1386
|
+
|
|
1387
|
+
Args:
|
|
1388
|
+
issue_id: Work issue page ID
|
|
1389
|
+
resolution: Resolution description
|
|
1390
|
+
|
|
1391
|
+
Example:
|
|
1392
|
+
$ notion work-issues resolve issue_123 --resolution "Fixed dependency version"
|
|
1393
|
+
"""
|
|
1394
|
+
async def _resolve() -> str:
|
|
1395
|
+
try:
|
|
1396
|
+
client = get_client()
|
|
1397
|
+
|
|
1398
|
+
# Register SDK plugin
|
|
1399
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1400
|
+
plugin = AgentsSDKPlugin()
|
|
1401
|
+
plugin.initialize(client)
|
|
1402
|
+
client.register_sdk_plugin(plugin)
|
|
1403
|
+
|
|
1404
|
+
# Get issue and resolve
|
|
1405
|
+
from better_notion.plugins.official.agents_sdk.models import WorkIssue
|
|
1406
|
+
issue = await WorkIssue.get(issue_id, client=client)
|
|
1407
|
+
await issue.resolve()
|
|
1408
|
+
|
|
1409
|
+
return format_success({
|
|
1410
|
+
"message": "Work issue resolved successfully",
|
|
1411
|
+
"issue_id": issue.id,
|
|
1412
|
+
"status": issue.status,
|
|
1413
|
+
"resolution": resolution,
|
|
1414
|
+
})
|
|
1415
|
+
|
|
1416
|
+
except Exception as e:
|
|
1417
|
+
return format_error("RESOLVE_WORK_ISSUE_ERROR", str(e), retry=False)
|
|
1418
|
+
|
|
1419
|
+
return asyncio.run(_resolve())
|
|
1420
|
+
|
|
1421
|
+
|
|
1422
|
+
def work_issues_blockers(project_id: str) -> str:
|
|
1423
|
+
"""
|
|
1424
|
+
Find all blocking work issues for a project.
|
|
1425
|
+
|
|
1426
|
+
Args:
|
|
1427
|
+
project_id: Project ID
|
|
1428
|
+
|
|
1429
|
+
Example:
|
|
1430
|
+
$ notion work-issues blockers proj_123
|
|
1431
|
+
"""
|
|
1432
|
+
async def _blockers() -> str:
|
|
1433
|
+
try:
|
|
1434
|
+
client = get_client()
|
|
1435
|
+
|
|
1436
|
+
# Register SDK plugin
|
|
1437
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1438
|
+
plugin = AgentsSDKPlugin()
|
|
1439
|
+
plugin.initialize(client)
|
|
1440
|
+
client.register_sdk_plugin(plugin)
|
|
1441
|
+
|
|
1442
|
+
# Get manager and find blockers
|
|
1443
|
+
manager = client.plugin_manager("work_issues")
|
|
1444
|
+
blockers = await manager.find_blockers(project_id)
|
|
1445
|
+
|
|
1446
|
+
return format_success({
|
|
1447
|
+
"blocking_issues": [
|
|
1448
|
+
{
|
|
1449
|
+
"id": issue.id,
|
|
1450
|
+
"title": issue.title,
|
|
1451
|
+
"type": issue.type,
|
|
1452
|
+
"severity": issue.severity,
|
|
1453
|
+
"status": issue.status,
|
|
1454
|
+
}
|
|
1455
|
+
for issue in blockers
|
|
1456
|
+
],
|
|
1457
|
+
"total": len(blockers),
|
|
1458
|
+
})
|
|
1459
|
+
|
|
1460
|
+
except Exception as e:
|
|
1461
|
+
return format_error("FIND_BLOCKERS_ERROR", str(e), retry=False)
|
|
1462
|
+
|
|
1463
|
+
return asyncio.run(_blockers())
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
# ===== INCIDENTS =====
|
|
1467
|
+
|
|
1468
|
+
def incidents_list(
|
|
1469
|
+
project_id: Optional[str] = None,
|
|
1470
|
+
version_id: Optional[str] = None,
|
|
1471
|
+
severity: Optional[str] = None,
|
|
1472
|
+
status: Optional[str] = None,
|
|
1473
|
+
) -> str:
|
|
1474
|
+
"""
|
|
1475
|
+
List incidents with optional filtering.
|
|
1476
|
+
|
|
1477
|
+
Args:
|
|
1478
|
+
project_id: Filter by project ID
|
|
1479
|
+
version_id: Filter by affected version ID
|
|
1480
|
+
severity: Filter by severity
|
|
1481
|
+
status: Filter by status
|
|
1482
|
+
|
|
1483
|
+
Example:
|
|
1484
|
+
$ notion incidents list --project-id proj_123 --severity Critical
|
|
1485
|
+
"""
|
|
1486
|
+
async def _list() -> str:
|
|
1487
|
+
try:
|
|
1488
|
+
client = get_client()
|
|
1489
|
+
|
|
1490
|
+
# Register SDK plugin
|
|
1491
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1492
|
+
plugin = AgentsSDKPlugin()
|
|
1493
|
+
plugin.initialize(client)
|
|
1494
|
+
client.register_sdk_plugin(plugin)
|
|
1495
|
+
|
|
1496
|
+
# Get manager and list
|
|
1497
|
+
manager = client.plugin_manager("incidents")
|
|
1498
|
+
incidents = await manager.list(
|
|
1499
|
+
project_id=project_id,
|
|
1500
|
+
version_id=version_id,
|
|
1501
|
+
severity=severity,
|
|
1502
|
+
status=status,
|
|
1503
|
+
)
|
|
1504
|
+
|
|
1505
|
+
return format_success({
|
|
1506
|
+
"incidents": [
|
|
1507
|
+
{
|
|
1508
|
+
"id": incident.id,
|
|
1509
|
+
"title": incident.title,
|
|
1510
|
+
"severity": incident.severity,
|
|
1511
|
+
"status": incident.status,
|
|
1512
|
+
"type": incident.type,
|
|
1513
|
+
}
|
|
1514
|
+
for incident in incidents
|
|
1515
|
+
],
|
|
1516
|
+
"total": len(incidents),
|
|
1517
|
+
})
|
|
1518
|
+
|
|
1519
|
+
except Exception as e:
|
|
1520
|
+
return format_error("LIST_INCIDENTS_ERROR", str(e), retry=False)
|
|
1521
|
+
|
|
1522
|
+
return asyncio.run(_list())
|
|
1523
|
+
|
|
1524
|
+
|
|
1525
|
+
def incidents_get(incident_id: str) -> str:
|
|
1526
|
+
"""
|
|
1527
|
+
Get an incident by ID.
|
|
1528
|
+
|
|
1529
|
+
Args:
|
|
1530
|
+
incident_id: Incident page ID
|
|
1531
|
+
|
|
1532
|
+
Example:
|
|
1533
|
+
$ notion incidents get incident_123
|
|
1534
|
+
"""
|
|
1535
|
+
async def _get() -> str:
|
|
1536
|
+
try:
|
|
1537
|
+
client = get_client()
|
|
1538
|
+
|
|
1539
|
+
# Register SDK plugin
|
|
1540
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1541
|
+
plugin = AgentsSDKPlugin()
|
|
1542
|
+
plugin.initialize(client)
|
|
1543
|
+
client.register_sdk_plugin(plugin)
|
|
1544
|
+
|
|
1545
|
+
# Get incident
|
|
1546
|
+
from better_notion.plugins.official.agents_sdk.models import Incident
|
|
1547
|
+
incident = await Incident.get(incident_id, client=client)
|
|
1548
|
+
|
|
1549
|
+
return format_success({
|
|
1550
|
+
"id": incident.id,
|
|
1551
|
+
"title": incident.title,
|
|
1552
|
+
"severity": incident.severity,
|
|
1553
|
+
"status": incident.status,
|
|
1554
|
+
"type": incident.type,
|
|
1555
|
+
"project_id": incident.project_id,
|
|
1556
|
+
"affected_version_id": incident.affected_version_id,
|
|
1557
|
+
"discovery_date": str(incident.discovery_date) if incident.discovery_date else None,
|
|
1558
|
+
"resolved_date": str(incident.resolved_date) if incident.resolved_date else None,
|
|
1559
|
+
})
|
|
1560
|
+
|
|
1561
|
+
except Exception as e:
|
|
1562
|
+
return format_error("GET_INCIDENT_ERROR", str(e), retry=False)
|
|
1563
|
+
|
|
1564
|
+
return asyncio.run(_get())
|
|
1565
|
+
|
|
1566
|
+
|
|
1567
|
+
def incidents_create(
|
|
1568
|
+
title: str,
|
|
1569
|
+
project_id: str,
|
|
1570
|
+
severity: str = "Medium",
|
|
1571
|
+
type: str = "Bug",
|
|
1572
|
+
affected_version_id: Optional[str] = None,
|
|
1573
|
+
root_cause: str = "",
|
|
1574
|
+
) -> str:
|
|
1575
|
+
"""
|
|
1576
|
+
Create a new incident.
|
|
1577
|
+
|
|
1578
|
+
Args:
|
|
1579
|
+
title: Incident title
|
|
1580
|
+
project_id: Project ID
|
|
1581
|
+
severity: Incident severity (Low, Medium, High, Critical)
|
|
1582
|
+
type: Incident type (Bug, Performance, Security, Data, Other)
|
|
1583
|
+
affected_version_id: Affected version ID
|
|
1584
|
+
root_cause: Root cause analysis
|
|
1585
|
+
|
|
1586
|
+
Example:
|
|
1587
|
+
$ notion incidents create "Production outage" --project-id proj_123 \\
|
|
1588
|
+
--severity Critical --type Bug
|
|
1589
|
+
"""
|
|
1590
|
+
async def _create() -> str:
|
|
1591
|
+
try:
|
|
1592
|
+
client = get_client()
|
|
1593
|
+
|
|
1594
|
+
# Register SDK plugin
|
|
1595
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1596
|
+
plugin = AgentsSDKPlugin()
|
|
1597
|
+
plugin.initialize(client)
|
|
1598
|
+
client.register_sdk_plugin(plugin)
|
|
1599
|
+
|
|
1600
|
+
# Get workspace config
|
|
1601
|
+
workspace_config = get_workspace_config()
|
|
1602
|
+
database_id = workspace_config.get("Incidents")
|
|
1603
|
+
|
|
1604
|
+
if not database_id:
|
|
1605
|
+
return format_error("NO_DATABASE", "Incidents database not configured", retry=False)
|
|
1606
|
+
|
|
1607
|
+
# Create incident in Notion
|
|
1608
|
+
from datetime import datetime, timezone
|
|
1609
|
+
|
|
1610
|
+
properties = {
|
|
1611
|
+
"title": {"title": [{"text": {"content": title}}]},
|
|
1612
|
+
"project_id": {"relation": [{"id": project_id}]},
|
|
1613
|
+
"severity": {"select": {"name": severity}},
|
|
1614
|
+
"type": {"select": {"name": type}},
|
|
1615
|
+
"status": {"select": {"name": "Active"}},
|
|
1616
|
+
"discovery_date": {
|
|
1617
|
+
"date": {"start": datetime.now(timezone.utc).isoformat()}
|
|
1618
|
+
},
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
if affected_version_id:
|
|
1622
|
+
properties["affected_version_id"] = {"relation": [{"id": affected_version_id}]}
|
|
1623
|
+
|
|
1624
|
+
if root_cause:
|
|
1625
|
+
properties["root_cause"] = {
|
|
1626
|
+
"rich_text": [{"text": {"content": root_cause}}]
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
response = await client._api.request(
|
|
1630
|
+
method="POST",
|
|
1631
|
+
path=f"databases/{database_id}",
|
|
1632
|
+
json={"properties": properties},
|
|
1633
|
+
)
|
|
1634
|
+
|
|
1635
|
+
return format_success({
|
|
1636
|
+
"message": "Incident created successfully",
|
|
1637
|
+
"incident_id": response["id"],
|
|
1638
|
+
"title": title,
|
|
1639
|
+
"severity": severity,
|
|
1640
|
+
})
|
|
1641
|
+
|
|
1642
|
+
except Exception as e:
|
|
1643
|
+
return format_error("CREATE_INCIDENT_ERROR", str(e), retry=False)
|
|
1644
|
+
|
|
1645
|
+
return asyncio.run(_create())
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
def incidents_resolve(incident_id: str, resolution: str = "") -> str:
|
|
1649
|
+
"""
|
|
1650
|
+
Resolve an incident.
|
|
1651
|
+
|
|
1652
|
+
Args:
|
|
1653
|
+
incident_id: Incident page ID
|
|
1654
|
+
resolution: Resolution description
|
|
1655
|
+
|
|
1656
|
+
Example:
|
|
1657
|
+
$ notion incidents resolve incident_123 --resolution "Hotfix deployed"
|
|
1658
|
+
"""
|
|
1659
|
+
async def _resolve() -> str:
|
|
1660
|
+
try:
|
|
1661
|
+
client = get_client()
|
|
1662
|
+
|
|
1663
|
+
# Register SDK plugin
|
|
1664
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1665
|
+
plugin = AgentsSDKPlugin()
|
|
1666
|
+
plugin.initialize(client)
|
|
1667
|
+
client.register_sdk_plugin(plugin)
|
|
1668
|
+
|
|
1669
|
+
# Get incident and resolve
|
|
1670
|
+
from better_notion.plugins.official.agents_sdk.models import Incident
|
|
1671
|
+
incident = await Incident.get(incident_id, client=client)
|
|
1672
|
+
await incident.resolve()
|
|
1673
|
+
|
|
1674
|
+
return format_success({
|
|
1675
|
+
"message": "Incident resolved successfully",
|
|
1676
|
+
"incident_id": incident.id,
|
|
1677
|
+
"status": incident.status,
|
|
1678
|
+
"resolution": resolution,
|
|
1679
|
+
})
|
|
1680
|
+
|
|
1681
|
+
except Exception as e:
|
|
1682
|
+
return format_error("RESOLVE_INCIDENT_ERROR", str(e), retry=False)
|
|
1683
|
+
|
|
1684
|
+
return asyncio.run(_resolve())
|
|
1685
|
+
|
|
1686
|
+
|
|
1687
|
+
def incidents_mttr(project_id: Optional[str] = None, within_days: int = 30) -> str:
|
|
1688
|
+
"""
|
|
1689
|
+
Calculate Mean Time To Resolve (MTTR) for incidents.
|
|
1690
|
+
|
|
1691
|
+
Args:
|
|
1692
|
+
project_id: Filter by project ID (optional)
|
|
1693
|
+
within_days: Only consider incidents from last N days
|
|
1694
|
+
|
|
1695
|
+
Example:
|
|
1696
|
+
$ notion incidents mttr --project-id proj_123 --within-days 30
|
|
1697
|
+
"""
|
|
1698
|
+
async def _mttr() -> str:
|
|
1699
|
+
try:
|
|
1700
|
+
client = get_client()
|
|
1701
|
+
|
|
1702
|
+
# Register SDK plugin
|
|
1703
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1704
|
+
plugin = AgentsSDKPlugin()
|
|
1705
|
+
plugin.initialize(client)
|
|
1706
|
+
client.register_sdk_plugin(plugin)
|
|
1707
|
+
|
|
1708
|
+
# Get manager and calculate MTTR
|
|
1709
|
+
manager = client.plugin_manager("incidents")
|
|
1710
|
+
mttr = await manager.calculate_mttr(
|
|
1711
|
+
project_id=project_id,
|
|
1712
|
+
within_days=within_days,
|
|
1713
|
+
)
|
|
1714
|
+
|
|
1715
|
+
return format_success({
|
|
1716
|
+
"mttr_hours": mttr,
|
|
1717
|
+
"within_days": within_days,
|
|
1718
|
+
"project_id": project_id,
|
|
1719
|
+
})
|
|
1720
|
+
|
|
1721
|
+
except Exception as e:
|
|
1722
|
+
return format_error("CALCULATE_MTTR_ERROR", str(e), retry=False)
|
|
1723
|
+
|
|
1724
|
+
return asyncio.run(_mttr())
|
|
1725
|
+
|
|
1726
|
+
|
|
1727
|
+
def incidents_sla_violations() -> str:
|
|
1728
|
+
"""
|
|
1729
|
+
Find all incidents that violated SLA.
|
|
1730
|
+
|
|
1731
|
+
SLA: Critical incidents should be resolved within 4 hours.
|
|
1732
|
+
|
|
1733
|
+
Example:
|
|
1734
|
+
$ notion incidents sla-violations
|
|
1735
|
+
"""
|
|
1736
|
+
async def _sla_violations() -> str:
|
|
1737
|
+
try:
|
|
1738
|
+
client = get_client()
|
|
1739
|
+
|
|
1740
|
+
# Register SDK plugin
|
|
1741
|
+
from better_notion.plugins.official.agents_sdk.plugin import AgentsSDKPlugin
|
|
1742
|
+
plugin = AgentsSDKPlugin()
|
|
1743
|
+
plugin.initialize(client)
|
|
1744
|
+
client.register_sdk_plugin(plugin)
|
|
1745
|
+
|
|
1746
|
+
# Get manager and find violations
|
|
1747
|
+
manager = client.plugin_manager("incidents")
|
|
1748
|
+
violations = await manager.find_sla_violations()
|
|
1749
|
+
|
|
1750
|
+
return format_success({
|
|
1751
|
+
"sla_violations": [
|
|
1752
|
+
{
|
|
1753
|
+
"id": incident.id,
|
|
1754
|
+
"title": incident.title,
|
|
1755
|
+
"severity": incident.severity,
|
|
1756
|
+
"discovery_date": str(incident.discovery_date) if incident.discovery_date else None,
|
|
1757
|
+
"resolved_date": str(incident.resolved_date) if incident.resolved_date else None,
|
|
1758
|
+
}
|
|
1759
|
+
for incident in violations
|
|
1760
|
+
],
|
|
1761
|
+
"total": len(violations),
|
|
1762
|
+
})
|
|
1763
|
+
|
|
1764
|
+
except Exception as e:
|
|
1765
|
+
return format_error("SLA_VIOLATIONS_ERROR", str(e), retry=False)
|
|
1766
|
+
|
|
1767
|
+
return asyncio.run(_sla_violations())
|