better-notion 1.0.1__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/__init__.py +1 -3
- better_notion/plugins/official/agents.py +211 -74
- 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.1.dist-info → better_notion-1.1.0.dist-info}/METADATA +2 -2
- {better_notion-1.0.1.dist-info → better_notion-1.1.0.dist-info}/RECORD +16 -10
- {better_notion-1.0.1.dist-info → better_notion-1.1.0.dist-info}/WHEEL +0 -0
- {better_notion-1.0.1.dist-info → better_notion-1.1.0.dist-info}/entry_points.txt +0 -0
- {better_notion-1.0.1.dist-info → better_notion-1.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -23,7 +23,9 @@ import typer
|
|
|
23
23
|
|
|
24
24
|
from better_notion._cli.config import Config
|
|
25
25
|
from better_notion._cli.response import format_error, format_success
|
|
26
|
+
from better_notion._sdk.cache import Cache
|
|
26
27
|
from better_notion._sdk.client import NotionClient
|
|
28
|
+
from better_notion._sdk.plugins import CombinedPluginInterface
|
|
27
29
|
from better_notion.plugins.base import PluginInterface
|
|
28
30
|
from better_notion.utils.agents import (
|
|
29
31
|
DependencyResolver,
|
|
@@ -40,17 +42,26 @@ def get_client() -> NotionClient:
|
|
|
40
42
|
return NotionClient(auth=config.token, timeout=config.timeout)
|
|
41
43
|
|
|
42
44
|
|
|
43
|
-
class AgentsPlugin(
|
|
45
|
+
class AgentsPlugin(CombinedPluginInterface):
|
|
44
46
|
"""
|
|
45
47
|
Official agents workflow management plugin.
|
|
46
48
|
|
|
47
49
|
This plugin provides tools for managing software development workflows
|
|
48
50
|
through Notion databases, enabling AI agents to coordinate work on projects.
|
|
49
51
|
|
|
50
|
-
Commands:
|
|
51
|
-
- init: Initialize a new workspace with all databases
|
|
52
|
-
- init-project: Initialize a new project with .notion file
|
|
53
|
-
- role: Manage project role
|
|
52
|
+
CLI Commands:
|
|
53
|
+
- agents init: Initialize a new workspace with all databases
|
|
54
|
+
- agents init-project: Initialize a new project with .notion file
|
|
55
|
+
- agents role: Manage project role
|
|
56
|
+
- orgs: Organizations CRUD commands
|
|
57
|
+
- projects: Projects CRUD commands
|
|
58
|
+
- versions: Versions CRUD commands
|
|
59
|
+
- tasks: Tasks CRUD and workflow commands
|
|
60
|
+
|
|
61
|
+
SDK Extensions:
|
|
62
|
+
- Organization, Project, Version, Task models
|
|
63
|
+
- Dedicated caches for each entity type
|
|
64
|
+
- Managers for convenient operations
|
|
54
65
|
"""
|
|
55
66
|
|
|
56
67
|
def register_commands(self, app: typer.Typer) -> None:
|
|
@@ -163,36 +174,34 @@ class AgentsPlugin(PluginInterface):
|
|
|
163
174
|
try:
|
|
164
175
|
# Validate role
|
|
165
176
|
if not RoleManager.is_valid_role(role):
|
|
166
|
-
|
|
177
|
+
return format_error(
|
|
167
178
|
"INVALID_ROLE",
|
|
168
179
|
f"Invalid role: {role}. Valid roles: {', '.join(RoleManager.get_all_roles())}",
|
|
169
180
|
retry=False,
|
|
170
181
|
)
|
|
171
|
-
else:
|
|
172
|
-
# Create .notion file
|
|
173
|
-
context = ProjectContext.create(
|
|
174
|
-
project_id=project_id,
|
|
175
|
-
project_name=project_name,
|
|
176
|
-
org_id=org_id,
|
|
177
|
-
role=role,
|
|
178
|
-
path=Path.cwd(),
|
|
179
|
-
)
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
)
|
|
183
|
+
# Create .notion file
|
|
184
|
+
context = ProjectContext.create(
|
|
185
|
+
project_id=project_id,
|
|
186
|
+
project_name=project_name,
|
|
187
|
+
org_id=org_id,
|
|
188
|
+
role=role,
|
|
189
|
+
path=Path.cwd(),
|
|
190
|
+
)
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
return format_success(
|
|
193
|
+
{
|
|
194
|
+
"message": "Project initialized successfully",
|
|
195
|
+
"project_id": context.project_id,
|
|
196
|
+
"project_name": context.project_name,
|
|
197
|
+
"org_id": context.org_id,
|
|
198
|
+
"role": context.role,
|
|
199
|
+
"notion_file": str(Path.cwd() / ".notion"),
|
|
200
|
+
}
|
|
201
|
+
)
|
|
194
202
|
|
|
195
|
-
|
|
203
|
+
except Exception as e:
|
|
204
|
+
return format_error("INIT_PROJECT_ERROR", str(e), retry=False)
|
|
196
205
|
|
|
197
206
|
# Role management commands
|
|
198
207
|
role_app = typer.Typer(
|
|
@@ -221,40 +230,38 @@ class AgentsPlugin(PluginInterface):
|
|
|
221
230
|
try:
|
|
222
231
|
# Validate role
|
|
223
232
|
if not RoleManager.is_valid_role(new_role):
|
|
224
|
-
|
|
233
|
+
return format_error(
|
|
225
234
|
"INVALID_ROLE",
|
|
226
235
|
f"Invalid role: {new_role}. Valid roles: {', '.join(RoleManager.get_all_roles())}",
|
|
227
236
|
retry=False,
|
|
228
237
|
)
|
|
238
|
+
|
|
239
|
+
# Load project context
|
|
240
|
+
if path:
|
|
241
|
+
context = ProjectContext.from_path(path)
|
|
229
242
|
else:
|
|
230
|
-
|
|
231
|
-
if path:
|
|
232
|
-
context = ProjectContext.from_path(path)
|
|
233
|
-
else:
|
|
234
|
-
context = ProjectContext.from_current_directory()
|
|
235
|
-
|
|
236
|
-
if not context:
|
|
237
|
-
result = format_error(
|
|
238
|
-
"NO_PROJECT_CONTEXT",
|
|
239
|
-
"No .notion file found. Are you in a project directory?",
|
|
240
|
-
retry=False,
|
|
241
|
-
)
|
|
242
|
-
else:
|
|
243
|
-
# Update role
|
|
244
|
-
context.update_role(new_role, path=path or None)
|
|
245
|
-
|
|
246
|
-
result = format_success(
|
|
247
|
-
{
|
|
248
|
-
"message": f"Role updated to {new_role}",
|
|
249
|
-
"previous_role": context.role,
|
|
250
|
-
"new_role": new_role,
|
|
251
|
-
}
|
|
252
|
-
)
|
|
243
|
+
context = ProjectContext.from_current_directory()
|
|
253
244
|
|
|
254
|
-
|
|
255
|
-
|
|
245
|
+
if not context:
|
|
246
|
+
return format_error(
|
|
247
|
+
"NO_PROJECT_CONTEXT",
|
|
248
|
+
"No .notion file found. Are you in a project directory?",
|
|
249
|
+
retry=False,
|
|
250
|
+
)
|
|
256
251
|
|
|
257
|
-
|
|
252
|
+
# Update role
|
|
253
|
+
context.update_role(new_role, path=path or None)
|
|
254
|
+
|
|
255
|
+
return format_success(
|
|
256
|
+
{
|
|
257
|
+
"message": f"Role updated to {new_role}",
|
|
258
|
+
"previous_role": context.role,
|
|
259
|
+
"new_role": new_role,
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
return format_error("ROLE_UPDATE_ERROR", str(e), retry=False)
|
|
258
265
|
|
|
259
266
|
@role_app.command("whoami")
|
|
260
267
|
def role_whoami(
|
|
@@ -280,28 +287,26 @@ class AgentsPlugin(PluginInterface):
|
|
|
280
287
|
context = ProjectContext.from_current_directory()
|
|
281
288
|
|
|
282
289
|
if not context:
|
|
283
|
-
|
|
290
|
+
return format_error(
|
|
284
291
|
"NO_PROJECT_CONTEXT",
|
|
285
292
|
"No .notion file found. Are you in a project directory?",
|
|
286
293
|
retry=False,
|
|
287
294
|
)
|
|
288
|
-
else:
|
|
289
|
-
# Get role description
|
|
290
|
-
description = RoleManager.get_role_description(context.role)
|
|
291
295
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
"role": context.role,
|
|
295
|
-
"description": description,
|
|
296
|
-
"project": context.project_name,
|
|
297
|
-
"permissions": RoleManager.get_permissions(context.role),
|
|
298
|
-
}
|
|
299
|
-
)
|
|
296
|
+
# Get role description
|
|
297
|
+
description = RoleManager.get_role_description(context.role)
|
|
300
298
|
|
|
301
|
-
|
|
302
|
-
|
|
299
|
+
return format_success(
|
|
300
|
+
{
|
|
301
|
+
"role": context.role,
|
|
302
|
+
"description": description,
|
|
303
|
+
"project": context.project_name,
|
|
304
|
+
"permissions": RoleManager.get_permissions(context.role),
|
|
305
|
+
}
|
|
306
|
+
)
|
|
303
307
|
|
|
304
|
-
|
|
308
|
+
except Exception as e:
|
|
309
|
+
return format_error("ROLE_ERROR", str(e), retry=False)
|
|
305
310
|
|
|
306
311
|
@role_app.command("list")
|
|
307
312
|
def role_list() -> None:
|
|
@@ -328,7 +333,7 @@ class AgentsPlugin(PluginInterface):
|
|
|
328
333
|
}
|
|
329
334
|
)
|
|
330
335
|
|
|
331
|
-
|
|
336
|
+
return format_success(
|
|
332
337
|
{
|
|
333
338
|
"roles": role_info,
|
|
334
339
|
"total": len(roles),
|
|
@@ -336,13 +341,145 @@ class AgentsPlugin(PluginInterface):
|
|
|
336
341
|
)
|
|
337
342
|
|
|
338
343
|
except Exception as e:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
typer.echo(result)
|
|
344
|
+
return format_error("ROLE_LIST_ERROR", str(e), retry=False)
|
|
342
345
|
|
|
343
346
|
# Register agents app to main CLI
|
|
344
347
|
app.add_typer(agents_app)
|
|
345
348
|
|
|
349
|
+
# Import and register CRUD commands
|
|
350
|
+
from better_notion.plugins.official import agents_cli
|
|
351
|
+
|
|
352
|
+
# Organizations commands
|
|
353
|
+
orgs_app = typer.Typer(name="orgs", help="Organizations management commands")
|
|
354
|
+
orgs_app.command("list")(agents_cli.orgs_list)
|
|
355
|
+
orgs_app.command("get")(agents_cli.orgs_get)
|
|
356
|
+
orgs_app.command("create")(agents_cli.orgs_create)
|
|
357
|
+
app.add_typer(orgs_app)
|
|
358
|
+
|
|
359
|
+
# Projects commands
|
|
360
|
+
projects_app = typer.Typer(name="projects", help="Projects management commands")
|
|
361
|
+
projects_app.command("list")(projects_list_with_cli := lambda **kwargs: agents_cli.projects_list(**kwargs))
|
|
362
|
+
projects_app.command("get")(projects_get_with_cli := lambda project_id: agents_cli.projects_get(project_id))
|
|
363
|
+
projects_app.command("create")(lambda **kwargs: agents_cli.projects_create(**kwargs))
|
|
364
|
+
app.add_typer(projects_app)
|
|
365
|
+
|
|
366
|
+
# Versions commands
|
|
367
|
+
versions_app = typer.Typer(name="versions", help="Versions management commands")
|
|
368
|
+
versions_app.command("list")(lambda **kwargs: agents_cli.versions_list(**kwargs))
|
|
369
|
+
versions_app.command("get")(lambda version_id: agents_cli.versions_get(version_id))
|
|
370
|
+
versions_app.command("create")(lambda **kwargs: agents_cli.versions_create(**kwargs))
|
|
371
|
+
app.add_typer(versions_app)
|
|
372
|
+
|
|
373
|
+
# Tasks commands
|
|
374
|
+
tasks_app = typer.Typer(name="tasks", help="Tasks management commands")
|
|
375
|
+
tasks_app.command("list")(lambda **kwargs: agents_cli.tasks_list(**kwargs))
|
|
376
|
+
tasks_app.command("get")(lambda task_id: agents_cli.tasks_get(task_id))
|
|
377
|
+
tasks_app.command("create")(lambda **kwargs: agents_cli.tasks_create(**kwargs))
|
|
378
|
+
tasks_app.command("next")(lambda **kwargs: agents_cli.tasks_next(**kwargs))
|
|
379
|
+
tasks_app.command("claim")(lambda task_id: agents_cli.tasks_claim(task_id))
|
|
380
|
+
tasks_app.command("start")(lambda task_id: agents_cli.tasks_start(task_id))
|
|
381
|
+
tasks_app.command("complete")(lambda **kwargs: agents_cli.tasks_complete(**kwargs))
|
|
382
|
+
tasks_app.command("can-start")(lambda task_id: agents_cli.tasks_can_start(task_id))
|
|
383
|
+
app.add_typer(tasks_app)
|
|
384
|
+
|
|
385
|
+
# Ideas commands
|
|
386
|
+
ideas_app = typer.Typer(name="ideas", help="Ideas management commands")
|
|
387
|
+
ideas_app.command("list")(lambda **kwargs: agents_cli.ideas_list(**kwargs))
|
|
388
|
+
ideas_app.command("get")(lambda idea_id: agents_cli.ideas_get(idea_id))
|
|
389
|
+
ideas_app.command("create")(lambda **kwargs: agents_cli.ideas_create(**kwargs))
|
|
390
|
+
ideas_app.command("review")(lambda count: agents_cli.ideas_review(count))
|
|
391
|
+
ideas_app.command("accept")(lambda idea_id: agents_cli.ideas_accept(idea_id))
|
|
392
|
+
ideas_app.command("reject")(lambda idea_id, reason: agents_cli.ideas_reject(idea_id, reason))
|
|
393
|
+
app.add_typer(ideas_app)
|
|
394
|
+
|
|
395
|
+
# Work Issues commands
|
|
396
|
+
work_issues_app = typer.Typer(name="work-issues", help="Work Issues management commands")
|
|
397
|
+
work_issues_app.command("list")(lambda **kwargs: agents_cli.work_issues_list(**kwargs))
|
|
398
|
+
work_issues_app.command("get")(lambda issue_id: agents_cli.work_issues_get(issue_id))
|
|
399
|
+
work_issues_app.command("create")(lambda **kwargs: agents_cli.work_issues_create(**kwargs))
|
|
400
|
+
work_issues_app.command("resolve")(lambda issue_id, resolution: agents_cli.work_issues_resolve(issue_id, resolution))
|
|
401
|
+
work_issues_app.command("blockers")(lambda project_id: agents_cli.work_issues_blockers(project_id))
|
|
402
|
+
app.add_typer(work_issues_app)
|
|
403
|
+
|
|
404
|
+
# Incidents commands
|
|
405
|
+
incidents_app = typer.Typer(name="incidents", help="Incidents management commands")
|
|
406
|
+
incidents_app.command("list")(lambda **kwargs: agents_cli.incidents_list(**kwargs))
|
|
407
|
+
incidents_app.command("get")(lambda incident_id: agents_cli.incidents_get(incident_id))
|
|
408
|
+
incidents_app.command("create")(lambda **kwargs: agents_cli.incidents_create(**kwargs))
|
|
409
|
+
incidents_app.command("resolve")(lambda incident_id, resolution: agents_cli.incidents_resolve(incident_id, resolution))
|
|
410
|
+
incidents_app.command("mttr")(lambda **kwargs: agents_cli.incidents_mttr(**kwargs))
|
|
411
|
+
incidents_app.command("sla-violations")(lambda: agents_cli.incidents_sla_violations())
|
|
412
|
+
app.add_typer(incidents_app)
|
|
413
|
+
|
|
414
|
+
def register_sdk_models(self) -> dict[str, type]:
|
|
415
|
+
"""Register workflow entity models."""
|
|
416
|
+
from better_notion.plugins.official.agents_sdk.models import (
|
|
417
|
+
Idea,
|
|
418
|
+
Incident,
|
|
419
|
+
Organization,
|
|
420
|
+
Project,
|
|
421
|
+
Task,
|
|
422
|
+
Version,
|
|
423
|
+
WorkIssue,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
"Organization": Organization,
|
|
428
|
+
"Project": Project,
|
|
429
|
+
"Version": Version,
|
|
430
|
+
"Task": Task,
|
|
431
|
+
"Idea": Idea,
|
|
432
|
+
"WorkIssue": WorkIssue,
|
|
433
|
+
"Incident": Incident,
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
def register_sdk_caches(self, client: NotionClient) -> dict[str, Cache]:
|
|
437
|
+
"""Register dedicated caches for workflow entities."""
|
|
438
|
+
return {
|
|
439
|
+
"organizations": Cache(),
|
|
440
|
+
"projects": Cache(),
|
|
441
|
+
"versions": Cache(),
|
|
442
|
+
"tasks": Cache(),
|
|
443
|
+
"ideas": Cache(),
|
|
444
|
+
"work_issues": Cache(),
|
|
445
|
+
"incidents": Cache(),
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
def register_sdk_managers(self, client: NotionClient) -> dict:
|
|
449
|
+
"""Register custom managers for workflow entities."""
|
|
450
|
+
from better_notion.plugins.official.agents_sdk.managers import (
|
|
451
|
+
IdeaManager,
|
|
452
|
+
IncidentManager,
|
|
453
|
+
OrganizationManager,
|
|
454
|
+
ProjectManager,
|
|
455
|
+
TaskManager,
|
|
456
|
+
VersionManager,
|
|
457
|
+
WorkIssueManager,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
"organizations": OrganizationManager(client),
|
|
462
|
+
"projects": ProjectManager(client),
|
|
463
|
+
"versions": VersionManager(client),
|
|
464
|
+
"tasks": TaskManager(client),
|
|
465
|
+
"ideas": IdeaManager(client),
|
|
466
|
+
"work_issues": WorkIssueManager(client),
|
|
467
|
+
"incidents": IncidentManager(client),
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
def sdk_initialize(self, client: NotionClient) -> None:
|
|
471
|
+
"""Initialize plugin resources."""
|
|
472
|
+
import json
|
|
473
|
+
from pathlib import Path
|
|
474
|
+
|
|
475
|
+
config_path = Path.home() / ".notion" / "workspace.json"
|
|
476
|
+
|
|
477
|
+
if config_path.exists():
|
|
478
|
+
with open(config_path) as f:
|
|
479
|
+
client._workspace_config = json.load(f)
|
|
480
|
+
else:
|
|
481
|
+
client._workspace_config = {}
|
|
482
|
+
|
|
346
483
|
def get_info(self) -> dict[str, str | bool | list]:
|
|
347
484
|
"""Return plugin metadata."""
|
|
348
485
|
return {
|