🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

M365 Planner

by @felox63

Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports...

Versionv1.2.3
Downloads563
Stars⭐ 1
TERMINAL
clawhub install m365-planner-v2

πŸ“– About This Skill


name: m365-planner description: Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports group-based plan management, task assignment, progress tracking, and recurring tasks. Requires Azure AD app registration with Group.Read.All and Tasks.ReadWrite.All permissions. license: MIT version: 1.2.3

M365 Planner Skill

Manage Microsoft 365 Planner through Microsoft Graph API.

What's New in v1.2.3

  • 🌍 English Documentation – Complete translation for international ClawHub availability
  • πŸ”§ Portability – Env path now uses os.homedir() instead of hardcoded paths
  • 🧹 Security Cleanup – Full audit for sensitive data (no IDs, names, domains)
  • πŸ“¦ ClawHub-ready – Tarball created, node_modules excluded
  • What's New in v1.2.2

  • πŸ”§ Portability – Env path now uses os.homedir() instead of hardcoded /home/claw/.openclaw/.env
  • 🧹 Security Cleanup – Full audit for sensitive data (no IDs, names, domains)
  • πŸ“¦ ClawHub-ready – Tarball created, node_modules excluded
  • What's New in v1.2.1

  • βœ… Generic Scripts – No hardcoded Group-IDs or Plan names anymore
  • βœ… Command-Line Parameters – All scripts accept IDs as arguments
  • βœ… List Plans Improved – Shows all groups when no ID provided
  • βœ… Flexible Cleanup – Works with any plans/buckets
  • βœ… ClawHub-ready – No project-specific data included
  • What's New in v1.1.0

  • βœ… Node.js Scripts – Standalone scripts without mgc CLI
  • βœ… If-Match Header Support – Correct ETag handling for updates/deletes
  • βœ… Group-Based API – Direct access via M365 Group-ID
  • βœ… Recurring Tasks – Note about native Planner recurrence feature
  • βœ… Cleanup Scripts – Automated task cleanup
  • Prerequisites

    1. Azure AD App Registration (see Setup below) 2. Node.js v18+ with @microsoft/microsoft-graph-client and axios 3. M365 Group (not Security Group or Distribution List!)

    Quick Start

    # Test connection
    node scripts/test-connection.js

    List all plans

    node scripts/list_plans.js

    List plans for specific group

    node scripts/list_plans.js

    Create plan

    node scripts/create_plan.js "Project Name"

    Create task

    node scripts/create_task.js "Task Title"

    Delete completed tasks

    node scripts/cleanup_verlaengerungen.js "" ""

    Setup: Azure AD App Registration

    Step 1: Create App Registration

    Azure Portal: 1. https://portal.azure.com β†’ Azure Active Directory β†’ App registrations 2. "New registration" 3. Name: M365-Planner-Integration 4. Supported account types: Accounts in this organizational directory only 5. Redirect URI: None (Client Credentials Flow)

    Or via Azure CLI:

    az login
    az ad app create --display-name "M365-Planner-Integration" --sign-in-audience "AzureADMyOrg"
    
    Note the Application (client) ID from the output.

    Step 2: Add API Permissions

    Important: Use Application Permissions (not Delegated)!

    Azure Portal: 1. App β†’ API permissions β†’ Add a permission 2. Microsoft Graph β†’ Application permissions 3. Add: - Group.Read.All (not Group.ReadWrite.All – sufficient for Planner) - Tasks.ReadWrite.All 4. Grant Admin Consent: Click "Grant admin consent for [Tenant]"!

    Or via Azure CLI:

    APP_ID="your-app-id"

    Group.Read.All

    az ad app permission add \ --id $APP_ID \ --api 00000003-0000-0000-c000-000000000000 \ --api-permissions 5b567253-7703-48e2-861c-caed61531407=Role

    Tasks.ReadWrite.All

    az ad app permission add \ --id $APP_ID \ --api 00000003-0000-0000-c000-000000000000 \ --api-permissions bdfbf15f-ee85-495a-99a9-ef9b2abb1dcb=Role

    Admin Consent

    az ad app permission admin-consent --id $APP_ID

    Step 3: Create Client Secret

    Azure Portal: 1. App β†’ Certificates & secrets β†’ Client secrets β†’ New client secret 2. Description: OpenClaw Integration 3. Expires: 24 months (Maximum) 4. Copy values immediately! – Only shown once

    Or via Azure CLI:

    az ad app credential reset \
      --id $APP_ID \
      --append \
      --display-name "OpenClaw Integration"
    

    Step 4: Configure Environment

    Store credentials in ~/.openclaw/.env:

    # Microsoft 365 Planner Credentials
    M365_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    M365_CLIENT_SECRET="your-secret-value"
    M365_TENANT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    

    Secure permissions:

    chmod 600 ~/.openclaw/.env
    

    Step 5: Test Connection

    node scripts/test-connection.js
    

    Expected output:

    βœ… Access Token successfully received!
    πŸ“‹ Test: M365 Groups...
       3 groups found:
       - My Team βœ… M365/Planner-capable
    βœ… Connection successful!
    

    Important Notes

    M365 Groups vs. Security Groups

    Planner ONLY works with M365 Groups!

  • βœ… M365 Group – Has Exchange mailbox, Teams, Planner (recognizable by mail attribute)
  • ❌ Security Group – Only for permissions, no Planner
  • ❌ Distribution List – Only for email distribution, no Planner
  • Check groups:

    node scripts/test-connection.js
    
    Shows all groups with status "βœ… M365/Planner-capable".

    Create M365 Group (if none exists):

  • Microsoft 365 Admin Center β†’ Groups β†’ Add a group
  • Or Teams: Create new team (automatically creates M365 Group)
  • If-Match Header (ETag)

    All update and delete operations require the If-Match header!

    Planner uses Optimistic Concurrency Control. Requests fail without ETag.

    Delete Example:

    // Wrong ❌
    await client.api(/planner/tasks/${taskId}).delete();

    // Correct βœ… const task = await client.api(/planner/tasks/${taskId}).get(); await client.api(/planner/tasks/${taskId}) .headers({ 'If-Match': task['@odata.etag'] }) .delete();

    Update Example:

    const task = await client.api(/planner/tasks/${taskId}).get();
    await client.api(/planner/tasks/${taskId})
        .headers({ 'If-Match': task['@odata.etag'] })
        .update({ percentComplete: 50 });
    

    Group-Based API Endpoints

    Do NOT use:

    GET /planner/plans  ❌ (requires complex filter)
    

    Use:

    GET /groups/{group-id}/planner/plans  βœ…
    

    Example:

    const plans = await client.api(/groups/${groupId}/planner/plans).get();
    

    Recurring Tasks

    Microsoft Planner supports native recurring tasks!

    In Planner Web UI or Mobile App: 1. Open task 2. Click "Repeat" 3. Choose frequency: Daily, Weekly, Monthly, Yearly, Custom

    Example:

  • "Domain renewal example.com" β†’ Repeat yearly
  • "Check backup" β†’ Repeat weekly
  • ⚠️ API Limitation: The Graph API does not support creating recurring tasks directly. Recurring tasks must be set up via Planner UI.

    Common Operations

    Plans

    | Operation | Script | |-----------|--------| | List all plans | node scripts/list_plans.js | | Create plan | node scripts/create_plan.js | | Delete plan | node scripts/delete_plan.js |

    Buckets

    | Operation | Script | |-----------|--------| | List buckets | Integrated in list_plans.js | | Create bucket | node scripts/create_bucket.js | | Delete bucket | node scripts/delete_bucket.js |

    Tasks

    | Operation | Script | |-----------|--------| | List tasks | Integrated in list_plans.js | | Create task | node scripts/create_task.js </code> | | Update task | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">node scripts/update_task.js <task-id> --percent-complete 50</code> | | Delete task | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">node scripts/delete_task.js <task-id></code> | | Cleanup | <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">node scripts/cleanup_verlaengerungen.js <group-id> "<plan-name>" "<bucket-name>"</code> |</p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Helper Scripts</h3></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">test-connection.js</h4> Tests connection to Microsoft Graph: <li style="color:#94a3b8;margin:3px 0">Request access token</li> <li style="color:#94a3b8;margin:3px 0">Display tenant information</li> <li style="color:#94a3b8;margin:3px 0">List available M365 Groups</li> <li style="color:#94a3b8;margin:3px 0">Check Planner capability</li></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">node scripts/test-connection.js </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">list_plans.js</h4> Shows all plans in an M365 Group with: <li style="color:#94a3b8;margin:3px 0">Buckets and their tasks</li> <li style="color:#94a3b8;margin:3px 0">Task status (completed/in progress/open)</li> <li style="color:#94a3b8;margin:3px 0">Percentage progress indicator</li></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># Without argument: Shows all available groups node scripts/list_plans.js</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">With Group ID: Shows plans for specific group</h2> node scripts/list_plans.js <group-id> </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">create_plan.js</h4> Creates a new plan with default buckets: <li style="color:#94a3b8;margin:3px 0">To Do</li> <li style="color:#94a3b8;margin:3px 0">In Progress </li> <li style="color:#94a3b8;margin:3px 0">Done</li></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">node scripts/create_plan.js "Project Alpha" <group-id> </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">cleanup_verlaengerungen.js</h4> Cleans up completed tasks from a bucket: <li style="color:#94a3b8;margin:3px 0">Deletes tasks with 100% progress</li> <li style="color:#94a3b8;margin:3px 0">Keeps open tasks</li> <li style="color:#94a3b8;margin:3px 0">Correct If-Match header handling</li></p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">node scripts/cleanup_verlaengerungen.js <group-id> "<plan-name>" "<bucket-name>" </code></pre></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Example:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">node scripts/cleanup_verlaengerungen.js abc-123 "My Project" "Completed" </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Troubleshooting</h3></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: Insufficient privileges</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Admin consent not granted</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">az ad app permission admin-consent --id <app-id> </code></pre> Or in Azure Portal: API permissions β†’ Grant admin consent</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: Group not found</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Planner only works with M365 Groups</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> 1. Check if it's an M365 Group (has mail attribute) 2. Security Groups/Distribution Lists don't work 3. Create new M365 Group (Teams or Admin Center)</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: The If-Match header must be specified</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Update/Delete without ETag</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">// First get task for ETag const task = await client.api(<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/planner/tasks/${id}</code>).get(); // Then update/delete with If-Match header await client.api(<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/planner/tasks/${id}</code>) .headers({ 'If-Match': task['@odata.etag'] }) .delete(); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: This entity set must be queried with a filter</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/planner/plans</code> endpoint requires filter</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> Use group-based endpoint: <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">// Wrong ❌ const plans = await client.api('/planner/plans').get();</p><p style="margin:8px 0">// Correct βœ… const plans = await client.api(<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/groups/${groupId}/planner/plans</code>).get(); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: Cannot find module '@microsoft/microsoft-graph-client'</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Node.js packages not installed</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">cd ~/.openclaw/workspace/skills/m365-planner npm install </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Dependencies</h3></p><p style="margin:8px 0">Install packages locally in skill directory:</p><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">cd ~/.openclaw/workspace/skills/m365-planner npm install @microsoft/microsoft-graph-client axios </code></pre></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">References</h3></p><p style="margin:8px 0"><li style="color:#94a3b8;margin:3px 0"><a href="references/planner-api.md" target="_blank" rel="noopener" style="color:#6366f1">Planner API Overview</a></li> <li style="color:#94a3b8;margin:3px 0"><a href="references/setup-guide.md" target="_blank" rel="noopener" style="color:#6366f1">Setup Guide</a></li> <li style="color:#94a3b8;margin:3px 0"><a href="references/common-patterns.md" target="_blank" rel="noopener" style="color:#6366f1">Common Patterns</a></li> <li style="color:#94a3b8;margin:3px 0"><a href="https://docs.microsoft.com/en-us/graph/api/overview" target="_blank" rel="noopener" style="color:#6366f1">Microsoft Graph API Docs</a></li> <li style="color:#94a3b8;margin:3px 0"><a href="https://docs.microsoft.com/en-us/graph/api/resources/planner-overview" target="_blank" rel="noopener" style="color:#6366f1">Planner REST API Reference</a></li></p><p style="margin:8px 0"><h3 style="color:#e5e7eb;margin:18px 0 8px;font-size:1.05em">Changelog</h3></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">v1.2.3 (2026-04-18)</h4> <li style="color:#94a3b8;margin:3px 0">🌍 <strong style="color:#e5e7eb">English Documentation</strong> – Complete translation for international ClawHub availability</li> <li style="color:#94a3b8;margin:3px 0">πŸ”§ <strong style="color:#e5e7eb">Portability</strong> – Env path uses <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">os.homedir()</code> for cross-system compatibility</li> <li style="color:#94a3b8;margin:3px 0">🧹 <strong style="color:#e5e7eb">Security Audit</strong> – No hardcoded IDs, names, or domains</li></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">v1.2.2 (2026-04-18)</h4> <li style="color:#94a3b8;margin:3px 0">πŸ”§ <strong style="color:#e5e7eb">Portability</strong> – Env path now uses <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">os.homedir()</code> instead of hardcoded <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/home/claw/.openclaw/.env</code></li> <li style="color:#94a3b8;margin:3px 0">🧹 <strong style="color:#e5e7eb">Security Cleanup</strong> – Full audit for sensitive data (no IDs, names, domains)</li> <li style="color:#94a3b8;margin:3px 0">πŸ“¦ <strong style="color:#e5e7eb">ClawHub-ready</strong> – Tarball created, node_modules excluded</li></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">v1.2.1 (2026-04-18)</h4> <li style="color:#94a3b8;margin:3px 0">βœ… <strong style="color:#e5e7eb">Generic Scripts</strong> – No hardcoded Group-IDs or Plan names anymore</li> <li style="color:#94a3b8;margin:3px 0">βœ… <strong style="color:#e5e7eb">Command-Line Parameters</strong> – All scripts accept IDs as arguments</li> <li style="color:#94a3b8;margin:3px 0">βœ… <strong style="color:#e5e7eb">List Plans Improved</strong> – Shows all groups when no ID provided</li> <li style="color:#94a3b8;margin:3px 0">βœ… <strong style="color:#e5e7eb">Flexible Cleanup</strong> – Works with any plans/buckets</li> <li style="color:#94a3b8;margin:3px 0">βœ… <strong style="color:#e5e7eb">ClawHub-ready</strong> – No project-specific data included</li></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">v1.1.0 (2026-04-17)</h4> <li style="color:#94a3b8;margin:3px 0">βœ… Node.js Scripts instead of mgc CLI</li> <li style="color:#94a3b8;margin:3px 0">βœ… If-Match Header Support for updates/deletes</li> <li style="color:#94a3b8;margin:3px 0">βœ… Group-based API Endpoints</li> <li style="color:#94a3b8;margin:3px 0">βœ… Test-Connection Script with M365 Group Detection</li> <li style="color:#94a3b8;margin:3px 0">βœ… List Plans Script with Bucket/Task overview</li> <li style="color:#94a3b8;margin:3px 0">βœ… Cleanup Script for completed tasks</li> <li style="color:#94a3b8;margin:3px 0">βœ… Documentation for recurring tasks (native Planner feature)</li> <li style="color:#94a3b8;margin:3px 0">βœ… Troubleshooting Section expanded</li></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">v1.0.0 (2023-01-19)</h4> <li style="color:#94a3b8;margin:3px 0">Initial version with mgc CLI</li> <li style="color:#94a3b8;margin:3px 0">Azure AD Setup Guide</li> <li style="color:#94a3b8;margin:3px 0">Basic CRUD operations</li> </p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">πŸ’‘ Examples</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0"><pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em"># Test connection node scripts/test-connection.js</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">List all plans</h2> node scripts/list_plans.js</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">List plans for specific group</h2> node scripts/list_plans.js <group-id></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Create plan</h2> node scripts/create_plan.js "Project Name" <group-id></p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Create task</h2> node scripts/create_task.js <plan-id> <bucket-id> "Task Title"</p><p style="margin:8px 0"><h2 style="color:#f3f4f6;margin:20px 0 10px;font-size:1.15em">Delete completed tasks</h2> node scripts/cleanup_verlaengerungen.js <group-id> "<plan-name>" "<bucket-name>" </code></pre></p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">βš™οΈ Configuration</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0">1. Azure AD App Registration (see Setup below) 2. Node.js v18+ with <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">@microsoft/microsoft-graph-client</code> and <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">axios</code> 3. M365 Group (not Security Group or Distribution List!)</p></div></section><section class="skill-card" style="margin-bottom:20px"><h2 style="color:#f8fafc;font-size:1.2em;font-weight:800;margin:0 0 16px;display:flex;align-items:center;gap:8px">πŸ“‹ Tips & Best Practices</h2><div style="font-size:.92em;color:#94a3b8;line-height:1.75"><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: Insufficient privileges</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Admin consent not granted</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">az ad app permission admin-consent --id <app-id> </code></pre> Or in Azure Portal: API permissions β†’ Grant admin consent</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: Group not found</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Planner only works with M365 Groups</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> 1. Check if it's an M365 Group (has mail attribute) 2. Security Groups/Distribution Lists don't work 3. Create new M365 Group (Teams or Admin Center)</p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: The If-Match header must be specified</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Update/Delete without ETag</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">// First get task for ETag const task = await client.api(<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/planner/tasks/${id}</code>).get(); // Then update/delete with If-Match header await client.api(<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/planner/tasks/${id}</code>) .headers({ 'If-Match': task['@odata.etag'] }) .delete(); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: This entity set must be queried with a filter</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> <code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/planner/plans</code> endpoint requires filter</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> Use group-based endpoint: <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">// Wrong ❌ const plans = await client.api('/planner/plans').get();</p><p style="margin:8px 0">// Correct βœ… const plans = await client.api(<code style="background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em">/groups/${groupId}/planner/plans</code>).get(); </code></pre></p><p style="margin:8px 0"><h4 style="color:#d1d5db;margin:14px 0 6px;font-size:.95em">Error: Cannot find module '@microsoft/microsoft-graph-client'</h4></p><p style="margin:8px 0"><strong style="color:#e5e7eb">Cause:</strong> Node.js packages not installed</p><p style="margin:8px 0"><strong style="color:#e5e7eb">Solution:</strong> <pre style="background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0"><code style="color:#a5f3fc;background:none;padding:0;font-size:1em">cd ~/.openclaw/workspace/skills/m365-planner npm install </code></pre></p></div></section></div><div class="two-col-side"></div></div></div><script> document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn => { btn.addEventListener('click', () => { const cmd = btn.getAttribute('data-cmd'); if (!cmd) return; navigator.clipboard.writeText(cmd).then(() => { const orig = btn.textContent; btn.textContent = 'Copied!'; setTimeout(() => btn.textContent = orig, 1500); }).catch(() => {}); }); }); </script><!--$--><!--/$--></main><footer style="background:var(--bg-primary);border-top:1px solid var(--border-secondary);margin-top:60px"><div style="border-top:1px solid var(--border-light);max-width:1200px;margin:0 auto;padding:24px 20px"><div style="display:flex;justify-content:space-between;flex-wrap:wrap;gap:24px;margin-bottom:24px"><div><div style="font-weight:700;color:var(--text-muted);margin-bottom:8px">BytesAgain</div><div style="color:var(--text-muted3);font-size:.82em;max-width:200px">Discover the best AI agent skills for your workflow.</div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Explore</div><div style="margin-bottom:6px"><a href="/skills" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Skills</a></div><div style="margin-bottom:6px"><a href="/articles" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Articles</a></div><div style="margin-bottom:6px"><a href="/use-case" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Cases</a></div></div><div><div style="color:var(--text-muted);font-size:.75em;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px">Company</div><div style="margin-bottom:6px"><a href="/about" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">About</a></div><div style="margin-bottom:6px"><a href="/contact" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Contact</a></div><div style="margin-bottom:6px"><a href="/privacy-policy" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Privacy Policy</a></div><div style="margin-bottom:6px"><a href="/terms" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Terms</a></div><div style="margin-bottom:6px"><a href="/feedback" style="color:var(--text-muted2);text-decoration:none;font-size:.85em">Feedback</a></div></div></div><div style="border-top:1px solid var(--border-light);padding-top:16px"><div style="color:var(--text-muted4);font-size:.8em;margin-bottom:8px">Β© <!-- -->2026<!-- --> BytesAgain. All rights reserved.</div><div style="color:var(--text-muted5);font-size:.75em;line-height:1.6;max-width:720px">BytesAgain is an independent skill directory. We index and link to third-party content (ClawHub, GitHub, LobeHub, Dify, etc.) for informational purposes only. All trademarks, skill names, and content are the property of their respective owners. BytesAgain does not claim ownership of any indexed content.</div></div></div></footer><button style="position:fixed;bottom:28px;right:28px;z-index:1000;width:48px;height:48px;border-radius:50%;border:none;cursor:pointer;background:linear-gradient(135deg,#667eea,#00d4ff);color:#fff;font-size:1.3em;box-shadow:0 4px 20px #667eea66;display:flex;align-items:center;justify-content:center;transition:transform .2s">πŸ’¬</button><script src="/_next/static/chunks/0ze4gu236oq96.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[62894,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"LangProvider\"]\n3:I[89220,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ThemeProvider\"]\n4:I[16988,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\ne:I[68027,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\",1]\n:HL[\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"style\"]\n:HL[\"/_next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n5:Td5e,"])</script><script>self.__next_f.push([1,"[{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"description\":\"Search 60,000+ verified AI agent skills via MCP API or REST. Supports 7 languages. Free, no auth required.\",\"inLanguage\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"ja\",\"ko\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https://bytesagain.com/skills?q={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}},{\"@context\":\"https://schema.org\",\"@type\":\"Organization\",\"name\":\"BytesAgain\",\"url\":\"https://bytesagain.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://bytesagain.com/og-image.png\"},\"description\":\"AI agent skill directory. Search 60,000+ skills, 1,000+ use cases, and community requests.\",\"foundingDate\":\"2026\",\"foundingLocation\":{\"@type\":\"Place\",\"name\":\"Global\"},\"sameAs\":[\"https://x.com/bytesagain\",\"https://github.com/bytesagain/ai-skills\",\"https://clawhub.ai/profile/bytesagain\"],\"contactPoint\":{\"@type\":\"ContactPoint\",\"email\":\"hello@bytesagain.com\",\"contactType\":\"customer support\"},\"numberOfEmployees\":{\"@type\":\"QuantitativeValue\",\"value\":1}},{\"@context\":\"https://schema.org\",\"@type\":\"WebApplication\",\"name\":\"BytesAgain AI Skills Search\",\"url\":\"https://bytesagain.com\",\"applicationCategory\":\"DeveloperApplication\",\"operatingSystem\":\"Web\",\"description\":\"Search engine and MCP API for 60,000+ AI agent skills. Semantic search, role recommendations, and use case packs.\",\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"USD\"},\"featureList\":[\"Search 60,000+ AI agent skills\",\"Role-based recommendations for developers, creators, and traders\",\"1,000+ curated use case packs\",\"Free MCP API and REST API\",\"Multi-language search (EN, ZH, ES, FR, DE, JA, KO)\"],\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":\"https://bytesagain.com/skills?q={search_term_string}\",\"query-input\":\"required name=search_term_string\"},\"dateModified\":\"2026-07-18\"},{\"@context\":\"https://schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"What is BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"BytesAgain is a curated directory of 60,000+ AI agent skills from ClawHub, GitHub, LobeHub, and Dify. Search skills by keyword in 7 languages, browse by role (developer, creator, trader, marketer) or by use case.\"}},{\"@type\":\"Question\",\"name\":\"How do I find AI skills on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Use the search bar on BytesAgain.com to search by keyword in 7 languages. You can also browse by role (developer, creator, trader, marketer) or by use case. Each skill shows install instructions for Claude, Cursor, OpenClaw, Continue, and more.\"}},{\"@type\":\"Question\",\"name\":\"Is BytesAgain free?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes, BytesAgain is completely free. No registration required for searching skills. The MCP API is also free with rate limits.\"}},{\"@type\":\"Question\",\"name\":\"Does BytesAgain have an API for AI agents?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! BytesAgain provides a free MCP SSE endpoint at /api/mcp/sse for AI agents, plus a REST API at /api/mcp?action=search\u0026q=\u003cquery\u003e. No authentication needed.\"}},{\"@type\":\"Question\",\"name\":\"Can I request a new AI skill on BytesAgain?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes! Visit the Requests page on BytesAgain.com to submit a skill request. Your request will be visible to the community and notified to the site admin.\"}}]}]"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"skill\",\"m365-planner-v2\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"skill\",{\"children\":[[\"slug\",\"m365-planner-v2\",\"d\",null],{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"link\",null,{\"rel\":\"llms\",\"href\":\"/llms.txt\"}],[\"$\",\"link\",null,{\"rel\":\"llms-full\",\"href\":\"/llms-full.txt\"}],[\"$\",\"script\",null,{\"async\":true,\"src\":\"https://www.googletagmanager.com/gtag/js?id=G-3C1MM9FWYF\"}],[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n window.dataLayer = window.dataLayer || [];\\n function gtag(){dataLayer.push(arguments);}\\n gtag('js', new Date());\\n gtag('config', 'G-3C1MM9FWYF');\\n \"}}]]}],[\"$\",\"body\",null,{\"className\":\"geist_9e050971-module__05dp7a__className\",\"style\":{\"margin\":0},\"children\":[\"$\",\"$L2\",null,{\"children\":[\"$\",\"$L3\",null,{\"children\":[[\"$\",\"div\",null,{\"style\":{\"width\":\"100%\",\"background\":\"var(--bg-subscribe)\",\"borderBottom\":\"1px solid var(--border-primary)\",\"padding\":\"8px 20px\",\"textAlign\":\"center\",\"fontSize\":\".82em\",\"color\":\"#818cf8\"},\"children\":[\"🎁 \",[\"$\",\"strong\",null,{\"style\":{\"color\":\"var(--text-primary)\"},\"children\":\"Get the FREE AI Skills Starter Guide\"}],\" β€” \",[\"$\",\"a\",null,{\"href\":\"/register\",\"style\":{\"color\":\"#00d4ff\",\"textDecoration\":\"underline\"},\"children\":\"Subscribe β†’\"}]]}],[\"$\",\"$L4\",null,{}],[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$5\"}}],\"$L6\",\"$L7\",\"$L8\"]}]}]}]]}]]}],{\"children\":[\"$L9\",{\"children\":[\"$La\",{\"children\":[\"$Lb\",{},null,false,null]},null,false,\"$@c\"]},null,false,\"$@c\"]},null,false,null],\"$Ld\",false]],\"m\":\"$undefined\",\"G\":[\"$e\",[\"$Lf\"]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"10:I[39756,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n11:I[37457,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n12:I[22016,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0ka051yepewro.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"\"]\n13:I[90940,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n14:I[16397,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n16:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"OutletBoundary\"]\n17:\"$Sreact.suspense\"\n1a:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"ViewportBoundary\"]\n1c:I[97367,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"MetadataBoundary\"]\n"])</script><script>self.__next_f.push([1,"6:[\"$\",\"main\",null,{\"children\":[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"main\",null,{\"style\":{\"minHeight\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"background\":\"#050611\",\"color\":\"#e5e7eb\"},\"children\":[[\"$\",\"style\",null,{\"children\":\"\\n .nf-box { text-align: center; padding: 60px 32px; }\\n .nf-code { font-size: 6rem; font-weight: 900; color: #22d3ee; line-height: 1; margin: 0; }\\n .nf-title { font-size: 1.8rem; font-weight: 800; margin: 12px 0 8px; }\\n .nf-desc { color: var(--text-muted2); font-size: 1rem; margin-bottom: 32px; max-width: 440px; }\\n .nf-link { display: inline-block; padding: 12px 28px; background: linear-gradient(135deg,#34d399,#22d3ee); color: #000; font-weight: 900; border-radius: 12px; text-decoration: none; }\\n \"}],[\"$\",\"div\",null,{\"className\":\"nf-box\",\"children\":[[\"$\",\"p\",null,{\"className\":\"nf-code\",\"children\":\"404\"}],[\"$\",\"h1\",null,{\"className\":\"nf-title\",\"children\":\"Page Not Found\"}],[\"$\",\"p\",null,{\"className\":\"nf-desc\",\"children\":\"The skill or page you're looking for doesn't exist or has been moved.\"}],[\"$\",\"$L12\",null,{\"className\":\"nf-link\",\"href\":\"/\",\"children\":\"Back to BytesAgain\"}]]}]]}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]\n"])</script><script>self.__next_f.push([1,"7:[\"$\",\"$L13\",null,{}]\n8:[\"$\",\"$L14\",null,{}]\n9:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\na:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L10\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L11\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\nb:[\"$\",\"$1\",\"c\",{\"children\":[\"$L15\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L16\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@18\"}]}]]}]\n19:[]\nc:\"$W19\"\nd:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L1a\",null,{\"children\":\"$L1b\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L1c\",null,{\"children\":[\"$\",\"$17\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L1d\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\nf:[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/051nc0vy_6.rl.css?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]\n"])</script><script>self.__next_f.push([1,"1b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"1e:I[27201,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"IconMark\"]\n18:null\n"])</script><script>self.__next_f.push([1,"1d:[[\"$\",\"title\",\"0\",{\"children\":\"M365 Planner β€” AI Agent Skill | BytesAgain | BytesAgain\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports...\"}],[\"$\",\"meta\",\"2\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"meta\",\"3\",{\"name\":\"googlebot\",\"content\":\"index, follow, max-image-preview:large, max-snippet:-1\"}],[\"$\",\"meta\",\"4\",{\"name\":\"llms-txt\",\"content\":\"https://bytesagain.com/llms.txt\"}],[\"$\",\"meta\",\"5\",{\"name\":\"llms-full-txt\",\"content\":\"https://bytesagain.com/llms-full.txt\"}],[\"$\",\"link\",\"6\",{\"rel\":\"canonical\",\"href\":\"https://bytesagain.com/skill/m365-planner-v2\"}],[\"$\",\"meta\",\"7\",{\"name\":\"baidu-site-verification\",\"content\":\"codeva-0evUqX1TFs\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:title\",\"content\":\"M365 Planner β€” AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:description\",\"content\":\"Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports...\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:url\",\"content\":\"https://bytesagain.com/skill/m365-planner-v2\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:site_name\",\"content\":\"BytesAgain\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"13\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"14\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"15\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:title\",\"content\":\"M365 Planner β€” AI Agent Skill | BytesAgain\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:description\",\"content\":\"Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports...\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image\",\"content\":\"https://bytesagain.com/social-preview.png\"}],[\"$\",\"meta\",\"20\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"21\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"22\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L1e\",\"23\",{}]]\n"])</script><script>self.__next_f.push([1,"1f:T1562,"])</script><script>self.__next_f.push([1,"\n .skill-page { max-width: 1100px; margin: 0 auto; padding: 32px 20px 80px; }\n .two-col { display: flex; gap: 32px; align-items: flex-start; }\n .two-col-main { flex: 1; min-width: 0; }\n .two-col-side { width: 300px; flex-shrink: 0; }\n @media (max-width: 860px) {\n .two-col { flex-direction: column; }\n .two-col-side { width: 100%; }\n }\n .breadcrumb { font-size: .82em; color: var(--text-muted2); margin-bottom: 28px; }\n .breadcrumb a { color: #818cf8; text-decoration: none; }\n .breadcrumb a:hover { text-decoration: underline; }\n .skill-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 20px; padding: 28px; margin-bottom: 24px; }\n .skill-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n .skill-badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .skill-top-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }\n .badge { display: inline-flex; align-items: center; gap: 5px; font-size: .75em; font-weight: 600; padding: 4px 12px; border-radius: 999px; border: 1px solid transparent; }\n .skill-title { font-size: 1.6em; font-weight: 800; color: var(--text-primary); margin: 0 0 4px; line-height: 1.2; }\n .skill-owner { font-size: .82em; color: var(--text-muted2); margin: 0 0 14px; }\n .skill-owner span { color: #818cf8; }\n .skill-desc { font-size: .92em; color: var(--text-secondary); line-height: 1.65; margin: 0 0 16px; }\n .skill-meta { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--border-card); }\n .meta-item { display: flex; flex-direction: column; gap: 2px; }\n .meta-label { font-size: .7em; color: var(--text-muted5); text-transform: uppercase; letter-spacing: 1px; font-weight: 600; }\n .meta-value { font-size: .92em; color: var(--text-muted2); font-weight: 600; }\n .tags-row { display: flex; gap: 6px; flex-wrap: wrap; }\n .tag { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 3px 10px; text-decoration: none; }\n .tag:hover { background: #6366f125; }\n .install-box { background: var(--bg-deep); border: 1px solid var(--border-card); border-radius: 12px; overflow: hidden; margin-bottom: 24px; }\n .install-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--border-card); }\n .install-dots { display: flex; gap: 6px; }\n .dot { width: 10px; height: 10px; border-radius: 50%; }\n .install-label { font-size: .72em; color: var(--text-muted5); font-family: monospace; letter-spacing: 1px; }\n .install-body { padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n .install-cmd { color: var(--text-code);\n font-family: 'Courier New', monospace; font-size: 1em; }\n .copy-btn { font-size: .75em; color: #6366f1; background: #6366f115; border: 1px solid #6366f130; border-radius: 6px; padding: 5px 12px; cursor: pointer; white-space: nowrap; transition: all .15s; }\n .copy-btn:hover { background: #6366f125; }\n .btn-secondary { display: inline-flex; align-items: center; gap: 8px; padding: 13px 24px; background: transparent; border: 1px solid var(--border-card); border-radius: 10px; color: #6b7280; text-decoration: none; font-weight: 600; font-size: .95em; transition: all .15s; }\n .btn-secondary:hover { border-color: #818cf8; color: #818cf8; }\n .ours-badge { display: inline-flex; align-items: center; gap: 6px; font-size: .72em; font-weight: 700; color: #22d3ee; background: #22d3ee10; border: 1px solid #22d3ee30; border-radius: 999px; padding: 4px 14px; }\n .section-card { background: var(--bg-card); border: 1px solid var(--border-card); border-radius: 16px; padding: 22px 24px; margin-bottom: 20px; }\n .section-title { color: var(--text-primary); font-size: 1.08em; font-weight: 800; margin: 0 0 12px; display: flex; align-items: center; gap: 8px; }\n /* Script box */\n .script-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; background: var(--bg-input); border-bottom: 1px solid var(--border-card); }\n .script-filename { font-size: .72em; color: var(--text-muted2); font-family: 'Courier New', monospace; }\n .script-copy-btn { font-size: .72em; color: #6366f1; background: none; border: 1px solid #6366f130; border-radius: 4px; padding: 2px 10px; cursor: pointer; }\n .script-copy-btn:hover { background: #6366f115; }\n .script-body { padding: 14px 16px; font-family: 'Courier New', monospace; font-size: .82em; line-height: 1.6; color: var(--text-code); overflow-x: auto; max-height: 420px; overflow-y: auto; white-space: pre; }\n /* Articles */\n .article-card { display: block; background: var(--bg-secondary); border: 1px solid var(--border-primary); border-radius: 10px; padding: 14px 16px; text-decoration: none; transition: border-color .15s; }\n .article-card:hover { border-color: #6366f1; }\n @media (max-width: 600px) {\n .skill-card { padding: 20px; }\n .skill-title { font-size: 1.5em; }\n }\n "])</script><script>self.__next_f.push([1,"15:[[\"$\",\"style\",null,{\"children\":\"$1f\"}],\"$L20\",\"$L21\"]\n"])</script><script>self.__next_f.push([1,"22:I[78297,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n23:T7e48,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\nname: m365-planner\ndescription: Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports group-based plan management, task assignment, progress tracking, and recurring tasks. Requires Azure AD app registration with Group.Read.All and Tasks.ReadWrite.All permissions.\nlicense: MIT\nversion: 1.2.3\n\u003chr style=\"border:none;border-top:1px solid #1e1e3f;margin:12px 0\"\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eM365 Planner Skill\u003c/h2\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eManage Microsoft 365 Planner through Microsoft Graph API.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhat's New in v1.2.3\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e🌍 \u003cstrong style=\"color:#e5e7eb\"\u003eEnglish Documentation\u003c/strong\u003e – Complete translation for international ClawHub availability\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eπŸ”§ \u003cstrong style=\"color:#e5e7eb\"\u003ePortability\u003c/strong\u003e – Env path now uses \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eos.homedir()\u003c/code\u003e instead of hardcoded paths\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e🧹 \u003cstrong style=\"color:#e5e7eb\"\u003eSecurity Cleanup\u003c/strong\u003e – Full audit for sensitive data (no IDs, names, domains)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eπŸ“¦ \u003cstrong style=\"color:#e5e7eb\"\u003eClawHub-ready\u003c/strong\u003e – Tarball created, node_modules excluded\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhat's New in v1.2.2\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eπŸ”§ \u003cstrong style=\"color:#e5e7eb\"\u003ePortability\u003c/strong\u003e – Env path now uses \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eos.homedir()\u003c/code\u003e instead of hardcoded \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/home/claw/.openclaw/.env\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e🧹 \u003cstrong style=\"color:#e5e7eb\"\u003eSecurity Cleanup\u003c/strong\u003e – Full audit for sensitive data (no IDs, names, domains)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eπŸ“¦ \u003cstrong style=\"color:#e5e7eb\"\u003eClawHub-ready\u003c/strong\u003e – Tarball created, node_modules excluded\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhat's New in v1.2.1\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eGeneric Scripts\u003c/strong\u003e – No hardcoded Group-IDs or Plan names anymore\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eCommand-Line Parameters\u003c/strong\u003e – All scripts accept IDs as arguments\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eList Plans Improved\u003c/strong\u003e – Shows all groups when no ID provided\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eFlexible Cleanup\u003c/strong\u003e – Works with any plans/buckets\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eClawHub-ready\u003c/strong\u003e – No project-specific data included\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eWhat's New in v1.1.0\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eNode.js Scripts\u003c/strong\u003e – Standalone scripts without \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003emgc\u003c/code\u003e CLI\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eIf-Match Header Support\u003c/strong\u003e – Correct ETag handling for updates/deletes\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eGroup-Based API\u003c/strong\u003e – Direct access via M365 Group-ID\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eRecurring Tasks\u003c/strong\u003e – Note about native Planner recurrence feature\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eCleanup Scripts\u003c/strong\u003e – Automated task cleanup\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003ePrerequisites\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e1. Azure AD App Registration (see Setup below)\n2. Node.js v18+ with \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e@microsoft/microsoft-graph-client\u003c/code\u003e and \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eaxios\u003c/code\u003e\n3. M365 Group (not Security Group or Distribution List!)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eQuick Start\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Test connection\nnode scripts/test-connection.js\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eList all plans\u003c/h2\u003e\nnode scripts/list_plans.js\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eList plans for specific group\u003c/h2\u003e\nnode scripts/list_plans.js \u003cgroup-id\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCreate plan\u003c/h2\u003e\nnode scripts/create_plan.js \"Project Name\" \u003cgroup-id\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCreate task\u003c/h2\u003e\nnode scripts/create_task.js \u003cplan-id\u003e \u003cbucket-id\u003e \"Task Title\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eDelete completed tasks\u003c/h2\u003e\nnode scripts/cleanup_verlaengerungen.js \u003cgroup-id\u003e \"\u003cplan-name\u003e\" \"\u003cbucket-name\u003e\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eSetup: Azure AD App Registration\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 1: Create App Registration\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAzure Portal:\u003c/strong\u003e\n1. https://portal.azure.com β†’ Azure Active Directory β†’ App registrations\n2. \"New registration\"\n3. Name: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eM365-Planner-Integration\u003c/code\u003e\n4. Supported account types: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eAccounts in this organizational directory only\u003c/code\u003e\n5. Redirect URI: None (Client Credentials Flow)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eOr via Azure CLI:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eaz login\naz ad app create --display-name \"M365-Planner-Integration\" --sign-in-audience \"AzureADMyOrg\"\n\u003c/code\u003e\u003c/pre\u003e\nNote the \u003cstrong style=\"color:#e5e7eb\"\u003eApplication (client) ID\u003c/strong\u003e from the output.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 2: Add API Permissions\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eImportant:\u003c/strong\u003e Use \u003cstrong style=\"color:#e5e7eb\"\u003eApplication Permissions\u003c/strong\u003e (not Delegated)!\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAzure Portal:\u003c/strong\u003e\n1. App β†’ API permissions β†’ Add a permission\n2. Microsoft Graph β†’ Application permissions\n3. Add:\n - \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eGroup.Read.All\u003c/code\u003e (not Group.ReadWrite.All – sufficient for Planner)\n - \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eTasks.ReadWrite.All\u003c/code\u003e\n4. \u003cstrong style=\"color:#e5e7eb\"\u003eGrant Admin Consent:\u003c/strong\u003e Click \"Grant admin consent for [Tenant]\"!\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eOr via Azure CLI:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eAPP_ID=\"your-app-id\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eGroup.Read.All\u003c/h2\u003e\naz ad app permission add \\\n --id $APP_ID \\\n --api 00000003-0000-0000-c000-000000000000 \\\n --api-permissions 5b567253-7703-48e2-861c-caed61531407=Role\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eTasks.ReadWrite.All\u003c/h2\u003e\naz ad app permission add \\\n --id $APP_ID \\\n --api 00000003-0000-0000-c000-000000000000 \\\n --api-permissions bdfbf15f-ee85-495a-99a9-ef9b2abb1dcb=Role\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eAdmin Consent\u003c/h2\u003e\naz ad app permission admin-consent --id $APP_ID\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 3: Create Client Secret\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAzure Portal:\u003c/strong\u003e\n1. App β†’ Certificates \u0026 secrets β†’ Client secrets β†’ New client secret\n2. Description: \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eOpenClaw Integration\u003c/code\u003e\n3. Expires: 24 months (Maximum)\n4. \u003cstrong style=\"color:#e5e7eb\"\u003eCopy values immediately!\u003c/strong\u003e – Only shown once\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eOr via Azure CLI:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eaz ad app credential reset \\\n --id $APP_ID \\\n --append \\\n --display-name \"OpenClaw Integration\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 4: Configure Environment\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eStore credentials in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e~/.openclaw/.env\u003c/code\u003e:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Microsoft 365 Planner Credentials\nM365_CLIENT_ID=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\nM365_CLIENT_SECRET=\"your-secret-value\"\nM365_TENANT_ID=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSecure permissions:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003echmod 600 ~/.openclaw/.env\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eStep 5: Test Connection\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003enode scripts/test-connection.js\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eExpected output:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eβœ… Access Token successfully received!\nπŸ“‹ Test: M365 Groups...\n 3 groups found:\n - My Team βœ… M365/Planner-capable\nβœ… Connection successful!\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eImportant Notes\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eM365 Groups vs. Security Groups\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003ePlanner ONLY works with M365 Groups!\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eM365 Group\u003c/strong\u003e – Has Exchange mailbox, Teams, Planner (recognizable by mail attribute)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e❌ \u003cstrong style=\"color:#e5e7eb\"\u003eSecurity Group\u003c/strong\u003e – Only for permissions, no Planner\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e❌ \u003cstrong style=\"color:#e5e7eb\"\u003eDistribution List\u003c/strong\u003e – Only for email distribution, no Planner\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCheck groups:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003enode scripts/test-connection.js\n\u003c/code\u003e\u003c/pre\u003e\nShows all groups with status \"βœ… M365/Planner-capable\".\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCreate M365 Group (if none exists):\u003c/strong\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eMicrosoft 365 Admin Center β†’ Groups β†’ Add a group\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eOr Teams: Create new team (automatically creates M365 Group)\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eIf-Match Header (ETag)\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eAll update and delete operations require the \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eIf-Match\u003c/code\u003e header!\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003ePlanner uses Optimistic Concurrency Control. Requests fail without ETag.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eDelete Example:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e// Wrong ❌\nawait client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${taskId}\u003c/code\u003e).delete();\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e// Correct βœ…\nconst task = await client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${taskId}\u003c/code\u003e).get();\nawait client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${taskId}\u003c/code\u003e)\n .headers({ 'If-Match': task['@odata.etag'] })\n .delete();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eUpdate Example:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003econst task = await client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${taskId}\u003c/code\u003e).get();\nawait client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${taskId}\u003c/code\u003e)\n .headers({ 'If-Match': task['@odata.etag'] })\n .update({ percentComplete: 50 });\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eGroup-Based API Endpoints\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eDo NOT use:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eGET /planner/plans ❌ (requires complex filter)\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eUse:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eGET /groups/{group-id}/planner/plans βœ…\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eExample:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003econst plans = await client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/groups/${groupId}/planner/plans\u003c/code\u003e).get();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eRecurring Tasks\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eMicrosoft Planner supports native recurring tasks!\u003c/strong\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eIn Planner Web UI or Mobile App:\n1. Open task\n2. Click \"Repeat\"\n3. Choose frequency: Daily, Weekly, Monthly, Yearly, Custom\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eExample:\u003c/strong\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Domain renewal example.com\" β†’ Repeat yearly\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\"Check backup\" β†’ Repeat weekly\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e⚠️ \u003cstrong style=\"color:#e5e7eb\"\u003eAPI Limitation:\u003c/strong\u003e The Graph API does not support creating recurring tasks directly.\nRecurring tasks must be set up via Planner UI.\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eCommon Operations\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ePlans\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Operation | Script |\n|-----------|--------|\n| List all plans | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/list_plans.js\u003c/code\u003e |\n| Create plan | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/create_plan.js \u003cname\u003e \u003cgroup-id\u003e\u003c/code\u003e |\n| Delete plan | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/delete_plan.js \u003cplan-id\u003e\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eBuckets\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Operation | Script |\n|-----------|--------|\n| List buckets | Integrated in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elist_plans.js\u003c/code\u003e |\n| Create bucket | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/create_bucket.js \u003cplan-id\u003e \u003cname\u003e\u003c/code\u003e |\n| Delete bucket | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/delete_bucket.js \u003cbucket-id\u003e\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eTasks\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e| Operation | Script |\n|-----------|--------|\n| List tasks | Integrated in \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003elist_plans.js\u003c/code\u003e |\n| Create task | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/create_task.js \u003cplan-id\u003e \u003cbucket-id\u003e \u003ctitle\u003e\u003c/code\u003e |\n| Update task | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/update_task.js \u003ctask-id\u003e --percent-complete 50\u003c/code\u003e |\n| Delete task | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/delete_task.js \u003ctask-id\u003e\u003c/code\u003e |\n| Cleanup | \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003enode scripts/cleanup_verlaengerungen.js \u003cgroup-id\u003e \"\u003cplan-name\u003e\" \"\u003cbucket-name\u003e\"\u003c/code\u003e |\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eHelper Scripts\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003etest-connection.js\u003c/h4\u003e\nTests connection to Microsoft Graph:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eRequest access token\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDisplay tenant information\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eList available M365 Groups\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eCheck Planner capability\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003enode scripts/test-connection.js\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003elist_plans.js\u003c/h4\u003e\nShows all plans in an M365 Group with:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eBuckets and their tasks\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eTask status (completed/in progress/open)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003ePercentage progress indicator\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Without argument: Shows all available groups\nnode scripts/list_plans.js\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eWith Group ID: Shows plans for specific group\u003c/h2\u003e\nnode scripts/list_plans.js \u003cgroup-id\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ecreate_plan.js\u003c/h4\u003e\nCreates a new plan with default buckets:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eTo Do\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eIn Progress \u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDone\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003enode scripts/create_plan.js \"Project Alpha\" \u003cgroup-id\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ecleanup_verlaengerungen.js\u003c/h4\u003e\nCleans up completed tasks from a bucket:\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eDeletes tasks with 100% progress\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eKeeps open tasks\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eCorrect If-Match header handling\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003enode scripts/cleanup_verlaengerungen.js \u003cgroup-id\u003e \"\u003cplan-name\u003e\" \"\u003cbucket-name\u003e\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eExample:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003enode scripts/cleanup_verlaengerungen.js abc-123 \"My Project\" \"Completed\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eTroubleshooting\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: Insufficient privileges\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Admin consent not granted\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eaz ad app permission admin-consent --id \u003capp-id\u003e\n\u003c/code\u003e\u003c/pre\u003e\nOr in Azure Portal: API permissions β†’ Grant admin consent\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: Group not found\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Planner only works with M365 Groups\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n1. Check if it's an M365 Group (has mail attribute)\n2. Security Groups/Distribution Lists don't work\n3. Create new M365 Group (Teams or Admin Center)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: The If-Match header must be specified\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Update/Delete without ETag\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e// First get task for ETag\nconst task = await client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${id}\u003c/code\u003e).get();\n// Then update/delete with If-Match header\nawait client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${id}\u003c/code\u003e)\n .headers({ 'If-Match': task['@odata.etag'] })\n .delete();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: This entity set must be queried with a filter\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/plans\u003c/code\u003e endpoint requires filter\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e Use group-based endpoint:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e// Wrong ❌\nconst plans = await client.api('/planner/plans').get();\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e// Correct βœ…\nconst plans = await client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/groups/${groupId}/planner/plans\u003c/code\u003e).get();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: Cannot find module '@microsoft/microsoft-graph-client'\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Node.js packages not installed\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecd ~/.openclaw/workspace/skills/m365-planner\nnpm install\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eDependencies\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003eInstall packages locally in skill directory:\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecd ~/.openclaw/workspace/skills/m365-planner\nnpm install @microsoft/microsoft-graph-client axios\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eReferences\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ca href=\"references/planner-api.md\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003ePlanner API Overview\u003c/a\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ca href=\"references/setup-guide.md\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003eSetup Guide\u003c/a\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ca href=\"references/common-patterns.md\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003eCommon Patterns\u003c/a\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ca href=\"https://docs.microsoft.com/en-us/graph/api/overview\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003eMicrosoft Graph API Docs\u003c/a\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e\u003ca href=\"https://docs.microsoft.com/en-us/graph/api/resources/planner-overview\" target=\"_blank\" rel=\"noopener\" style=\"color:#6366f1\"\u003ePlanner REST API Reference\u003c/a\u003e\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch3 style=\"color:#e5e7eb;margin:18px 0 8px;font-size:1.05em\"\u003eChangelog\u003c/h3\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ev1.2.3 (2026-04-18)\u003c/h4\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e🌍 \u003cstrong style=\"color:#e5e7eb\"\u003eEnglish Documentation\u003c/strong\u003e – Complete translation for international ClawHub availability\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eπŸ”§ \u003cstrong style=\"color:#e5e7eb\"\u003ePortability\u003c/strong\u003e – Env path uses \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eos.homedir()\u003c/code\u003e for cross-system compatibility\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e🧹 \u003cstrong style=\"color:#e5e7eb\"\u003eSecurity Audit\u003c/strong\u003e – No hardcoded IDs, names, or domains\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ev1.2.2 (2026-04-18)\u003c/h4\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eπŸ”§ \u003cstrong style=\"color:#e5e7eb\"\u003ePortability\u003c/strong\u003e – Env path now uses \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003eos.homedir()\u003c/code\u003e instead of hardcoded \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/home/claw/.openclaw/.env\u003c/code\u003e\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003e🧹 \u003cstrong style=\"color:#e5e7eb\"\u003eSecurity Cleanup\u003c/strong\u003e – Full audit for sensitive data (no IDs, names, domains)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eπŸ“¦ \u003cstrong style=\"color:#e5e7eb\"\u003eClawHub-ready\u003c/strong\u003e – Tarball created, node_modules excluded\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ev1.2.1 (2026-04-18)\u003c/h4\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eGeneric Scripts\u003c/strong\u003e – No hardcoded Group-IDs or Plan names anymore\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eCommand-Line Parameters\u003c/strong\u003e – All scripts accept IDs as arguments\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eList Plans Improved\u003c/strong\u003e – Shows all groups when no ID provided\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eFlexible Cleanup\u003c/strong\u003e – Works with any plans/buckets\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… \u003cstrong style=\"color:#e5e7eb\"\u003eClawHub-ready\u003c/strong\u003e – No project-specific data included\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ev1.1.0 (2026-04-17)\u003c/h4\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… Node.js Scripts instead of mgc CLI\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… If-Match Header Support for updates/deletes\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… Group-based API Endpoints\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… Test-Connection Script with M365 Group Detection\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… List Plans Script with Bucket/Task overview\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… Cleanup Script for completed tasks\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… Documentation for recurring tasks (native Planner feature)\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eβœ… Troubleshooting Section expanded\u003c/li\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003ev1.0.0 (2023-01-19)\u003c/h4\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eInitial version with mgc CLI\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eAzure AD Setup Guide\u003c/li\u003e\n\u003cli style=\"color:#94a3b8;margin:3px 0\"\u003eBasic CRUD operations\u003c/li\u003e\n\u003c/p\u003e"])</script><script>self.__next_f.push([1,"20:[\"$\",\"div\",null,{\"className\":\"skill-page\",\"children\":[[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@type\\\":\\\"SoftwareApplication\\\",\\\"name\\\":\\\"M365 Planner\\\",\\\"description\\\":\\\"Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports...\\\",\\\"url\\\":\\\"https://bytesagain.com/skill/m365-planner-v2\\\",\\\"applicationCategory\\\":\\\"requires-oauth-token\\\",\\\"operatingSystem\\\":\\\"Any\\\",\\\"offers\\\":{\\\"@type\\\":\\\"Offer\\\",\\\"price\\\":\\\"0\\\",\\\"priceCurrency\\\":\\\"USD\\\"},\\\"publisher\\\":{\\\"@type\\\":\\\"Organization\\\",\\\"name\\\":\\\"BytesAgain\\\",\\\"url\\\":\\\"https://bytesagain.com\\\"}}\"}}],[\"$\",\"div\",null,{\"className\":\"breadcrumb\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/\",\"children\":\"BytesAgain\"}],\" β€Ί \",[\"$\",\"a\",null,{\"href\":\"/skills\",\"children\":\"Skills\"}],\" β€Ί \",\"M365 Planner\"]}],[\"$\",\"div\",null,{\"className\":\"two-col\",\"children\":[[\"$\",\"div\",null,{\"className\":\"two-col-main\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-card\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"skill-badges\",\"children\":[[\"$\",\"span\",null,{\"className\":\"badge\",\"style\":{\"color\":\"#818cf8\",\"background\":\"#818cf822\",\"borderColor\":\"#818cf844\"},\"children\":[\"πŸ¦€\",\" \",\"ClawHub\"]}],false]}],[\"$\",\"div\",null,{\"className\":\"skill-top-actions\",\"children\":[\"$\",\"$L22\",null,{\"slug\":\"m365-planner-v2\"}]}]]}],[\"$\",\"h1\",null,{\"className\":\"skill-title\",\"children\":\"M365 Planner\"}],[\"$\",\"p\",null,{\"className\":\"skill-owner\",\"children\":[\"by \",[\"$\",\"span\",null,{\"children\":[\"@\",\"felox63\"]}]]}],[\"$\",\"p\",null,{\"className\":\"skill-desc\",\"children\":\"Manage Microsoft 365 Planner plans, buckets, and tasks via Microsoft Graph API. Use when creating, listing, updating, or deleting Planner resources. Supports...\"}],[\"$\",\"div\",null,{\"className\":\"skill-meta\",\"children\":[[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Version\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":[\"v\",\"1.2.3\"]}]]}],[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Downloads\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":\"563\"}]]}],false,[\"$\",\"div\",null,{\"className\":\"meta-item\",\"children\":[[\"$\",\"span\",null,{\"className\":\"meta-label\",\"children\":\"Stars\"}],[\"$\",\"span\",null,{\"className\":\"meta-value\",\"children\":[\"⭐ \",\"1\"]}]]}],false,[\"$\",\"div\",null,{\"className\":\"meta-item\",\"style\":{\"flexDirection\":\"row\",\"gap\":6,\"alignItems\":\"center\"},\"children\":[[\"$\",\"a\",\"crypto-defi\",{\"href\":\"/?q=crypto-defi\",\"className\":\"tag\",\"children\":[\"#\",\"crypto-defi\"]}],[\"$\",\"a\",\"legal\",{\"href\":\"/?q=legal\",\"className\":\"tag\",\"children\":[\"#\",\"legal\"]}],[\"$\",\"a\",\"real-estate\",{\"href\":\"/?q=real-estate\",\"className\":\"tag\",\"children\":[\"#\",\"real-estate\"]}],[\"$\",\"a\",\"productivity\",{\"href\":\"/?q=productivity\",\"className\":\"tag\",\"children\":[\"#\",\"productivity\"]}]]}]]}],[\"$\",\"div\",null,{\"style\":{\"marginTop\":6},\"children\":[\"$\",\"a\",null,{\"href\":\"https://clawhub.ai/felox63/m365-planner-v2\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"btn-secondary\",\"style\":{\"padding\":\"6px 12px\",\"fontSize\":\".82em\",\"borderRadius\":8,\"background\":\"transparent\",\"border\":\"1px solid var(--border-card)\",\"color\":\"var(--text-muted2)\",\"textDecoration\":\"none\",\"whiteSpace\":\"nowrap\"},\"children\":[\"View on \",\"ClawHub\",\" β†’\"]}]}]]}],[\"$\",\"div\",null,{\"className\":\"install-box\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-header\",\"children\":[[\"$\",\"div\",null,{\"className\":\"install-dots\",\"children\":[[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#ef4444\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#eab308\"}}],[\"$\",\"div\",null,{\"className\":\"dot\",\"style\":{\"background\":\"#22c55e\"}}]]}],[\"$\",\"span\",null,{\"className\":\"install-label\",\"children\":\"TERMINAL\"}]]}],[\"$\",\"div\",null,{\"className\":\"install-body\",\"style\":{\"flexWrap\":\"wrap\"},\"children\":[[\"$\",\"code\",null,{\"className\":\"install-cmd\",\"children\":\"clawhub install m365-planner-v2\"}],[\"$\",\"button\",null,{\"className\":\"copy-btn\",\"data-cmd\":\"clawhub install m365-planner-v2\",\"style\":{\"fontWeight\":700},\"children\":\"Copy\"}]]}]]}],[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ“– About This Skill\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$23\"}}]]}],null,\"$L24\",\"$L25\",\"$L26\",null,null,null,false,false]}],\"$L27\"]}]]}]\n"])</script><script>self.__next_f.push([1,"21:[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"\\n document.querySelectorAll('.copy-btn, .script-copy-btn').forEach(btn =\u003e {\\n btn.addEventListener('click', () =\u003e {\\n const cmd = btn.getAttribute('data-cmd');\\n if (!cmd) return;\\n navigator.clipboard.writeText(cmd).then(() =\u003e {\\n const orig = btn.textContent;\\n btn.textContent = 'Copied!';\\n setTimeout(() =\u003e btn.textContent = orig, 1500);\\n }).catch(() =\u003e {});\\n });\\n });\\n \"}}]\n"])</script><script>self.__next_f.push([1,"2a:I[71521,[\"/_next/static/chunks/0ph_0rx9ah5dc.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/0i_x3w546rsb3.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/06ig5gym-0n-u.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\",\"/_next/static/chunks/12w5ognupk9fb.js?dpl=dpl_9zPFH6pojPkqyspdDNz28GzCc6KQ\"],\"default\"]\n28:T461,\u003cp style=\"margin:8px 0\"\u003e\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e# Test connection\nnode scripts/test-connection.js\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eList all plans\u003c/h2\u003e\nnode scripts/list_plans.js\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eList plans for specific group\u003c/h2\u003e\nnode scripts/list_plans.js \u003cgroup-id\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCreate plan\u003c/h2\u003e\nnode scripts/create_plan.js \"Project Name\" \u003cgroup-id\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eCreate task\u003c/h2\u003e\nnode scripts/create_task.js \u003cplan-id\u003e \u003cbucket-id\u003e \"Task Title\"\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch2 style=\"color:#f3f4f6;margin:20px 0 10px;font-size:1.15em\"\u003eDelete completed tasks\u003c/h2\u003e\nnode scripts/cleanup_verlaengerungen.js \u003cgroup-id\u003e \"\u003cplan-name\u003e\" \"\u003cbucket-name\u003e\"\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e24:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ’‘ Examples\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$28\"}}]]}]\n25:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"βš™οΈ Configuration\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cp style=\\\"margin:8px 0\\\"\u003e1. Azure AD App Registration (see Setup below)\\n2. Node.js v18+ with \u003ccode style=\\\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\\\"\u003e@microsoft/microsoft-graph-client\u003c/code\u003e and \u003ccode style=\\\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\\\"\u003eaxios\u003c/code\u003e\\n3. M365 Group (not Security Group or Distribution List!)\u003c/p\u003e\"}}]]}]\n29:Te2d,"])</script><script>self.__next_f.push([1,"\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: Insufficient privileges\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Admin consent not granted\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003eaz ad app permission admin-consent --id \u003capp-id\u003e\n\u003c/code\u003e\u003c/pre\u003e\nOr in Azure Portal: API permissions β†’ Grant admin consent\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: Group not found\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Planner only works with M365 Groups\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n1. Check if it's an M365 Group (has mail attribute)\n2. Security Groups/Distribution Lists don't work\n3. Create new M365 Group (Teams or Admin Center)\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: The If-Match header must be specified\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Update/Delete without ETag\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e// First get task for ETag\nconst task = await client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${id}\u003c/code\u003e).get();\n// Then update/delete with If-Match header\nawait client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/tasks/${id}\u003c/code\u003e)\n .headers({ 'If-Match': task['@odata.etag'] })\n .delete();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: This entity set must be queried with a filter\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e \u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/planner/plans\u003c/code\u003e endpoint requires filter\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e Use group-based endpoint:\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003e// Wrong ❌\nconst plans = await client.api('/planner/plans').get();\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e// Correct βœ…\nconst plans = await client.api(\u003ccode style=\"background:#0d0d1e;color:#a5f3fc;padding:1px 5px;border-radius:3px;font-size:.88em\"\u003e/groups/${groupId}/planner/plans\u003c/code\u003e).get();\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003ch4 style=\"color:#d1d5db;margin:14px 0 6px;font-size:.95em\"\u003eError: Cannot find module '@microsoft/microsoft-graph-client'\u003c/h4\u003e\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eCause:\u003c/strong\u003e Node.js packages not installed\u003c/p\u003e\u003cp style=\"margin:8px 0\"\u003e\u003cstrong style=\"color:#e5e7eb\"\u003eSolution:\u003c/strong\u003e\n\u003cpre style=\"background:#0a0a1c;border:1px solid #1e1e3f;border-radius:6px;padding:10px 12px;overflow-x:auto;font-size:.9em;margin:8px 0\"\u003e\u003ccode style=\"color:#a5f3fc;background:none;padding:0;font-size:1em\"\u003ecd ~/.openclaw/workspace/skills/m365-planner\nnpm install\n\u003c/code\u003e\u003c/pre\u003e\u003c/p\u003e"])</script><script>self.__next_f.push([1,"26:[\"$\",\"section\",null,{\"className\":\"skill-card\",\"style\":{\"marginBottom\":20},\"children\":[[\"$\",\"h2\",null,{\"style\":{\"color\":\"#f8fafc\",\"fontSize\":\"1.2em\",\"fontWeight\":800,\"margin\":\"0 0 16px\",\"display\":\"flex\",\"alignItems\":\"center\",\"gap\":8},\"children\":\"πŸ“‹ Tips \u0026 Best Practices\"}],[\"$\",\"div\",null,{\"style\":{\"fontSize\":\".92em\",\"color\":\"#94a3b8\",\"lineHeight\":1.75},\"dangerouslySetInnerHTML\":{\"__html\":\"$29\"}}]]}]\n27:[\"$\",\"div\",null,{\"className\":\"two-col-side\",\"children\":[\"$\",\"$L2a\",null,{\"category\":\"requires-oauth-token\",\"currentSlug\":\"m365-planner-v2\",\"name\":\"M365 Planner\",\"tags\":[\"crypto-defi\",\"legal\",\"real-estate\",\"productivity\"]}]}]\n"])</script></body></html>