nucleus-mcp 1.0.7 → 1.2.0

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.
Files changed (135) hide show
  1. package/package.json +2 -4
  2. package/.github/workflows/ci.yml +0 -72
  3. package/.smithery/shttp/module.js +0 -3
  4. package/.smithery/shttp/module.js.map +0 -7
  5. package/.smithery/stdio/index.cjs +0 -74
  6. package/.smithery/stdio/index.cjs.map +0 -7
  7. package/CHANGELOG.md +0 -65
  8. package/CODE_OF_CONDUCT.md +0 -34
  9. package/COMPARISON.md +0 -20
  10. package/CONTRIBUTING.md +0 -137
  11. package/DEVELOPMENT.md +0 -109
  12. package/LICENSE +0 -21
  13. package/README.md +0 -319
  14. package/ROADMAP.md +0 -24
  15. package/SECURITY.md +0 -88
  16. package/docs/COMMUNITY_FAQ.md +0 -48
  17. package/docs/COMPETITIVE_ANALYSIS.md +0 -90
  18. package/docs/DEMO_SCRIPT.md +0 -86
  19. package/docs/ENTERPRISE.md +0 -169
  20. package/docs/FAQ.md +0 -228
  21. package/docs/QUICK_START.md +0 -155
  22. package/docs/README.md +0 -67
  23. package/docs/RECURSIVE_SNAP_DEMO.gif +0 -0
  24. package/docs/social-preview-hq.png +0 -0
  25. package/docs/social-preview.png +0 -0
  26. package/examples/basic_usage.py +0 -84
  27. package/package.json.real +0 -24
  28. package/pyproject.toml +0 -50
  29. package/registry-server.json +0 -20
  30. package/scripts/demo_governance.py +0 -109
  31. package/scripts/sse_bridge.py +0 -87
  32. package/scripts/verify_windows_paths.py +0 -48
  33. package/server.json +0 -20
  34. package/src/mcp_server_nucleus/__init__.py +0 -8583
  35. package/src/mcp_server_nucleus/__main__.py +0 -9
  36. package/src/mcp_server_nucleus/cli.py +0 -1225
  37. package/src/mcp_server_nucleus/commitment_core.py +0 -146
  38. package/src/mcp_server_nucleus/commitment_ledger.py +0 -841
  39. package/src/mcp_server_nucleus/core/__init__.py +0 -6
  40. package/src/mcp_server_nucleus/core/orchestrator.py +0 -9
  41. package/src/mcp_server_nucleus/core/tool_registration_impl.py +0 -99
  42. package/src/mcp_server_nucleus/daemon/safety/lock.py +0 -121
  43. package/src/mcp_server_nucleus/deployment/render_integration.py +0 -4
  44. package/src/mcp_server_nucleus/hypervisor/__init__.py +0 -0
  45. package/src/mcp_server_nucleus/hypervisor/injector.py +0 -83
  46. package/src/mcp_server_nucleus/hypervisor/locker.py +0 -130
  47. package/src/mcp_server_nucleus/hypervisor/watchdog.py +0 -71
  48. package/src/mcp_server_nucleus/runtime/__init__.py +0 -58
  49. package/src/mcp_server_nucleus/runtime/agent.py +0 -517
  50. package/src/mcp_server_nucleus/runtime/agent_pool.py +0 -797
  51. package/src/mcp_server_nucleus/runtime/agents/base.py +0 -34
  52. package/src/mcp_server_nucleus/runtime/agents/oracle/manifest.json +0 -28
  53. package/src/mcp_server_nucleus/runtime/auth.py +0 -98
  54. package/src/mcp_server_nucleus/runtime/autopilot.py +0 -1057
  55. package/src/mcp_server_nucleus/runtime/broker.py +0 -126
  56. package/src/mcp_server_nucleus/runtime/budget.py +0 -153
  57. package/src/mcp_server_nucleus/runtime/capabilities/base.py +0 -20
  58. package/src/mcp_server_nucleus/runtime/capabilities/brain_ops.py +0 -253
  59. package/src/mcp_server_nucleus/runtime/capabilities/code_ops.py +0 -192
  60. package/src/mcp_server_nucleus/runtime/capabilities/depth_tracker.py +0 -81
  61. package/src/mcp_server_nucleus/runtime/capabilities/feature_map.py +0 -274
  62. package/src/mcp_server_nucleus/runtime/capabilities/marketing_engine.py +0 -205
  63. package/src/mcp_server_nucleus/runtime/capabilities/memory_ops.py +0 -79
  64. package/src/mcp_server_nucleus/runtime/capabilities/proof_system.py +0 -127
  65. package/src/mcp_server_nucleus/runtime/capabilities/render_ops.py +0 -80
  66. package/src/mcp_server_nucleus/runtime/capabilities/render_poller_cap.py +0 -266
  67. package/src/mcp_server_nucleus/runtime/capabilities/self_healing.py +0 -110
  68. package/src/mcp_server_nucleus/runtime/capabilities/strategy.py +0 -138
  69. package/src/mcp_server_nucleus/runtime/capabilities/synthesizer.py +0 -134
  70. package/src/mcp_server_nucleus/runtime/capabilities/web_ops.py +0 -94
  71. package/src/mcp_server_nucleus/runtime/common.py +0 -134
  72. package/src/mcp_server_nucleus/runtime/context_manager.py +0 -342
  73. package/src/mcp_server_nucleus/runtime/context_rules.json +0 -86
  74. package/src/mcp_server_nucleus/runtime/crdt_task_store.py +0 -349
  75. package/src/mcp_server_nucleus/runtime/critic.py +0 -150
  76. package/src/mcp_server_nucleus/runtime/daemon.py +0 -142
  77. package/src/mcp_server_nucleus/runtime/dashboard.py +0 -1087
  78. package/src/mcp_server_nucleus/runtime/depth_ops.py +0 -339
  79. package/src/mcp_server_nucleus/runtime/event_ops.py +0 -103
  80. package/src/mcp_server_nucleus/runtime/event_stream.py +0 -213
  81. package/src/mcp_server_nucleus/runtime/export.py +0 -64
  82. package/src/mcp_server_nucleus/runtime/factory.py +0 -594
  83. package/src/mcp_server_nucleus/runtime/federation.py +0 -1040
  84. package/src/mcp_server_nucleus/runtime/fetcher.py +0 -92
  85. package/src/mcp_server_nucleus/runtime/file_monitor.py +0 -208
  86. package/src/mcp_server_nucleus/runtime/firestore_bridge.py +0 -91
  87. package/src/mcp_server_nucleus/runtime/gcloud_ops.py +0 -186
  88. package/src/mcp_server_nucleus/runtime/hooks.py +0 -246
  89. package/src/mcp_server_nucleus/runtime/identity/gatekeeper.py +0 -106
  90. package/src/mcp_server_nucleus/runtime/identity/keygen.py +0 -147
  91. package/src/mcp_server_nucleus/runtime/identity/manifest.py +0 -62
  92. package/src/mcp_server_nucleus/runtime/identity/trust.py +0 -27
  93. package/src/mcp_server_nucleus/runtime/inspector.py +0 -74
  94. package/src/mcp_server_nucleus/runtime/installer.py +0 -81
  95. package/src/mcp_server_nucleus/runtime/ipc_auth.py +0 -377
  96. package/src/mcp_server_nucleus/runtime/lifecycle.py +0 -129
  97. package/src/mcp_server_nucleus/runtime/llm_client.py +0 -408
  98. package/src/mcp_server_nucleus/runtime/llm_tiers.json +0 -88
  99. package/src/mcp_server_nucleus/runtime/locking.py +0 -228
  100. package/src/mcp_server_nucleus/runtime/loops/fixer.py +0 -110
  101. package/src/mcp_server_nucleus/runtime/memory.py +0 -158
  102. package/src/mcp_server_nucleus/runtime/mounter.py +0 -210
  103. package/src/mcp_server_nucleus/runtime/mounter_ops.py +0 -250
  104. package/src/mcp_server_nucleus/runtime/nuke_protocol.py +0 -147
  105. package/src/mcp_server_nucleus/runtime/orchestrator.py +0 -370
  106. package/src/mcp_server_nucleus/runtime/orchestrator_unified.py +0 -263
  107. package/src/mcp_server_nucleus/runtime/orchestrator_v3.py +0 -617
  108. package/src/mcp_server_nucleus/runtime/plugin_loader.py +0 -84
  109. package/src/mcp_server_nucleus/runtime/policy.py +0 -117
  110. package/src/mcp_server_nucleus/runtime/profiling.py +0 -219
  111. package/src/mcp_server_nucleus/runtime/prometheus.py +0 -286
  112. package/src/mcp_server_nucleus/runtime/proposals.py +0 -101
  113. package/src/mcp_server_nucleus/runtime/publisher.py +0 -109
  114. package/src/mcp_server_nucleus/runtime/pulse.py +0 -516
  115. package/src/mcp_server_nucleus/runtime/registry.py +0 -82
  116. package/src/mcp_server_nucleus/runtime/render_ops.py +0 -83
  117. package/src/mcp_server_nucleus/runtime/schema_gen.py +0 -140
  118. package/src/mcp_server_nucleus/runtime/session_ops.py +0 -224
  119. package/src/mcp_server_nucleus/runtime/stdio_server.py +0 -633
  120. package/src/mcp_server_nucleus/runtime/storage.py +0 -83
  121. package/src/mcp_server_nucleus/runtime/strategy.py +0 -91
  122. package/src/mcp_server_nucleus/runtime/swarm.py +0 -170
  123. package/src/mcp_server_nucleus/runtime/sync_ops.py +0 -878
  124. package/src/mcp_server_nucleus/runtime/task_ingestion.py +0 -1020
  125. package/src/mcp_server_nucleus/runtime/task_ops.py +0 -449
  126. package/src/mcp_server_nucleus/runtime/task_scheduler.py +0 -488
  127. package/src/mcp_server_nucleus/runtime/team.py +0 -59
  128. package/src/mcp_server_nucleus/runtime/triggers.py +0 -237
  129. package/src/mcp_server_nucleus/runtime/vector_store.py +0 -125
  130. package/src/mcp_server_nucleus/runtime/watcher.py +0 -112
  131. package/src/mcp_server_nucleus/tool_tiers.py +0 -243
  132. package/tests/__init__.py +0 -1
  133. package/tests/conftest.py +0 -42
  134. package/tests/test_core.py +0 -235
  135. package/tests/test_search.py +0 -69
@@ -1,449 +0,0 @@
1
- """
2
- Nucleus Runtime - Task Operations (V2)
3
- =====================================
4
- Core logic for task management (CRUD, Claiming, Importing).
5
- Moves task orchestration out of the monolith.
6
- """
7
-
8
- import json
9
- import time
10
- import uuid
11
- import logging
12
- from typing import Dict, Any, List, Optional
13
- from pathlib import Path
14
-
15
- # Relative imports assuming this is in mcp_server_nucleus.runtime
16
- from .common import get_brain_path, _get_state
17
- from .event_ops import _emit_event
18
-
19
- logger = logging.getLogger("nucleus.task_ops")
20
-
21
- def _get_tasks_list() -> List[Dict]:
22
- """Get the tasks array from tasks.json (V2) or fallback to state.json (V1)."""
23
- try:
24
- brain = get_brain_path()
25
- tasks_path = brain / "ledger" / "tasks.json"
26
-
27
- # Priority 1: Read V2 tasks.json
28
- if tasks_path.exists():
29
- with open(tasks_path, "r") as f:
30
- data = json.load(f)
31
- if isinstance(data, dict) and "tasks" in data:
32
- return data["tasks"]
33
- return data
34
-
35
- # Priority 2: Fallback to V1 state.json
36
- state = _get_state()
37
- current_sprint = state.get("current_sprint", {})
38
- return current_sprint.get("tasks", [])
39
- except Exception as e:
40
- logger.error(f"Error getting tasks list: {e}")
41
- return []
42
-
43
- def _save_tasks_list(tasks: List[Dict]) -> str:
44
- """Save the tasks array (prefers V2 tasks.json if it exists)."""
45
- try:
46
- brain = get_brain_path()
47
- tasks_path = brain / "ledger" / "tasks.json"
48
-
49
- # Priority 1: Write to V2 tasks.json if it exists (or create it)
50
- if not tasks_path.exists():
51
- tasks_path.parent.mkdir(parents=True, exist_ok=True)
52
-
53
- with open(tasks_path, "w") as f:
54
- json.dump(tasks, f, indent=2)
55
- return "Tasks saved (V2)"
56
-
57
- except Exception as e:
58
- logger.error(f"Error saving tasks list: {e}")
59
- return f"Error saving tasks: {str(e)}"
60
-
61
- def _list_tasks(
62
- status: Optional[str] = None,
63
- priority: Optional[int] = None,
64
- skill: Optional[str] = None,
65
- claimed_by: Optional[str] = None
66
- ) -> List[Dict]:
67
- """List tasks with optional filters and external provider merging."""
68
- try:
69
- tasks = _get_tasks_list()
70
-
71
- # Ensure all tasks have V2 fields (backward compat)
72
- for task in tasks:
73
- if "id" not in task:
74
- task["id"] = f"task-{str(uuid.uuid4())[:8]}"
75
- if "priority" not in task:
76
- task["priority"] = 3 # Default medium
77
- if "blocked_by" not in task:
78
- task["blocked_by"] = []
79
- if "required_skills" not in task:
80
- # Migrate from preferred_role
81
- if "preferred_role" in task:
82
- task["required_skills"] = [task["preferred_role"].lower()]
83
- else:
84
- task["required_skills"] = []
85
- if "source" not in task:
86
- task["source"] = "synthesizer"
87
- if "created_at" not in task:
88
- task["created_at"] = time.strftime("%Y-%m-%dT%H:%M:%S%z")
89
- if "updated_at" not in task:
90
- task["updated_at"] = task["created_at"]
91
-
92
- # Merge with Commitment Ledger (PEFS)
93
- try:
94
- from mcp_server_nucleus import commitment_ledger
95
- brain = get_brain_path()
96
- ledger = commitment_ledger.load_ledger(brain)
97
-
98
- for comm in ledger.get("commitments", []):
99
- comm_status = comm.get("status", "open").lower()
100
- task_status = "PENDING"
101
- if comm_status == "closed":
102
- task_status = "DONE"
103
-
104
- cm_task = {
105
- "id": comm["id"],
106
- "description": comm["description"],
107
- "status": task_status,
108
- "priority": comm.get("priority", 3),
109
- "blocked_by": [],
110
- "required_skills": comm.get("required_skills", []),
111
- "source": f"ledger:{comm.get('source', 'unknown')}",
112
- "created_at": comm.get("created"),
113
- "claimed_by": None
114
- }
115
- tasks.append(cm_task)
116
- except Exception as e:
117
- logger.warning(f"Failed to merge commitment ledger: {e}")
118
-
119
- # Apply filters
120
- filtered = tasks
121
-
122
- if status:
123
- status_map = {"TODO": "PENDING", "COMPLETE": "DONE"}
124
- target_status = status_map.get(status, status)
125
- filtered = [t for t in filtered if t.get("status", "").upper() == target_status.upper()
126
- or t.get("status", "").upper() == status.upper()]
127
-
128
- if priority is not None:
129
- filtered = [t for t in filtered if t.get("priority") == priority]
130
-
131
- if skill:
132
- filtered = [t for t in filtered if skill.lower() in
133
- [s.lower() for s in t.get("required_skills", [])]]
134
-
135
- if claimed_by:
136
- filtered = [t for t in filtered if claimed_by in str(t.get("claimed_by", ""))]
137
-
138
- # Merge with Cloud Tasks (if available)
139
- try:
140
- from mcp_server_nucleus.runtime.firestore_bridge import get_bridge
141
- cloud_tasks = get_bridge().list_cloud_tasks()
142
- if cloud_tasks:
143
- local_ids = {t["id"] for t in filtered}
144
- for ct in cloud_tasks:
145
- if ct["id"] not in local_ids:
146
- filtered.append(ct)
147
- except Exception as e:
148
- logger.warning(f"Failed to merge cloud tasks: {e}")
149
- pass
150
-
151
- # Sort by priority (asc)
152
- filtered.sort(key=lambda x: x.get("priority", 3))
153
-
154
- return filtered
155
- except Exception as e:
156
- logger.error(f"Error listing tasks: {e}")
157
- return []
158
-
159
- def _add_task(
160
- description: str,
161
- priority: int = 3,
162
- blocked_by: Optional[List[str]] = None,
163
- required_skills: Optional[List[str]] = None,
164
- source: str = "synthesizer",
165
- task_id: Optional[str] = None,
166
- skip_dep_check: bool = False
167
- ) -> Dict[str, Any]:
168
- """Create a new task."""
169
- try:
170
- tasks = _get_tasks_list()
171
- task_ids = {t.get("id") for t in tasks if t.get("id")}
172
-
173
- if blocked_by and not skip_dep_check:
174
- for dep_id in blocked_by:
175
- if dep_id not in task_ids:
176
- return {
177
- "success": False,
178
- "error": f"Referential integrity violation: dependency '{dep_id}' does not exist"
179
- }
180
-
181
- now = time.strftime("%Y-%m-%dT%H:%M:%S%z")
182
- new_task_id = task_id if task_id else f"task-{str(uuid.uuid4())[:8]}"
183
-
184
- if new_task_id in task_ids:
185
- return {"success": False, "error": f"Task ID '{new_task_id}' already exists"}
186
-
187
- if blocked_by and new_task_id in blocked_by:
188
- return {"success": False, "error": "DAG violation: task cannot block itself"}
189
-
190
- new_task = {
191
- "id": new_task_id,
192
- "description": description,
193
- "status": "PENDING" if not blocked_by else "BLOCKED",
194
- "priority": priority,
195
- "blocked_by": blocked_by or [],
196
- "required_skills": required_skills or [],
197
- "claimed_by": None,
198
- "source": source,
199
- "escalation_reason": None,
200
- "created_at": now,
201
- "updated_at": now
202
- }
203
-
204
- tasks.append(new_task)
205
- _save_tasks_list(tasks)
206
-
207
- _emit_event("task_created", source, {
208
- "task_id": new_task_id,
209
- "description": description,
210
- "priority": priority
211
- })
212
-
213
- return {"success": True, "task": new_task}
214
- except Exception as e:
215
- return {"success": False, "error": str(e)}
216
-
217
- def _update_task(task_id: str, updates: Dict[str, Any]) -> Dict:
218
- """Update task fields."""
219
- try:
220
- tasks = _get_tasks_list()
221
-
222
- for task in tasks:
223
- if task.get("id") == task_id or task.get("description") == task_id:
224
- valid_keys = ["status", "priority", "description", "blocked_by",
225
- "required_skills", "claimed_by"]
226
-
227
- if "blocked_by" in updates:
228
- all_ids = {t["id"] for t in tasks}
229
- for dep_id in updates["blocked_by"]:
230
- if dep_id not in all_ids:
231
- raise ValueError(f"Referential integrity violation: Dependency task '{dep_id}' does not exist")
232
-
233
- old_status = task.get("status")
234
-
235
- for key, value in updates.items():
236
- if key in valid_keys:
237
- task[key] = value
238
-
239
- task["updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%S%z")
240
-
241
- _save_tasks_list(tasks)
242
-
243
- new_status = task.get("status")
244
- if old_status != new_status and "status" in updates:
245
- _emit_event(
246
- "task_state_changed",
247
- "brain_update_task",
248
- {
249
- "task_id": task.get("id"),
250
- "old_status": old_status,
251
- "new_status": new_status
252
- }
253
- )
254
-
255
- return {"success": True, "task": task}
256
-
257
- return {"success": False, "error": "Task not found"}
258
- except Exception as e:
259
- return {"success": False, "error": str(e)}
260
-
261
- def _claim_task(task_id: str, agent_id: str) -> Dict[str, Any]:
262
- """Atomically claim a task."""
263
- try:
264
- tasks = _get_tasks_list()
265
-
266
- for task in tasks:
267
- if task.get("id") == task_id or task.get("description") == task_id:
268
- if task.get("claimed_by"):
269
- return {"success": False, "error": f"Task already claimed by {task['claimed_by']}"}
270
-
271
- status = task.get("status", "").upper()
272
- if status not in ["TODO", "PENDING", "READY"]:
273
- return {"success": False, "error": f"Task status is {status}, cannot claim"}
274
-
275
- task["claimed_by"] = agent_id
276
- task["status"] = "IN_PROGRESS"
277
- task["updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%S%z")
278
-
279
- _save_tasks_list(tasks)
280
-
281
- _emit_event("task_claimed", agent_id, {
282
- "task_id": task.get("id", task_id),
283
- "description": task.get("description")
284
- })
285
-
286
- return {"success": True, "task": task}
287
-
288
- return {"success": False, "error": "Task not found"}
289
- except Exception as e:
290
- return {"success": False, "error": str(e)}
291
-
292
- def _get_next_task(skills: List[str]) -> Optional[Dict]:
293
- """Get highest priority unblocked task matching skills."""
294
- try:
295
- tasks = _list_tasks() # This handles sorting and external merging
296
-
297
- actionable = []
298
- for task in tasks:
299
- status = task.get("status", "").upper()
300
- if status not in ["TODO", "PENDING", "READY"]:
301
- continue
302
-
303
- if task.get("claimed_by"):
304
- continue
305
-
306
- blocked_by = task.get("blocked_by", [])
307
- if blocked_by:
308
- all_tasks = _get_tasks_list()
309
- blocking_done = True
310
- for blocker_id in blocked_by:
311
- for t in all_tasks:
312
- if t.get("id") == blocker_id:
313
- if t.get("status", "").upper() not in ["DONE", "COMPLETE"]:
314
- blocking_done = False
315
- break
316
- if not blocking_done:
317
- continue
318
-
319
- required = [s.lower() for s in task.get("required_skills", [])]
320
- available = [s.lower() for s in skills]
321
-
322
- if not required or any(r in available for r in required):
323
- actionable.append(task)
324
-
325
- actionable.sort(key=lambda t: t.get("priority", 3))
326
- return actionable[0] if actionable else None
327
- except Exception as e:
328
- logger.error(f"Error getting next task: {e}")
329
- return None
330
-
331
- def _escalate_task(task_id: str, reason: str) -> Dict[str, Any]:
332
- """Escalate a task to request human help."""
333
- try:
334
- tasks = _get_tasks_list()
335
-
336
- for task in tasks:
337
- if task.get("id") == task_id or task.get("description") == task_id:
338
- task["status"] = "ESCALATED"
339
- task["escalation_reason"] = reason
340
- task["claimed_by"] = None # Unclaim
341
- task["updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%S%z")
342
-
343
- _save_tasks_list(tasks)
344
-
345
- _emit_event("task_escalated", "nucleus_mcp", {
346
- "task_id": task.get("id", task_id),
347
- "reason": reason
348
- })
349
-
350
- return {"success": True, "task": task}
351
-
352
- return {"success": False, "error": "Task not found"}
353
- except Exception as e:
354
- return {"success": False, "error": str(e)}
355
-
356
- def _import_tasks_from_jsonl(jsonl_path: str, clear_existing: bool = False, merge_gtm_params: bool = True) -> Dict:
357
- """Import tasks from a JSONL file into the brain database."""
358
- try:
359
- brain = get_brain_path()
360
-
361
- jsonl_file = Path(jsonl_path)
362
- if not jsonl_file.is_absolute():
363
- jsonl_file = brain / jsonl_path
364
-
365
- if not jsonl_file.exists():
366
- return {
367
- "success": False,
368
- "error": f"File not found: {jsonl_path}",
369
- "imported": 0,
370
- "skipped": 0
371
- }
372
-
373
- existing_tasks = _get_tasks_list()
374
- existing_ids = {t.get("id") for t in existing_tasks if t.get("id")}
375
-
376
- if clear_existing:
377
- existing_tasks = []
378
- existing_ids = set()
379
-
380
- imported = 0
381
- skipped = 0
382
- errors = []
383
- new_tasks = []
384
-
385
- for line_num, line in enumerate(jsonl_file.read_text().splitlines(), 1):
386
- if not line.strip():
387
- continue
388
-
389
- try:
390
- task_data = json.loads(line)
391
- except json.JSONDecodeError as e:
392
- errors.append(f"Line {line_num}: Invalid JSON - {str(e)}")
393
- skipped += 1
394
- continue
395
-
396
- task_id = task_data.get("id")
397
- if not clear_existing and task_id in existing_ids:
398
- skipped += 1
399
- continue
400
-
401
- if not task_data.get("description"):
402
- errors.append(f"Line {line_num}: Missing description")
403
- skipped += 1
404
- continue
405
-
406
- now = time.strftime("%Y-%m-%dT%H:%M:%S%z")
407
- task = {
408
- "id": task_id or f"task-{str(uuid.uuid4())[:8]}",
409
- "description": task_data.get("description"),
410
- "status": task_data.get("status", "PENDING").upper(),
411
- "priority": int(task_data.get("priority", 3)),
412
- "blocked_by": task_data.get("blocked_by", []),
413
- "required_skills": task_data.get("required_skills", []),
414
- "claimed_by": task_data.get("claimed_by"),
415
- "source": f"import:{jsonl_file.name}",
416
- "created_at": task_data.get("created_at", now),
417
- "updated_at": now
418
- }
419
-
420
- # GTM Metadata Merging
421
- if merge_gtm_params:
422
- if "environment" in task_data:
423
- task["environment"] = task_data["environment"]
424
- if "model" in task_data:
425
- task["model"] = task_data["model"]
426
- if "step" in task_data:
427
- task["step"] = task_data["step"]
428
-
429
- new_tasks.append(task)
430
- existing_ids.add(task["id"])
431
- imported += 1
432
-
433
- if imported > 0:
434
- final_tasks = existing_tasks + new_tasks
435
- _save_tasks_list(final_tasks)
436
-
437
- _emit_event("tasks_imported", "nucleus_mcp", {
438
- "source": str(jsonl_file),
439
- "count": imported
440
- })
441
-
442
- return {
443
- "success": True,
444
- "imported": imported,
445
- "skipped": skipped,
446
- "errors": errors
447
- }
448
- except Exception as e:
449
- return {"success": False, "error": str(e), "imported": 0, "skipped": 0}