better-notion 1.5.4__py3-none-any.whl → 1.6.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/_cli/docs/__init__.py +20 -0
- better_notion/_cli/docs/base.py +204 -0
- better_notion/_cli/docs/formatters.py +128 -0
- better_notion/_cli/docs/registry.py +82 -0
- better_notion/_cli/main.py +167 -0
- better_notion/_cli/response.py +64 -8
- better_notion/plugins/official/agents.py +274 -3
- better_notion/plugins/official/agents_schema.py +369 -0
- better_notion/utils/agents/metadata.py +185 -0
- better_notion/utils/agents/workspace.py +49 -2
- {better_notion-1.5.4.dist-info → better_notion-1.6.0.dist-info}/METADATA +1 -1
- {better_notion-1.5.4.dist-info → better_notion-1.6.0.dist-info}/RECORD +15 -9
- {better_notion-1.5.4.dist-info → better_notion-1.6.0.dist-info}/WHEEL +0 -0
- {better_notion-1.5.4.dist-info → better_notion-1.6.0.dist-info}/entry_points.txt +0 -0
- {better_notion-1.5.4.dist-info → better_notion-1.6.0.dist-info}/licenses/LICENSE +0 -0
better_notion/_cli/response.py
CHANGED
|
@@ -21,6 +21,7 @@ def format_response(
|
|
|
21
21
|
data: Any = None,
|
|
22
22
|
error: dict[str, Any] | None = None,
|
|
23
23
|
rate_limit: dict[str, Any] | None = None,
|
|
24
|
+
meta: dict[str, Any] | None = None,
|
|
24
25
|
) -> str:
|
|
25
26
|
"""
|
|
26
27
|
Format CLI response as JSON.
|
|
@@ -33,6 +34,7 @@ def format_response(
|
|
|
33
34
|
data: Response data (for successful operations)
|
|
34
35
|
error: Error information (for failed operations)
|
|
35
36
|
rate_limit: Rate limit information from API response
|
|
37
|
+
meta: Additional metadata for AI agents (context, next_steps, etc.)
|
|
36
38
|
|
|
37
39
|
Returns:
|
|
38
40
|
JSON-formatted response string
|
|
@@ -50,6 +52,28 @@ def format_response(
|
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
Response with AI metadata:
|
|
56
|
+
>>> format_response(
|
|
57
|
+
... success=True,
|
|
58
|
+
... data={"workspace_id": "abc123"},
|
|
59
|
+
... meta={
|
|
60
|
+
... "command": "agents init",
|
|
61
|
+
... "context": {"state": "initialized"},
|
|
62
|
+
... "next_steps": [{"command": "agents info"}]
|
|
63
|
+
... }
|
|
64
|
+
... )
|
|
65
|
+
{
|
|
66
|
+
"success": true,
|
|
67
|
+
"data": {"workspace_id": "abc123"},
|
|
68
|
+
"meta": {
|
|
69
|
+
"version": "0.4.0",
|
|
70
|
+
"timestamp": "2025-01-26T10:00:00Z",
|
|
71
|
+
"command": "agents init",
|
|
72
|
+
"context": {"state": "initialized"},
|
|
73
|
+
"next_steps": [{"command": "agents info"}]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
53
77
|
Error response:
|
|
54
78
|
>>> format_response(
|
|
55
79
|
... success=False,
|
|
@@ -58,11 +82,7 @@ def format_response(
|
|
|
58
82
|
{
|
|
59
83
|
"success": false,
|
|
60
84
|
"error": {"code": "NOT_FOUND", "message": "Page not found", "retry": false},
|
|
61
|
-
"meta": {
|
|
62
|
-
"version": "0.4.0",
|
|
63
|
-
"timestamp": "2025-01-26T10:00:00Z",
|
|
64
|
-
"rate_limit": {"remaining": null, "reset_at": null}
|
|
65
|
-
}
|
|
85
|
+
"meta": {...}
|
|
66
86
|
}
|
|
67
87
|
"""
|
|
68
88
|
response: dict[str, Any] = {
|
|
@@ -81,6 +101,10 @@ def format_response(
|
|
|
81
101
|
if error is not None:
|
|
82
102
|
response["error"] = error
|
|
83
103
|
|
|
104
|
+
# Add custom metadata for AI agents
|
|
105
|
+
if meta:
|
|
106
|
+
response["meta"].update(meta)
|
|
107
|
+
|
|
84
108
|
return json.dumps(response, indent=2)
|
|
85
109
|
|
|
86
110
|
|
|
@@ -90,6 +114,7 @@ def format_error(
|
|
|
90
114
|
*,
|
|
91
115
|
retry: bool = False,
|
|
92
116
|
details: dict[str, Any] | None = None,
|
|
117
|
+
meta: dict[str, Any] | None = None,
|
|
93
118
|
) -> str:
|
|
94
119
|
"""
|
|
95
120
|
Format an error response.
|
|
@@ -101,6 +126,7 @@ def format_error(
|
|
|
101
126
|
message: Human-readable error message
|
|
102
127
|
retry: Whether the operation should be retried
|
|
103
128
|
details: Additional error details
|
|
129
|
+
meta: Additional metadata for AI agents (recovery strategies, etc.)
|
|
104
130
|
|
|
105
131
|
Returns:
|
|
106
132
|
JSON-formatted error response string
|
|
@@ -116,6 +142,20 @@ def format_error(
|
|
|
116
142
|
},
|
|
117
143
|
"meta": {...}
|
|
118
144
|
}
|
|
145
|
+
|
|
146
|
+
With error recovery metadata:
|
|
147
|
+
>>> format_error(
|
|
148
|
+
... "WORKSPACE_EXISTS",
|
|
149
|
+
... "Workspace already initialized",
|
|
150
|
+
... meta={
|
|
151
|
+
... "error_recovery": {
|
|
152
|
+
... "solutions": [
|
|
153
|
+
... {"flag": "--skip", "action": "use_existing"},
|
|
154
|
+
... {"flag": "--reset", "action": "recreate"}
|
|
155
|
+
... ]
|
|
156
|
+
... }
|
|
157
|
+
... }
|
|
158
|
+
... )
|
|
119
159
|
"""
|
|
120
160
|
error_info: dict[str, Any] = {
|
|
121
161
|
"code": code,
|
|
@@ -126,10 +166,15 @@ def format_error(
|
|
|
126
166
|
if details:
|
|
127
167
|
error_info["details"] = details
|
|
128
168
|
|
|
129
|
-
return format_response(success=False, error=error_info)
|
|
169
|
+
return format_response(success=False, error=error_info, meta=meta)
|
|
130
170
|
|
|
131
171
|
|
|
132
|
-
def format_success(
|
|
172
|
+
def format_success(
|
|
173
|
+
data: Any,
|
|
174
|
+
*,
|
|
175
|
+
rate_limit: dict[str, Any] | None = None,
|
|
176
|
+
meta: dict[str, Any] | None = None,
|
|
177
|
+
) -> str:
|
|
133
178
|
"""
|
|
134
179
|
Format a success response.
|
|
135
180
|
|
|
@@ -138,6 +183,7 @@ def format_success(data: Any, *, rate_limit: dict[str, Any] | None = None) -> st
|
|
|
138
183
|
Args:
|
|
139
184
|
data: Response data
|
|
140
185
|
rate_limit: Rate limit information from API response
|
|
186
|
+
meta: Additional metadata for AI agents (context, next_steps, etc.)
|
|
141
187
|
|
|
142
188
|
Returns:
|
|
143
189
|
JSON-formatted success response string
|
|
@@ -149,5 +195,15 @@ def format_success(data: Any, *, rate_limit: dict[str, Any] | None = None) -> st
|
|
|
149
195
|
"data": {"id": "page_123", "title": "My Page"},
|
|
150
196
|
"meta": {...}
|
|
151
197
|
}
|
|
198
|
+
|
|
199
|
+
With contextual metadata:
|
|
200
|
+
>>> format_success(
|
|
201
|
+
... {"workspace_id": "abc123"},
|
|
202
|
+
... meta={
|
|
203
|
+
... "command": "agents init",
|
|
204
|
+
... "context": {"state": "initialized"},
|
|
205
|
+
... "next_steps": [{"command": "agents info"}]
|
|
206
|
+
... }
|
|
207
|
+
... )
|
|
152
208
|
"""
|
|
153
|
-
return format_response(success=True, data=data, rate_limit=rate_limit)
|
|
209
|
+
return format_response(success=True, data=data, rate_limit=rate_limit, meta=meta)
|
|
@@ -86,6 +86,18 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
86
86
|
"-n",
|
|
87
87
|
help="Name for the workspace",
|
|
88
88
|
),
|
|
89
|
+
reset: bool = typer.Option(
|
|
90
|
+
False,
|
|
91
|
+
"--reset",
|
|
92
|
+
"-r",
|
|
93
|
+
help="Reset workspace (delete existing databases and recreate)",
|
|
94
|
+
),
|
|
95
|
+
skip: bool = typer.Option(
|
|
96
|
+
False,
|
|
97
|
+
"--skip",
|
|
98
|
+
"-s",
|
|
99
|
+
help="Skip if workspace already exists (return existing databases)",
|
|
100
|
+
),
|
|
89
101
|
debug: bool = typer.Option(
|
|
90
102
|
False,
|
|
91
103
|
"--debug",
|
|
@@ -106,8 +118,13 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
106
118
|
- Incidents
|
|
107
119
|
- Tags
|
|
108
120
|
|
|
121
|
+
If a workspace already exists in the page, use --reset to recreate
|
|
122
|
+
or --skip to keep the existing one.
|
|
123
|
+
|
|
109
124
|
Example:
|
|
110
125
|
$ notion agents init --parent-page page123 --name "My Workspace"
|
|
126
|
+
$ notion agents init --parent-page page123 --reset # Recreate
|
|
127
|
+
$ notion agents init --parent-page page123 --skip # Keep existing
|
|
111
128
|
"""
|
|
112
129
|
import asyncio
|
|
113
130
|
import logging
|
|
@@ -128,19 +145,107 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
128
145
|
client = get_client()
|
|
129
146
|
initializer = WorkspaceInitializer(client)
|
|
130
147
|
|
|
148
|
+
# Handle --skip flag
|
|
149
|
+
if skip:
|
|
150
|
+
from better_notion._sdk.models.page import Page
|
|
151
|
+
from better_notion.utils.agents.metadata import WorkspaceMetadata
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
page = await Page.get(parent_page_id, client=client)
|
|
155
|
+
existing = await WorkspaceMetadata.detect_workspace(page, client)
|
|
156
|
+
|
|
157
|
+
if existing:
|
|
158
|
+
database_ids = existing.get("database_ids", {})
|
|
159
|
+
return format_success(
|
|
160
|
+
{
|
|
161
|
+
"message": "Workspace already exists, skipping initialization",
|
|
162
|
+
"workspace_id": existing.get("workspace_id"),
|
|
163
|
+
"databases_found": len(database_ids),
|
|
164
|
+
"database_ids": database_ids,
|
|
165
|
+
},
|
|
166
|
+
meta={
|
|
167
|
+
"command": "agents init",
|
|
168
|
+
"context": {
|
|
169
|
+
"state": "already_initialized",
|
|
170
|
+
"description": "Agents workspace already exists in this page",
|
|
171
|
+
},
|
|
172
|
+
"next_steps": [
|
|
173
|
+
{
|
|
174
|
+
"action": "query_tasks",
|
|
175
|
+
"command": f"notion databases query --database {database_ids.get('tasks', 'TASKS_DB_ID')}",
|
|
176
|
+
"purpose": "List all tasks in workspace",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"action": "create_task",
|
|
180
|
+
"description": "To create a new task, use pages create with Version relation",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
"capabilities": [
|
|
184
|
+
"create_tasks",
|
|
185
|
+
"manage_versions",
|
|
186
|
+
"track_projects",
|
|
187
|
+
],
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
except Exception:
|
|
191
|
+
# If detection fails, proceed with normal init
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
# Initialize workspace (with skip_detection=True if --reset)
|
|
131
195
|
database_ids = await initializer.initialize_workspace(
|
|
132
196
|
parent_page_id=parent_page_id,
|
|
133
197
|
workspace_name=workspace_name,
|
|
198
|
+
skip_detection=reset, # Skip detection if resetting
|
|
134
199
|
)
|
|
135
200
|
|
|
136
|
-
# Save database IDs
|
|
137
|
-
initializer.save_database_ids()
|
|
138
|
-
|
|
139
201
|
return format_success(
|
|
140
202
|
{
|
|
141
203
|
"message": "Workspace initialized successfully",
|
|
204
|
+
"workspace_id": initializer._workspace_id,
|
|
142
205
|
"databases_created": len(database_ids),
|
|
143
206
|
"database_ids": database_ids,
|
|
207
|
+
},
|
|
208
|
+
meta={
|
|
209
|
+
"command": "agents init",
|
|
210
|
+
"context": {
|
|
211
|
+
"state": "initialized",
|
|
212
|
+
"description": "Agents workspace ready for task management",
|
|
213
|
+
"workspace_name": workspace_name,
|
|
214
|
+
},
|
|
215
|
+
"next_steps": [
|
|
216
|
+
{
|
|
217
|
+
"command": "agents info --parent-page " + parent_page_id,
|
|
218
|
+
"purpose": "View workspace status and database IDs",
|
|
219
|
+
"when": "To verify workspace setup",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"command": f"notion databases query --database {database_ids.get('tasks', 'TASKS_DB_ID')}",
|
|
223
|
+
"purpose": "List all tasks in workspace",
|
|
224
|
+
"when": "To see existing tasks",
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
"capabilities": [
|
|
228
|
+
"create_tasks",
|
|
229
|
+
"manage_versions",
|
|
230
|
+
"track_projects",
|
|
231
|
+
"manage_ideas",
|
|
232
|
+
"track_incidents",
|
|
233
|
+
],
|
|
234
|
+
"common_workflows": [
|
|
235
|
+
{
|
|
236
|
+
"name": "create_organization",
|
|
237
|
+
"description": "Create a new organization in the workspace",
|
|
238
|
+
"command": f"notion pages create --parent {database_ids.get('organizations', 'ORGANIZATIONS_DB_ID')} --title 'Organization Name'",
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "create_task",
|
|
242
|
+
"description": "Create a task in the workspace",
|
|
243
|
+
"steps": [
|
|
244
|
+
f"agents info --parent-page {parent_page_id}",
|
|
245
|
+
f"notion pages create --parent {database_ids.get('tasks', 'TASKS_DB_ID')} --title 'Task Name' --properties '{{\"Status\": \"Todo\", \"Version\": \"VERSION_ID\"}}'",
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
],
|
|
144
249
|
}
|
|
145
250
|
)
|
|
146
251
|
|
|
@@ -151,6 +256,172 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
151
256
|
result = asyncio.run(_init())
|
|
152
257
|
typer.echo(result)
|
|
153
258
|
|
|
259
|
+
@agents_app.command("info")
|
|
260
|
+
def workspace_info(
|
|
261
|
+
parent_page_id: str = typer.Option(
|
|
262
|
+
...,
|
|
263
|
+
"--parent-page",
|
|
264
|
+
"-p",
|
|
265
|
+
help="ID of the parent page to check",
|
|
266
|
+
),
|
|
267
|
+
) -> None:
|
|
268
|
+
"""
|
|
269
|
+
Show workspace information for a page.
|
|
270
|
+
|
|
271
|
+
Displays whether a workspace is initialized in the given page,
|
|
272
|
+
along with workspace metadata and database information.
|
|
273
|
+
|
|
274
|
+
Example:
|
|
275
|
+
$ notion agents info --parent-page page123
|
|
276
|
+
"""
|
|
277
|
+
import asyncio
|
|
278
|
+
|
|
279
|
+
async def _info() -> str:
|
|
280
|
+
try:
|
|
281
|
+
client = get_client()
|
|
282
|
+
from better_notion._sdk.models.page import Page
|
|
283
|
+
from better_notion.utils.agents.metadata import WorkspaceMetadata
|
|
284
|
+
|
|
285
|
+
page = await Page.get(parent_page_id, client=client)
|
|
286
|
+
existing = await WorkspaceMetadata.detect_workspace(page, client)
|
|
287
|
+
|
|
288
|
+
if existing:
|
|
289
|
+
database_ids = existing.get("database_ids", {})
|
|
290
|
+
|
|
291
|
+
return format_success(
|
|
292
|
+
{
|
|
293
|
+
"message": "Workspace found in this page",
|
|
294
|
+
"parent_page": parent_page_id,
|
|
295
|
+
"parent_title": page.title,
|
|
296
|
+
"workspace_id": existing.get("workspace_id"),
|
|
297
|
+
"workspace_name": existing.get("workspace_name"),
|
|
298
|
+
"initialized_at": existing.get("initialized_at"),
|
|
299
|
+
"databases_count": len(database_ids),
|
|
300
|
+
"database_ids": database_ids,
|
|
301
|
+
"detection_method": existing.get("detection_method", "config_file"),
|
|
302
|
+
},
|
|
303
|
+
meta={
|
|
304
|
+
"command": "agents info",
|
|
305
|
+
"context": {
|
|
306
|
+
"state": "workspace_found",
|
|
307
|
+
"description": "Workspace is ready for use",
|
|
308
|
+
},
|
|
309
|
+
"available_databases": list(database_ids.keys()),
|
|
310
|
+
"next_steps": [
|
|
311
|
+
{
|
|
312
|
+
"action": "query_tasks",
|
|
313
|
+
"command": f"notion databases query --database {database_ids.get('tasks', 'TASKS_DB_ID')}",
|
|
314
|
+
"purpose": "List all tasks in workspace",
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"action": "create_task",
|
|
318
|
+
"command": f"notion pages create --parent {database_ids.get('tasks', 'TASKS_DB_ID')} --title 'Task Name' --properties '{{\"Status\": \"Todo\"}}'",
|
|
319
|
+
"purpose": "Create a new task",
|
|
320
|
+
"note": "Make sure to set Version relation",
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"action": "list_projects",
|
|
324
|
+
"command": f"notion databases query --database {database_ids.get('projects', 'PROJECTS_DB_ID')}",
|
|
325
|
+
"purpose": "List all projects",
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
else:
|
|
331
|
+
# Search for any databases in the page
|
|
332
|
+
results = await client.search(
|
|
333
|
+
query="",
|
|
334
|
+
filter={"value": "database", "property": "object"}
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Count databases in this page
|
|
338
|
+
databases_in_page = []
|
|
339
|
+
for result in results:
|
|
340
|
+
if hasattr(result, 'title'):
|
|
341
|
+
databases_in_page.append(result.title)
|
|
342
|
+
|
|
343
|
+
return format_success(
|
|
344
|
+
{
|
|
345
|
+
"message": "No agents workspace found in this page",
|
|
346
|
+
"parent_page": parent_page_id,
|
|
347
|
+
"parent_title": page.title,
|
|
348
|
+
"workspace_initialized": False,
|
|
349
|
+
"other_databases": len(databases_in_page),
|
|
350
|
+
},
|
|
351
|
+
meta={
|
|
352
|
+
"command": "agents info",
|
|
353
|
+
"context": {
|
|
354
|
+
"state": "no_workspace",
|
|
355
|
+
"description": "No agents workspace detected in this page",
|
|
356
|
+
},
|
|
357
|
+
"next_steps": [
|
|
358
|
+
{
|
|
359
|
+
"action": "initialize_workspace",
|
|
360
|
+
"command": f"notion agents init --parent-page {parent_page_id}",
|
|
361
|
+
"purpose": "Initialize a new agents workspace",
|
|
362
|
+
"note": "This will create 8 databases for workflow management",
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
"error_recovery": {
|
|
366
|
+
"message": "Workspace must be initialized before managing tasks",
|
|
367
|
+
"solution": "Run 'agents init' to create the workspace",
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
except Exception as e:
|
|
373
|
+
result = format_error("INFO_ERROR", str(e), retry=False)
|
|
374
|
+
return result
|
|
375
|
+
|
|
376
|
+
result = asyncio.run(_info())
|
|
377
|
+
typer.echo(result)
|
|
378
|
+
|
|
379
|
+
@agents_app.command("schema")
|
|
380
|
+
def agents_schema(
|
|
381
|
+
format: str = typer.Option(
|
|
382
|
+
"json",
|
|
383
|
+
"--format",
|
|
384
|
+
"-f",
|
|
385
|
+
help="Output format (json, yaml, pretty)",
|
|
386
|
+
),
|
|
387
|
+
) -> None:
|
|
388
|
+
"""
|
|
389
|
+
Get agents plugin schema for AI agents.
|
|
390
|
+
|
|
391
|
+
Returns comprehensive documentation about the agents system including:
|
|
392
|
+
- Concepts (workspace, task, project, version)
|
|
393
|
+
- Workflows (initialize, create_task, query_tasks)
|
|
394
|
+
- Commands documentation (init, info)
|
|
395
|
+
- Best practices and usage examples
|
|
396
|
+
|
|
397
|
+
Example:
|
|
398
|
+
$ notion agents schema
|
|
399
|
+
$ notion agents schema --format json
|
|
400
|
+
$ notion agents schema --format yaml
|
|
401
|
+
$ notion agents schema --format pretty
|
|
402
|
+
"""
|
|
403
|
+
from better_notion.plugins.official.agents_schema import AGENTS_SCHEMA
|
|
404
|
+
from better_notion._cli.docs.formatters import (
|
|
405
|
+
format_schema_json,
|
|
406
|
+
format_schema_yaml,
|
|
407
|
+
format_schema_pretty,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
if format == "json":
|
|
411
|
+
typer.echo(format_schema_json(AGENTS_SCHEMA))
|
|
412
|
+
elif format == "yaml":
|
|
413
|
+
typer.echo(format_schema_yaml(AGENTS_SCHEMA))
|
|
414
|
+
elif format == "pretty":
|
|
415
|
+
typer.echo(format_schema_pretty(AGENTS_SCHEMA))
|
|
416
|
+
else:
|
|
417
|
+
result = format_error(
|
|
418
|
+
"INVALID_FORMAT",
|
|
419
|
+
f"Unknown format: {format}. Supported formats: json, yaml, pretty",
|
|
420
|
+
retry=False,
|
|
421
|
+
)
|
|
422
|
+
typer.echo(result)
|
|
423
|
+
raise typer.Exit(code=1)
|
|
424
|
+
|
|
154
425
|
@agents_app.command("init-project")
|
|
155
426
|
def init_project(
|
|
156
427
|
project_id: str = typer.Option(
|