ofiere-openclaw-plugin 4.18.2 → 4.18.4
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.
- package/package.json +1 -1
- package/src/tools.ts +67 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.18.
|
|
3
|
+
"version": "4.18.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw plugin for Ofiere PM - 12 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, constellation, space file management, and execution plan builder",
|
|
6
6
|
"keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
|
package/src/tools.ts
CHANGED
|
@@ -4269,6 +4269,18 @@ async function handleUpdatePlan(supabase: SupabaseClient, userId: string, params
|
|
|
4269
4269
|
if (params.nodes !== undefined) {
|
|
4270
4270
|
const rootNodes = Array.isArray(params.nodes) ? (params.nodes as any[]).map(normalizeNode) : [];
|
|
4271
4271
|
updates.plan_data = buildPlanJson(params.plan_id as string, (params.name as string) || "Plan", params.description as string | undefined, rootNodes);
|
|
4272
|
+
} else if (params.name !== undefined || params.description !== undefined) {
|
|
4273
|
+
// Sync plan_data inner name/description without replacing nodes
|
|
4274
|
+
const { data: existing } = await supabase.from("pm_plans").select("plan_data").eq("id", params.plan_id as string).eq("user_id", userId);
|
|
4275
|
+
if (existing && existing.length > 0) {
|
|
4276
|
+
try {
|
|
4277
|
+
const pd = JSON.parse(existing[0].plan_data || "{}");
|
|
4278
|
+
if (params.name !== undefined) pd.name = params.name;
|
|
4279
|
+
if (params.description !== undefined) pd.description = params.description;
|
|
4280
|
+
pd.updatedAt = new Date().toISOString();
|
|
4281
|
+
updates.plan_data = JSON.stringify(pd, null, 2);
|
|
4282
|
+
} catch { /* plan_data parse failure — skip inner sync */ }
|
|
4283
|
+
}
|
|
4272
4284
|
}
|
|
4273
4285
|
const { data, error } = await supabase.from("pm_plans").update(updates).eq("id", params.plan_id as string).eq("user_id", userId).select("id");
|
|
4274
4286
|
if (error) return err(error.message);
|
|
@@ -4334,15 +4346,23 @@ async function handleExecutePlan(
|
|
|
4334
4346
|
let folderId = (params.folder_id as string) || null;
|
|
4335
4347
|
|
|
4336
4348
|
// Step 1: Create project folder
|
|
4349
|
+
let folderSkipped = false;
|
|
4350
|
+
let folderError: string | null = null;
|
|
4337
4351
|
if (createFolder && spaceId) {
|
|
4338
4352
|
const folderRow = {
|
|
4339
4353
|
id: crypto.randomUUID(),
|
|
4340
|
-
user_id: userId, space_id: spaceId, name: plan.name || "Untitled Plan",
|
|
4341
|
-
|
|
4354
|
+
user_id: userId, space_id: spaceId, name: data.name || plan.name || "Untitled Plan",
|
|
4355
|
+
folder_type: "project", parent_folder_id: folderId, sort_order: 0,
|
|
4342
4356
|
created_at: new Date().toISOString(), updated_at: new Date().toISOString(),
|
|
4343
4357
|
};
|
|
4344
4358
|
const { error: folderErr } = await supabase.from("pm_folders").insert(folderRow);
|
|
4345
|
-
if (!folderErr)
|
|
4359
|
+
if (!folderErr) {
|
|
4360
|
+
folderId = folderRow.id;
|
|
4361
|
+
} else {
|
|
4362
|
+
folderError = folderErr.message;
|
|
4363
|
+
}
|
|
4364
|
+
} else if (createFolder && !spaceId) {
|
|
4365
|
+
folderSkipped = true;
|
|
4346
4366
|
}
|
|
4347
4367
|
|
|
4348
4368
|
// Step 2: BFS — create tasks with full field mapping
|
|
@@ -4352,7 +4372,7 @@ async function handleExecutePlan(
|
|
|
4352
4372
|
|
|
4353
4373
|
while (queue.length > 0) {
|
|
4354
4374
|
const { node, parentTaskId } = queue.shift()!;
|
|
4355
|
-
if (node.type === "task"
|
|
4375
|
+
if (node.type === "task") {
|
|
4356
4376
|
const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4357
4377
|
const now = new Date().toISOString();
|
|
4358
4378
|
const agentId = node.agentId ? await resolveAgent(node.agentId) : null;
|
|
@@ -4413,48 +4433,78 @@ async function handleExecutePlan(
|
|
|
4413
4433
|
}
|
|
4414
4434
|
}
|
|
4415
4435
|
|
|
4416
|
-
// Step 3: Create dependencies
|
|
4436
|
+
// Step 3: Create dependencies — sibling chaining + parent→child
|
|
4417
4437
|
const depsQueue = [...rootNodes];
|
|
4418
4438
|
let depsCreated = 0;
|
|
4439
|
+
|
|
4440
|
+
// Helper: resolve a node to its nearest task ID (skip gates/milestones)
|
|
4441
|
+
function resolveTaskId(n: any): string | undefined {
|
|
4442
|
+
return idMap.get(n.id);
|
|
4443
|
+
}
|
|
4444
|
+
|
|
4419
4445
|
while (depsQueue.length > 0) {
|
|
4420
4446
|
const node = depsQueue.shift()!;
|
|
4421
|
-
const nodeRealId =
|
|
4447
|
+
const nodeRealId = resolveTaskId(node);
|
|
4448
|
+
|
|
4449
|
+
// Chain siblings: sequential children of this node get chained left-to-right
|
|
4450
|
+
if (Array.isArray(node.children) && node.children.length > 1 && !node.parallel) {
|
|
4451
|
+
// Collect task-type children in order for chaining
|
|
4452
|
+
const taskChildren: string[] = [];
|
|
4453
|
+
for (const child of node.children) {
|
|
4454
|
+
const cid = resolveTaskId(child);
|
|
4455
|
+
if (cid) taskChildren.push(cid);
|
|
4456
|
+
}
|
|
4457
|
+
for (let i = 1; i < taskChildren.length; i++) {
|
|
4458
|
+
await supabase.from("task_dependencies").insert({ user_id: userId, predecessor_id: taskChildren[i - 1], successor_id: taskChildren[i], dependency_type: "finish_to_start", lag_days: 0 });
|
|
4459
|
+
depsCreated++;
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
|
|
4463
|
+
// Parent → first child link (sequential only)
|
|
4422
4464
|
if (nodeRealId && !node.parallel && node.children?.length > 0) {
|
|
4423
|
-
const firstChildId =
|
|
4465
|
+
const firstChildId = resolveTaskId(node.children[0]);
|
|
4424
4466
|
if (firstChildId) {
|
|
4425
4467
|
await supabase.from("task_dependencies").insert({ user_id: userId, predecessor_id: nodeRealId, successor_id: firstChildId, dependency_type: "finish_to_start", lag_days: 0 });
|
|
4426
4468
|
depsCreated++;
|
|
4427
4469
|
}
|
|
4428
|
-
for (let i = 1; i < node.children.length; i++) {
|
|
4429
|
-
const prevId = idMap.get(node.children[i - 1].id);
|
|
4430
|
-
const currId = idMap.get(node.children[i].id);
|
|
4431
|
-
if (prevId && currId) {
|
|
4432
|
-
await supabase.from("task_dependencies").insert({ user_id: userId, predecessor_id: prevId, successor_id: currId, dependency_type: "finish_to_start", lag_days: 0 });
|
|
4433
|
-
depsCreated++;
|
|
4434
|
-
}
|
|
4435
|
-
}
|
|
4436
4470
|
}
|
|
4471
|
+
|
|
4472
|
+
// Parallel fan-out: parent → each child
|
|
4437
4473
|
if (nodeRealId && node.parallel && node.children?.length > 0) {
|
|
4438
4474
|
for (const child of node.children) {
|
|
4439
|
-
const childId =
|
|
4475
|
+
const childId = resolveTaskId(child);
|
|
4440
4476
|
if (childId) {
|
|
4441
4477
|
await supabase.from("task_dependencies").insert({ user_id: userId, predecessor_id: nodeRealId, successor_id: childId, dependency_type: "finish_to_start", lag_days: 0 });
|
|
4442
4478
|
depsCreated++;
|
|
4443
4479
|
}
|
|
4444
4480
|
}
|
|
4445
4481
|
}
|
|
4482
|
+
|
|
4446
4483
|
if (Array.isArray(node.children)) depsQueue.push(...node.children);
|
|
4447
4484
|
}
|
|
4448
4485
|
|
|
4486
|
+
// Chain root-level siblings (sequential root nodes)
|
|
4487
|
+
const rootTaskIds: string[] = [];
|
|
4488
|
+
for (const rn of rootNodes) {
|
|
4489
|
+
const rid = resolveTaskId(rn);
|
|
4490
|
+
if (rid) rootTaskIds.push(rid);
|
|
4491
|
+
}
|
|
4492
|
+
for (let i = 1; i < rootTaskIds.length; i++) {
|
|
4493
|
+
await supabase.from("task_dependencies").insert({ user_id: userId, predecessor_id: rootTaskIds[i - 1], successor_id: rootTaskIds[i], dependency_type: "finish_to_start", lag_days: 0 });
|
|
4494
|
+
depsCreated++;
|
|
4495
|
+
}
|
|
4496
|
+
|
|
4449
4497
|
// Mark plan as deployed
|
|
4450
4498
|
await supabase.from("pm_plans").update({ is_deployed: true, deployed_at: new Date().toISOString(), updated_at: new Date().toISOString() }).eq("id", params.plan_id as string).eq("user_id", userId);
|
|
4451
4499
|
|
|
4452
4500
|
return ok({
|
|
4453
|
-
message: `Plan "${plan.name}" executed successfully`,
|
|
4501
|
+
message: `Plan "${data.name || plan.name}" executed successfully`,
|
|
4454
4502
|
tasks_created: tasksCreated,
|
|
4455
4503
|
dependencies_created: depsCreated,
|
|
4456
4504
|
folder_id: folderId,
|
|
4457
4505
|
space_id: spaceId,
|
|
4506
|
+
...(folderSkipped ? { folder_skipped_reason: "No space_id provided — assign the plan to a space to enable folder creation" } : {}),
|
|
4507
|
+
...(folderError ? { folder_error: folderError } : {}),
|
|
4458
4508
|
});
|
|
4459
4509
|
} catch (e) { return err(e instanceof Error ? e.message : String(e)); }
|
|
4460
4510
|
}
|