ofiere-openclaw-plugin 4.18.1 → 4.18.3

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.ts +30 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.18.1",
3
+ "version": "4.18.3",
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
@@ -4131,7 +4131,7 @@ function generatePlanNodeId(): string {
4131
4131
  }
4132
4132
 
4133
4133
  function generatePlanId(): string {
4134
- return `plan-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
4134
+ return crypto.randomUUID();
4135
4135
  }
4136
4136
 
4137
4137
  /** Normalize agent-provided node into the PlanNode shape stored in plan_data */
@@ -4233,11 +4233,13 @@ async function handleListPlans(supabase: SupabaseClient, userId: string, params:
4233
4233
  async function handleGetPlan(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
4234
4234
  try {
4235
4235
  if (!params.plan_id) return err("Missing required field: plan_id");
4236
- const { data, error } = await supabase.from("pm_plans").select("*").eq("id", params.plan_id as string).eq("user_id", userId).single();
4236
+ const { data, error } = await supabase.from("pm_plans").select("*").eq("id", params.plan_id as string).eq("user_id", userId);
4237
4237
  if (error) return err(error.message);
4238
+ if (!data || data.length === 0) return err("Plan not found");
4239
+ const plan = data[0];
4238
4240
  let parsed: any = null;
4239
- try { parsed = JSON.parse(data.plan_data || "{}"); } catch { parsed = {}; }
4240
- return ok({ plan: { ...data, plan_data: parsed } });
4241
+ try { parsed = JSON.parse(plan.plan_data || "{}"); } catch { parsed = {}; }
4242
+ return ok({ plan: { ...plan, plan_data: parsed } });
4241
4243
  } catch (e) { return err(e instanceof Error ? e.message : String(e)); }
4242
4244
  }
4243
4245
 
@@ -4267,9 +4269,22 @@ async function handleUpdatePlan(supabase: SupabaseClient, userId: string, params
4267
4269
  if (params.nodes !== undefined) {
4268
4270
  const rootNodes = Array.isArray(params.nodes) ? (params.nodes as any[]).map(normalizeNode) : [];
4269
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
+ }
4270
4284
  }
4271
- const { error } = await supabase.from("pm_plans").update(updates).eq("id", params.plan_id as string).eq("user_id", userId);
4285
+ const { data, error } = await supabase.from("pm_plans").update(updates).eq("id", params.plan_id as string).eq("user_id", userId).select("id");
4272
4286
  if (error) return err(error.message);
4287
+ if (!data || data.length === 0) return err("Plan not found");
4273
4288
  return ok({ message: `Plan updated` });
4274
4289
  } catch (e) { return err(e instanceof Error ? e.message : String(e)); }
4275
4290
  }
@@ -4277,8 +4292,9 @@ async function handleUpdatePlan(supabase: SupabaseClient, userId: string, params
4277
4292
  async function handleDeletePlan(supabase: SupabaseClient, userId: string, params: Record<string, unknown>): Promise<ToolResult> {
4278
4293
  try {
4279
4294
  if (!params.plan_id) return err("Missing required field: plan_id");
4280
- const { error } = await supabase.from("pm_plans").delete().eq("id", params.plan_id as string).eq("user_id", userId);
4295
+ const { data, error } = await supabase.from("pm_plans").delete().eq("id", params.plan_id as string).eq("user_id", userId).select("id");
4281
4296
  if (error) return err(error.message);
4297
+ if (!data || data.length === 0) return err("Plan not found — nothing deleted");
4282
4298
  return ok({ message: `Plan deleted`, deleted: true });
4283
4299
  } catch (e) { return err(e instanceof Error ? e.message : String(e)); }
4284
4300
  }
@@ -4330,15 +4346,18 @@ async function handleExecutePlan(
4330
4346
  let folderId = (params.folder_id as string) || null;
4331
4347
 
4332
4348
  // Step 1: Create project folder
4349
+ let folderSkipped = false;
4333
4350
  if (createFolder && spaceId) {
4334
4351
  const folderRow = {
4335
- id: `folder-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
4336
- user_id: userId, space_id: spaceId, name: plan.name || "Untitled Plan",
4352
+ id: crypto.randomUUID(),
4353
+ user_id: userId, space_id: spaceId, name: data.name || plan.name || "Untitled Plan",
4337
4354
  type: "project", parent_folder_id: folderId, sort_order: 0,
4338
4355
  created_at: new Date().toISOString(), updated_at: new Date().toISOString(),
4339
4356
  };
4340
4357
  const { error: folderErr } = await supabase.from("pm_folders").insert(folderRow);
4341
4358
  if (!folderErr) folderId = folderRow.id;
4359
+ } else if (createFolder && !spaceId) {
4360
+ folderSkipped = true;
4342
4361
  }
4343
4362
 
4344
4363
  // Step 2: BFS — create tasks with full field mapping
@@ -4348,7 +4367,7 @@ async function handleExecutePlan(
4348
4367
 
4349
4368
  while (queue.length > 0) {
4350
4369
  const { node, parentTaskId } = queue.shift()!;
4351
- if (node.type === "task" || node.type === "milestone") {
4370
+ if (node.type === "task") {
4352
4371
  const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4353
4372
  const now = new Date().toISOString();
4354
4373
  const agentId = node.agentId ? await resolveAgent(node.agentId) : null;
@@ -4446,11 +4465,12 @@ async function handleExecutePlan(
4446
4465
  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);
4447
4466
 
4448
4467
  return ok({
4449
- message: `Plan "${plan.name}" executed successfully`,
4468
+ message: `Plan "${data.name || plan.name}" executed successfully`,
4450
4469
  tasks_created: tasksCreated,
4451
4470
  dependencies_created: depsCreated,
4452
4471
  folder_id: folderId,
4453
4472
  space_id: spaceId,
4473
+ ...(folderSkipped ? { folder_skipped_reason: "No space_id provided — assign the plan to a space to enable folder creation" } : {}),
4454
4474
  });
4455
4475
  } catch (e) { return err(e instanceof Error ? e.message : String(e)); }
4456
4476
  }