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.
@@ -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(PluginInterface):
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
- result = format_error(
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
- result = format_success(
182
- {
183
- "message": "Project initialized successfully",
184
- "project_id": context.project_id,
185
- "project_name": context.project_name,
186
- "org_id": context.org_id,
187
- "role": context.role,
188
- "notion_file": str(Path.cwd() / ".notion"),
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
- except Exception as e:
193
- result = format_error("INIT_PROJECT_ERROR", str(e), retry=False)
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
- typer.echo(result)
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
- result = format_error(
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
- # Load project context
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
- except Exception as e:
255
- result = format_error("ROLE_UPDATE_ERROR", str(e), retry=False)
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
- typer.echo(result)
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
- result = format_error(
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
- result = format_success(
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
- except Exception as e:
302
- result = format_error("ROLE_ERROR", str(e), retry=False)
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
- typer.echo(result)
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
- result = format_success(
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
- result = format_error("ROLE_LIST_ERROR", str(e), retry=False)
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 {