Back to skills
SkillHub ClubShip Full StackFull Stack

openclaw-quickstart

OpenClaw onboarding guide for new users. Activate when a user asks how to get started with OpenClaw, says they are new, asks for a quickstart or tutorial, wants to learn OpenClaw basics, or mentions "new user tasks" / "onboarding" / "新手任务" / "快速入门". Guides users through 8 practical tasks with automatic progress detection and daily reminders until all tasks are complete.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,087
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
C60.3

Install command

npx @skill-hub/cli install openclaw-skills-openclaw-quickstart

Repository

openclaw/skills

Skill path: skills/heyuncoder/openclaw-quickstart

OpenClaw onboarding guide for new users. Activate when a user asks how to get started with OpenClaw, says they are new, asks for a quickstart or tutorial, wants to learn OpenClaw basics, or mentions "new user tasks" / "onboarding" / "新手任务" / "快速入门". Guides users through 8 practical tasks with automatic progress detection and daily reminders until all tasks are complete.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install openclaw-quickstart into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding openclaw-quickstart to shared team environments
  • Use openclaw-quickstart for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: openclaw-quickstart
description: OpenClaw onboarding guide for new users. Activate when a user asks how to get started with OpenClaw, says they are new, asks for a quickstart or tutorial, wants to learn OpenClaw basics, or mentions "new user tasks" / "onboarding" / "新手任务" / "快速入门". Guides users through 8 practical tasks with automatic progress detection and daily reminders until all tasks are complete.
---

# OpenClaw Quickstart

Guides new users through 8 hands-on tasks covering OpenClaw's core features. Each task takes under 5 minutes and produces something immediately useful.

## Task List

| # | Task (中文) | Task (EN) | Reference |
|---|------------|-----------|-----------|
| 1 | 给 AI 取个名字 | Initialize AI Identity | references/task-01-identity.md |
| 2 | 让 AI 记住你 | Save Your Preferences | references/task-02-memory.md |
| 3 | 查今天天气 | Check the Weather | references/task-03-weather.md |
| 4 | 写一篇日报 | Generate a Report | references/task-04-report.md |
| 5 | 建一个日历提醒 | Set a Reminder | references/task-05-reminder.md |
| 6 | 用浏览器收集信息 | Browser Info Gathering | references/task-06-browser.md |
| 7 | 让 AI 做一张 PPT | Generate a PPT | references/task-07-ppt.md |
| 8 | 安装一个新 Skill | Install a Skill from ClawHub | references/task-08-skill.md |

---

## Workflow

### 0. Auto-Install on First Activation

**Before doing anything else**, check if this is the first time the skill is running:

```bash
# Check if already installed
test -f ~/.openclaw/workspace/.quickstart-progress.json && echo "installed" || echo "fresh"
```

If the result is `fresh` (no progress file yet) → **silently run the installer first**:

```bash
node {baseDir}/scripts/install.js --workspace ~/.openclaw/workspace
```

Do not ask the user — just run it. The installer is idempotent and safe. After it completes, proceed normally.

### 1. Check Progress

Always start by running:

```bash
node scripts/check_progress.js --workspace ~/.openclaw/workspace
```

Parse JSON output to get current task status and `next_task`.

### 2. Show Status & Orient User

Display current progress:

```
📋 你的 OpenClaw 新手任务进度(3/8)

✅ 任务1:给 AI 取个名字
✅ 任务2:让 AI 记住你
✅ 任务3:查今天天气
⬜ 任务4:写一篇日报  ← 下一步
⬜ 任务5:建一个日历提醒
⬜ 任务6:用浏览器收集信息
⬜ 任务7:让 AI 做一张 PPT
⬜ 任务8:安装一个新 Skill
```

Ask if they want to do the next task now, or jump to a specific one.

### 3. Guide Each Task

1. Read the task's reference file
2. Walk the user through it step by step — do it with them, don't just explain
3. After the task action succeeds → **immediately call `mark_done.js`** (see below)
4. Confirm completion, then offer the next task

### 4. First-Run Setup (Installation)

On first activation (no `.quickstart-progress.json` yet), run the installer immediately:

```bash
node scripts/install.js --workspace ~/.openclaw/workspace
```

The installer does **two things in one shot**:
1. **HEARTBEAT.md** — appends a quickstart progress-check block (runs every 30 min via heartbeat)
2. **Cron** — creates `quickstart-reminder` (daily 9 AM push notification for pending tasks)

Optional: customize reminder time:
```bash
node scripts/install.js --workspace ~/.openclaw/workspace --hour 9 --minute 30
```

Preview without applying:
```bash
node scripts/install.js --workspace ~/.openclaw/workspace --dry-run
```

> **Note:** The installer is idempotent — safe to run multiple times. It skips steps already done.

---

## Auto-Mark Completion Rules

**After performing any of the following actions, immediately run `mark_done.js`:**

```bash
node scripts/mark_done.js --task <N> --workspace ~/.openclaw/workspace
```

| Task | Trigger condition |
|------|------------------|
| 1 | SOUL.md or IDENTITY.md written with AI name/rules |
| 2 | USER.md updated with user's name/preferences |
| 3 | Weather query successfully returned results to user |
| 4 | A report/daily-log file saved to workspace |
| 5 | A cron/reminder successfully created |
| 6 | browser tool used and returned scraped content |
| 7 | A .pptx file saved to workspace |
| 8 | A skill successfully installed via clawhub |

**If `all_done: true` is returned** → immediately run:

```bash
node scripts/cleanup_crons.js
```

Then send the graduation message (see below).

---

## Heartbeat Cron Behavior (`quickstart-heartbeat`, every 30 min)

```bash
node scripts/check_progress.js --mark-done --workspace ~/.openclaw/workspace
```

- `--mark-done` flag: auto-persists newly detected completions to progress.json
- If `all_done: true` → run `cleanup_crons.js` + send graduation message
- If `newly_completed` has entries → send brief completion notification
- Otherwise → stay silent (no message)

---

## Daily Reminder Format (`quickstart-reminder`, 9 AM)

```
🦐 嗨!你的 OpenClaw 新手任务还有 N 个待完成:

✅ 任务1:给 AI 取个名字
✅ 任务2:让 AI 记住你
⬜ 任务3:查今天天气  ← 今天来试试?

直接说「帮我完成任务3」就开始了!
```

---

## Graduation Message (all tasks done)

Send this, then run `cleanup_crons.js`:

```
🎓 恭喜!你已完成全部 8 个新手任务!

OpenClaw 你玩明白了 🦐
以后有任何问题,随时问我。

(每日提醒和自动检测已自动关闭)
```

---

## Key Principles

- **Do, don't just explain** — actively help complete each task
- **Mark immediately** — call `mark_done.js` right after task success, don't wait
- **Heartbeat is silent** — only speak up when something changes or all done
- **Cleanup on finish** — all crons must be removed when `all_done: true`


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/check_progress.js

```javascript
#!/usr/bin/env node
/**
 * check_progress.js - OpenClaw Quickstart Task Progress Checker
 *
 * Detection priority:
 *   1. .quickstart-progress.json  (written by AI via mark_done.js — most accurate)
 *   2. File system scan           (objective evidence: SOUL.md, .pptx, etc.)
 *   3. memory/*.md keyword scan   (fallback for ephemeral tasks)
 *
 * Usage:
 *   node check_progress.js [--workspace /path/to/workspace] [--mark-done]
 *
 *   --mark-done  Auto-write newly detected completions back to progress.json
 *
 * Output (JSON):
 *   {
 *     "tasks": [{ "id": 1, "name": "...", "done": true/false, "source": "progress|scan|memory" }],
 *     "completed": 3, "total": 8, "all_done": false,
 *     "next_task": { "id": 4, "name": "..." }
 *   }
 */

const fs   = require('fs');
const path = require('path');

// ── Args ─────────────────────────────────────────────────────────────────────
const args      = process.argv.slice(2);
const wsIdx     = args.indexOf('--workspace');
const workspace = wsIdx !== -1 ? args[wsIdx + 1] : path.join(process.env.HOME, '.openclaw', 'workspace');
const markDone  = args.includes('--mark-done');

// ── Helpers ──────────────────────────────────────────────────────────────────
function readFile(p) {
  try { return fs.readFileSync(p, 'utf8'); } catch { return ''; }
}
function exists(p) { return fs.existsSync(p); }

function globFiles(dir, ext) {
  if (!exists(dir)) return [];
  return fs.readdirSync(dir).filter(f => f.endsWith(ext)).map(f => path.join(dir, f));
}

function memoryContains(ws, ...keywords) {
  const memDir = path.join(ws, 'memory');
  if (!exists(memDir)) return false;
  for (const f of globFiles(memDir, '.md')) {
    const c = readFile(f).toLowerCase();
    if (keywords.some(k => c.includes(k.toLowerCase()))) return true;
  }
  return false;
}

function scanDirForPattern(dir, patterns) {
  if (!exists(dir)) return false;
  for (const f of fs.readdirSync(dir)) {
    const full = path.join(dir, f);
    const stat = fs.statSync(full);
    if (stat.isFile() && patterns.some(p => f.toLowerCase().includes(p))) return true;
    if (stat.isDirectory() && scanDirForPattern(full, patterns)) return true;
  }
  return false;
}

// ── Load progress.json ───────────────────────────────────────────────────────
const progressFile = path.join(workspace, '.quickstart-progress.json');
let savedProgress = {};
try {
  if (exists(progressFile)) savedProgress = JSON.parse(readFile(progressFile));
} catch {}

// ── File-system / memory checks (fallback) ───────────────────────────────────
function scanTask1() {
  for (const fname of ['SOUL.md', 'IDENTITY.md']) {
    const c = readFile(path.join(workspace, fname));
    if (c.length > 100 && (c.toLowerCase().includes('name') || c.includes('名字') || c.includes('identity')))
      return true;
  }
  return false;
}

function scanTask2() {
  const c = readFile(path.join(workspace, 'USER.md'));
  const match = c.match(/\*\*Name:\*\*\s*(.+)/);
  if (match && match[1].trim()) return true;
  return c.length > 200;
}

function scanTask3() {
  return memoryContains(workspace, 'weather', '天气', 'wttr');
}

function scanTask4() {
  if (scanDirForPattern(workspace, ['日报', '周报', 'report', 'daily', 'weekly'])) return true;
  return memoryContains(workspace, '日报', '周报', 'report');
}

function scanTask5() {
  const cronPaths = [
    path.join(process.env.HOME, '.openclaw', 'crons.json'),
    path.join(process.env.HOME, '.openclaw', 'config', 'crons.json'),
  ];
  for (const cp of cronPaths) {
    try {
      const d = JSON.parse(readFile(cp));
      if ((Array.isArray(d) ? d : Object.values(d)).length > 0) return true;
    } catch {}
  }
  return memoryContains(workspace, 'cron', '提醒', 'reminder', '定时');
}

function scanTask6() {
  return memoryContains(workspace, 'browser', '浏览器', 'screenshot', 'snapshot');
}

function scanTask7() {
  if (scanDirForPattern(workspace, ['.pptx', '.ppt'])) return true;
  return memoryContains(workspace, 'ppt', '幻灯片', 'presentation');
}

function scanTask8() {
  const skillDirs = [
    path.join(process.env.HOME, '.openclaw', 'skills'),
    path.join(process.env.HOME, '.openclaw', 'extensions'),
  ];
  const builtins = new Set(['feishu', 'skill-creator', 'clawhub', 'healthcheck', 'weather', 'video-frames']);
  for (const sd of skillDirs) {
    if (!exists(sd)) continue;
    const items = fs.readdirSync(sd).filter(f =>
      fs.statSync(path.join(sd, f)).isDirectory() && !builtins.has(f)
    );
    if (items.length > 0) return true;
  }
  return memoryContains(workspace, 'clawhub install', 'skill 安装', 'installed skill');
}

const SCAN_FNS = [null, scanTask1, scanTask2, scanTask3, scanTask4, scanTask5, scanTask6, scanTask7, scanTask8];

// ── Task Definitions ─────────────────────────────────────────────────────────
const TASKS = [
  { id: 1, name: '给 AI 取个名字',    name_en: 'Initialize AI Identity'  },
  { id: 2, name: '让 AI 记住你',      name_en: 'Save Your Preferences'   },
  { id: 3, name: '查今天天气',        name_en: 'Check the Weather'       },
  { id: 4, name: '写一篇日报',        name_en: 'Generate a Report'       },
  { id: 5, name: '建一个日历提醒',    name_en: 'Set a Reminder'          },
  { id: 6, name: '用浏览器收集信息',  name_en: 'Browser Info Gathering'  },
  { id: 7, name: '让 AI 做一张 PPT', name_en: 'Generate a PPT'          },
  { id: 8, name: '安装一个新 Skill',  name_en: 'Install a Skill'         },
];

// ── Main ─────────────────────────────────────────────────────────────────────
const newlyCompleted = [];

const tasks = TASKS.map(t => {
  const key = `task${t.id}`;

  // Priority 1: progress.json (written by AI via mark_done.js)
  if (savedProgress[key]?.done) {
    return { ...t, done: true, source: 'progress', completedAt: savedProgress[key].completedAt };
  }

  // Priority 2 & 3: file scan + memory
  let done = false;
  let source = null;
  try { done = SCAN_FNS[t.id](); source = done ? 'scan' : null; } catch {}

  // Auto-persist newly detected completions back to progress.json
  if (done && markDone) {
    savedProgress[key] = { done: true, completedAt: new Date().toISOString(), source: 'auto-scan' };
    newlyCompleted.push(t.id);
  }

  return { ...t, done, source };
});

// Write back if --mark-done and any new completions found
if (markDone && newlyCompleted.length > 0) {
  try {
    fs.writeFileSync(progressFile, JSON.stringify(savedProgress, null, 2), 'utf8');
  } catch (e) {
    process.stderr.write(`Warning: could not write progress.json: ${e.message}\n`);
  }
}

const completed  = tasks.filter(t => t.done).length;
const total      = tasks.length;
const all_done   = completed === total;
const next_task  = tasks.find(t => !t.done) || null;

console.log(JSON.stringify({
  tasks,
  completed,
  total,
  all_done,
  next_task,
  newly_completed: newlyCompleted,
  workspace,
}, null, 2));

// Signal for cron/heartbeat handler to run cleanup
if (all_done) {
  process.stderr.write('\nALL_TASKS_DONE\n');
}

```

### scripts/install.js

```javascript
#!/usr/bin/env node
/**
 * install.js - OpenClaw Quickstart One-Click Installer
 *
 * Sets up everything needed for the onboarding experience:
 *   1. Injects quickstart check into HEARTBEAT.md (every 30min auto-detect)
 *   2. Creates quickstart-reminder cron (daily 9 AM push notification)
 *
 * Usage:
 *   node install.js [--workspace /path] [--hour 9] [--minute 0] [--dry-run]
 *
 * Options:
 *   --workspace <path>  Workspace directory (default: ~/.openclaw/workspace)
 *   --hour <n>          Hour for daily reminder (default: 9)
 *   --minute <n>        Minute for daily reminder (default: 0)
 *   --dry-run           Preview changes without writing anything
 */

const fs        = require('fs');
const path      = require('path');
const { spawnSync } = require('child_process');

// ── Args ──────────────────────────────────────────────────────────────────────
const args      = process.argv.slice(2);
const wsIdx     = args.indexOf('--workspace');
const workspace = wsIdx !== -1 ? args[wsIdx + 1] : path.join(process.env.HOME, '.openclaw', 'workspace');
const hourIdx   = args.indexOf('--hour');
const hour      = hourIdx !== -1 ? parseInt(args[hourIdx + 1], 10) : 9;
const minIdx    = args.indexOf('--minute');
const minute    = minIdx !== -1 ? parseInt(args[minIdx + 1], 10) : 0;
const dryRun    = args.includes('--dry-run');

const scriptsDir = path.resolve(__dirname);

console.log(`\n🦐 OpenClaw Quickstart Installer`);
console.log(`   Workspace: ${workspace}`);
console.log(`   Daily reminder: ${String(hour).padStart(2,'0')}:${String(minute).padStart(2,'0')}`);
if (dryRun) console.log(`   Mode: DRY RUN (no changes will be made)\n`);
else console.log('');

let allOk = true;

// ── Step 1: HEARTBEAT.md ──────────────────────────────────────────────────────
const heartbeatFile = path.join(workspace, 'HEARTBEAT.md');
const heartbeatBlock = `
## OpenClaw Quickstart Progress Check

Run the following and parse the JSON output:
\`\`\`
node ${scriptsDir}/check_progress.js --workspace ${workspace} --mark-done
\`\`\`

- If \`all_done\` is true:
  1. Run \`node ${scriptsDir}/cleanup_crons.js\` to remove all quickstart crons
  2. Remove the quickstart block from HEARTBEAT.md (delete lines between "## OpenClaw Quickstart" and "## End OpenClaw Quickstart")
  3. Send the user this message: "🎓 恭喜!你已完成全部 8 个新手任务,OpenClaw 你玩明白了!(提醒已自动关闭)"
- If \`newly_completed\` has entries: send "✅ 新手任务更新:任务[N]「任务名」已自动标记完成!"
- Otherwise: stay silent (HEARTBEAT_OK)

## End OpenClaw Quickstart
`;

console.log('📝 Step 1: Updating HEARTBEAT.md...');

let heartbeatContent = '';
try { heartbeatContent = fs.readFileSync(heartbeatFile, 'utf8'); } catch {}

const alreadyInstalled = heartbeatContent.includes('## OpenClaw Quickstart Progress Check');

if (alreadyInstalled) {
  console.log('   ℹ️  Quickstart block already present in HEARTBEAT.md, skipping.\n');
} else {
  const newContent = heartbeatContent.trimEnd() + '\n' + heartbeatBlock;
  if (dryRun) {
    console.log('   [DRY RUN] Would append to HEARTBEAT.md:');
    console.log(heartbeatBlock.split('\n').map(l => '   | ' + l).join('\n'));
  } else {
    try {
      fs.writeFileSync(heartbeatFile, newContent, 'utf8');
      console.log('   ✅ HEARTBEAT.md updated\n');
    } catch (e) {
      console.error(`   ⚠️  Failed to write HEARTBEAT.md: ${e.message}\n`);
      allOk = false;
    }
  }
}

// ── Step 2: Daily Reminder Cron ───────────────────────────────────────────────
console.log('⏰ Step 2: Creating daily reminder cron (quickstart-reminder)...');

// Check if cron already exists
function openclaw(...cmdArgs) {
  const result = spawnSync('openclaw', cmdArgs, { encoding: 'utf8' });
  if (result.error?.code === 'ENOENT') return { error: 'not-found' };
  const raw = result.stdout || '';
  const start = raw.split('\n').findIndex(l => l.trimStart().startsWith('{'));
  if (start === -1) return { raw, status: result.status };
  try { return { data: JSON.parse(raw.split('\n').slice(start).join('\n')), status: result.status }; }
  catch { return { raw, status: result.status }; }
}

const listResult = openclaw('cron', 'list', '--json');
if (listResult.error === 'not-found') {
  console.error('   ⚠️  openclaw CLI not found. Please run setup_reminder_cron.js manually.\n');
  allOk = false;
} else {
  const jobs = listResult.data?.jobs || [];
  const existing = jobs.find(j => j.name === 'quickstart-reminder');

  if (existing) {
    console.log(`   ℹ️  Cron "quickstart-reminder" already exists (id: ${existing.id}), skipping.\n`);
  } else {
    const reminderTask = [
      `Run: node ${scriptsDir}/check_progress.js --workspace ${workspace}`,
      'Parse the JSON output.',
      'If all_done is true: run cleanup_crons.js, remove quickstart block from HEARTBEAT.md, send 🎓 graduation message.',
      'Otherwise: send a friendly reminder listing ✅ completed and ⬜ pending tasks, highlight next_task, encourage user to complete it.',
    ].join(' ');

    if (dryRun) {
      console.log(`   [DRY RUN] Would create cron: quickstart-reminder @ ${minute} ${hour} * * *\n`);
    } else {
      const addResult = openclaw(
        'cron', 'add',
        '--name', 'quickstart-reminder',
        '--cron', `${minute} ${hour} * * *`,
        '--tz', 'Asia/Shanghai',
        '--announce',
        '--message', reminderTask,
        '--json'
      );

      if (addResult.data?.id) {
        console.log(`   ✅ Created "quickstart-reminder" (id: ${addResult.data.id})\n`);
      } else {
        console.warn(`   ⚠️  Unexpected response:`, JSON.stringify(addResult.data || addResult.raw || '').slice(0, 200));
        allOk = false;
      }
    }
  }
}

// ── Step 3: Summary ───────────────────────────────────────────────────────────
console.log('─'.repeat(50));
if (dryRun) {
  console.log('✅ Dry run complete. Re-run without --dry-run to apply.\n');
} else if (allOk) {
  console.log(`✅ Installation complete!

   What's set up:
   🫀 Heartbeat (every 30min): auto-detects task completions, marks done, graduates when all done
   ⏰ Daily Cron (${String(hour).padStart(2,'0')}:${String(minute).padStart(2,'0')} CST): reminds you of pending tasks each morning

   When all 8 tasks are done, both will automatically shut down. 🎓
`);
} else {
  console.log('⚠️  Installation completed with some warnings. Check the output above.\n');
}

```

### scripts/mark_done.js

```javascript
#!/usr/bin/env node
/**
 * mark_done.js - Mark a quickstart task as completed
 *
 * Called by the AI immediately after the user completes a task action.
 *
 * Usage:
 *   node mark_done.js --task <1-8> [--workspace /path/to/workspace]
 *
 * Output: prints updated progress summary as JSON
 */

const fs = require('fs');
const path = require('path');

// Parse args
const args = process.argv.slice(2);
function getArg(name) {
  const idx = args.indexOf(name);
  return idx !== -1 ? args[idx + 1] : null;
}

const taskId = parseInt(getArg('--task'), 10);
const workspace = getArg('--workspace') || path.join(process.env.HOME, '.openclaw', 'workspace');

if (!taskId || taskId < 1 || taskId > 8) {
  console.error('Usage: node mark_done.js --task <1-8> [--workspace /path]');
  process.exit(1);
}

const progressFile = path.join(workspace, '.quickstart-progress.json');

// Load existing progress
let progress = {};
try {
  if (fs.existsSync(progressFile)) {
    progress = JSON.parse(fs.readFileSync(progressFile, 'utf8'));
  }
} catch {}

// Mark task as done
progress[`task${taskId}`] = {
  done: true,
  completedAt: new Date().toISOString(),
};

// Save
fs.writeFileSync(progressFile, JSON.stringify(progress, null, 2), 'utf8');

const completedCount = Object.values(progress).filter(v => v.done).length;
const total = 8;
const allDone = completedCount === total;

console.log(JSON.stringify({
  marked: taskId,
  completed: completedCount,
  total,
  all_done: allDone,
  progress,
}, null, 2));

if (allDone) {
  console.error('\n🎓 All tasks complete! Run cleanup_crons.js to remove reminders.');
}

```

### scripts/cleanup_crons.js

```javascript
#!/usr/bin/env node
/**
 * cleanup_crons.js - Remove all quickstart-related cron jobs
 *
 * Called automatically when all 8 tasks are completed.
 * Looks up crons by name and removes them by id.
 * Targets: quickstart-reminder, quickstart-heartbeat
 *
 * Usage:
 *   node cleanup_crons.js
 */

const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');

const TARGET_NAMES = new Set(['quickstart-reminder', 'quickstart-heartbeat']);

// ── Clean up HEARTBEAT.md ─────────────────────────────────────────────────────
const args = process.argv.slice(2);
const wsIdx = args.indexOf('--workspace');
const workspace = wsIdx !== -1 ? args[wsIdx + 1] : path.join(process.env.HOME, '.openclaw', 'workspace');
const heartbeatFile = path.join(workspace, 'HEARTBEAT.md');

console.log('🧹 Cleaning up HEARTBEAT.md...');
try {
  let content = fs.readFileSync(heartbeatFile, 'utf8');
  // Remove the quickstart block between markers
  const startMarker = '## OpenClaw Quickstart Progress Check';
  const endMarker = '## End OpenClaw Quickstart';
  const startIdx = content.indexOf(startMarker);
  const endIdx = content.indexOf(endMarker);
  if (startIdx !== -1 && endIdx !== -1) {
    // Remove from the blank line before start marker to end of end marker line
    const before = content.slice(0, startIdx).trimEnd();
    const after = content.slice(endIdx + endMarker.length).replace(/^\n/, '');
    content = before + (after ? '\n' + after : '\n');
    fs.writeFileSync(heartbeatFile, content, 'utf8');
    console.log('  ✅ Quickstart block removed from HEARTBEAT.md');
  } else {
    console.log('  ℹ️  Quickstart block not found in HEARTBEAT.md (already removed)');
  }
} catch (e) {
  console.warn(`  ⚠️  Could not update HEARTBEAT.md: ${e.message}`);
}

function openclaw(...args) {
  const result = spawnSync('openclaw', args, { encoding: 'utf8' });
  if (result.error?.code === 'ENOENT') {
    console.error("⚠️  'openclaw' CLI not found in PATH.");
    process.exit(1);
  }
  // stdout may have decorative warning boxes before the JSON — find last { that parses
  const raw = result.stdout || '';
  // Try to find the JSON object by scanning for lines starting with '{'
  const lines = raw.split('\n');
  for (let i = 0; i < lines.length; i++) {
    if (lines[i].trimStart().startsWith('{')) {
      try { return JSON.parse(lines.slice(i).join('\n')); } catch {}
    }
  }
  return null;
}

// Step 1: list all crons
console.log('Fetching cron list...');
const list = openclaw('cron', 'list', '--json');
if (!list || !Array.isArray(list.jobs)) {
  console.error('⚠️  Could not retrieve cron list.');
  process.exit(1);
}

// Step 2: find matching crons by name
const toRemove = list.jobs.filter(j => TARGET_NAMES.has(j.name));

if (toRemove.length === 0) {
  console.log('ℹ️  No quickstart crons found (already removed or never created).');
  console.log('🎓 Congratulations on completing all OpenClaw onboarding tasks!');
  process.exit(0);
}

// Step 3: remove each by id
let allOk = true;
for (const job of toRemove) {
  console.log(`Removing "${job.name}" (${job.id})...`);
  const res = openclaw('cron', 'rm', job.id, '--json');
  if (res?.ok || res?.removed) {
    console.log(`  ✅ Removed`);
  } else {
    console.warn(`  ⚠️  Unexpected response:`, JSON.stringify(res));
    allOk = false;
  }
}

if (allOk) {
  console.log('\n✅ All quickstart crons removed. No more reminders!');
  console.log('🎓 Congratulations on completing all OpenClaw onboarding tasks!');
} else {
  console.log('\n⚠️  Some crons may not have been removed. Check: openclaw cron list');
}

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### _meta.json

```json
{
  "owner": "heyuncoder",
  "slug": "openclaw-quickstart",
  "displayName": "OpenClaw Quickstart",
  "latest": {
    "version": "1.0.0",
    "publishedAt": 1772364788875,
    "commit": "https://github.com/openclaw/skills/commit/4fc1d3986992989dd62c6196bd2f22ca37a7b46f"
  },
  "history": []
}

```

### references/task-01-identity.md

```markdown
# 任务 1:给 AI 取个名字

## 目标
完成 AI 身份初始化,让 AI 知道自己叫什么、有什么性格、遵守什么规则。

## 为什么重要
OpenClaw 的 AI 默认是空白的。通过写 SOUL.md 和 IDENTITY.md,你给它注入灵魂——每次启动它都会读取这些文件,形成稳定的个性。

## 动手步骤

### 第一步:直接跟 AI 说

> "我想给你取个名字,帮我初始化你的身份"

AI 会引导你完成对话。

### 第二步:AI 会帮你写的文件

**SOUL.md** — AI 的行为准则,包含:
- 行为风格(正式/随意/简洁/幽默)
- 安全规则(不泄露密码和 API Key)
- 删除保护(删文件前必须确认)
- 外部操作规则(发消息前要确认)

**IDENTITY.md** — AI 的身份卡片,包含:
- 名字(比如:小虾、阿米、NOVA)
- 定位(私人秘书?研究助手?代码伙伴?)
- 签名 emoji

### 第三步:验证

跟 AI 说「你叫什么名字?你有什么规则?」—— 它应该能回答出你刚定义的内容。

## 示例对话

```
你:帮我初始化你的身份,我想给你取名叫"阿米",你要保守秘密,删文件前要问我
AI:好的!我来帮你写 SOUL.md 和 IDENTITY.md...
```

## ✅ 完成标志
SOUL.md 或 IDENTITY.md 存在,且包含 AI 名字相关内容。

```

### references/task-02-memory.md

```markdown
# 任务 2:让 AI 记住你

## 目标
告诉 AI 你是谁、你的偏好,让它在每次对话中都记得你。

## 为什么重要
每次 OpenClaw 启动都是全新的。记忆文件(USER.md / MEMORY.md)让 AI 跨会话记住你的名字、习惯和偏好,不用每次重复介绍自己。

## 动手步骤

### 第一步:告诉 AI 基本信息

> "记住我的名字叫[你的名字],我在[城市],时区是[时区],平时说中文"

### 第二步:说几个偏好

比如:
- "我喜欢简短的回复,不要废话"
- "我早上 8 点起床,晚上 11 点睡觉"
- "我用 Mac,主要用飞书和微信"
- "回复我的时候用中文,代码注释用英文"

### 第三步:AI 会更新的文件

**USER.md** — 关于你的基础信息:
```markdown
- **Name:** 小明
- **Timezone:** Asia/Shanghai
- **Language:** 中文
- **Notes:** 简短回复,早 8 晚 11
```

**MEMORY.md** — AI 的长期记忆(更丰富的上下文)

### 第四步:验证

新开一个对话,问:「你还记得我叫什么吗?」—— AI 应该能说出你的名字。

## ✅ 完成标志
USER.md 存在,且包含你的姓名信息。

```

### references/task-03-weather.md

```markdown
# 任务 3:查今天天气

## 目标
用一句话触发 OpenClaw 的 weather skill,体验内置 Skill 的用法。

## 为什么重要
Skill 是 OpenClaw 的超能力模块。天气 Skill 是最简单的内置 Skill,一句话就能触发,适合第一次感受"原来 AI 会自动调用工具"。

## 动手步骤

### 就说这一句话:

> "帮我查一下[你的城市]今天的天气"

比如:
- "帮我查一下上海今天的天气"
- "北京今天天气怎么样"
- "What's the weather in Tokyo today?"

### AI 会做什么

1. 自动识别这是天气查询
2. 加载 weather skill
3. 调用 wttr.in 或 Open-Meteo 接口
4. 返回今日天气:温度、天气状况、体感温度

### 示例输出

```
🌤 上海今日天气
温度:18°C(体感 16°C)
天气:多云转晴
风速:东风 12km/h
建议:适合外出,带件薄外套
```

## 延伸玩法
- "未来三天的天气怎么样?"
- "下周末适合去爬山吗?"
- "今天要带伞吗?"

## ✅ 完成标志
成功收到 AI 返回的天气信息(会在 memory 中记录使用记录)。

```

### references/task-04-report.md

```markdown
# 任务 4:写一篇日报

## 目标
让 AI 帮你写一篇工作日报或周报,体验 AI 的文档生成能力。

## 为什么重要
这是最直接的生产力场景。很多人每天要写日报周报,这正是 AI 擅长的——你提供要点,它帮你组织语言、生成格式。

## 动手步骤

### 第一步:提供今天的工作内容

> "帮我写今天的工作日报,今天做了:
> 1. 开了产品评审会,确定了 Q2 排期
> 2. 修了 3 个 Bug,都已上线
> 3. 写了新功能的需求文档初稿
> 明天计划:联调测试环境"

### 第二步:AI 生成日报

AI 会自动:
- 整理成标准格式(今日完成 / 遇到问题 / 明日计划)
- 语言润色,专业且简洁
- 保存到 workspace 文件夹(如 `daily-report-2026-03-01.md`)

### 示例输出

```markdown
## 工作日报 2026-03-01

**今日完成**
- ✅ 产品评审会:Q2 排期确认,主要功能优先级已对齐
- ✅ Bug 修复:修复并上线 3 个问题(#123 #124 #125)
- ✅ 需求文档:新功能初稿完成,待评审

**明日计划**
- 与测试团队联调测试环境
```

### 延伸玩法
- "帮我写本周的周报"
- "把这篇日报翻译成英文"
- "帮我把日报整理成更正式的邮件格式"
- "帮我分析最近一周的工作,哪些可以优化?"

## ✅ 完成标志
workspace 中生成了日报/周报文件(.md 格式)。

```

### references/task-05-reminder.md

```markdown
# 任务 5:建一个日历提醒

## 目标
设置一个定时提醒,让 AI 在指定时间主动联系你。

## 为什么重要
AI 不只是被动回答问题,它可以主动出击。设置提醒后,AI 会在你设定的时间发通知,就像有个贴心助手帮你记事。

## 动手步骤

### 第一步:直接说你要提醒的事

> "明天早上 9 点提醒我开周会"

> "每天下午 6 点提醒我写日报"

> "这个周五下午 3 点提醒我给客户发报价单"

### 第二步:AI 会做什么

AI 会创建一个 cron 任务,到时间通过你配置的 IM 渠道(飞书/Telegram 等)发送提醒。

### 单次提醒示例

```
你:明天早上 9 点提醒我开周会
AI:好的,我已设置提醒:
    ⏰ 明天 2026-03-02 09:00
    📌 事项:开周会
    通知将通过飞书发送给你
```

### 定期提醒示例

```
你:每天下午 6 点提醒我写日报
AI:已设置每日定时任务:
    🔁 每天 18:00
    📌 提醒写工作日报
```

### 延伸玩法
- "每周一早上 8:30 提醒我看本周计划"
- "每月 25 号提醒我报销"
- "取消之前设置的周会提醒"
- "帮我看看现在有哪些提醒任务"

## ✅ 完成标志
AI 成功创建了一个 cron/reminder 任务。

```

### references/task-06-browser.md

```markdown
# 任务 6:用浏览器收集信息

## 目标
让 AI 控制浏览器,帮你搜索一个主题并整理成摘要。

## 为什么重要
AI + 浏览器 = 超级调研助手。它能打开网页、阅读内容、汇总要点,帮你节省大量手动查资料的时间。

## 前置条件
需要浏览器已启动(OpenClaw 会自动处理,或参考 `/openclaw browser` 命令)。

## 动手步骤

### 第一步:告诉 AI 你想了解什么

> "帮我搜一下最近 AI 编程工具的进展,整理成要点"

> "帮我查一下竞品 XX 最近有什么新功能,做个简单对比"

> "帮我看一下 [某个网址] 这篇文章的主要观点是什么"

### 第二步:AI 会做什么

1. 打开浏览器
2. 访问相关网页(搜索引擎或直接 URL)
3. 阅读页面内容
4. 整理成结构化摘要返回给你

### 示例输出

```
🔍 AI 编程工具最新进展(2026年3月)

**主要工具动态**
- GitHub Copilot:支持多文件重构,新增 PR Review 功能
- Cursor:发布 v0.4,Agent 模式大幅升级
- Windsurf:推出团队协作模式

**趋势观察**
- Agent 模式成标配,从"补全"走向"自主编写"
- 上下文窗口普遍扩展至 200k+
- 各家开始争夺 IDE 深度集成
```

### 延伸玩法
- "帮我监控一下 [某网站] 的价格,每天告诉我"
- "帮我填写这个表单:[URL]"
- "帮我截一张 [某网页] 的截图"
- "帮我登录 [某平台] 查看我的账单"(需要你先配置账号)

## ✅ 完成标志
AI 使用了浏览器工具并返回了从网页收集的信息。

```

### references/task-07-ppt.md

```markdown
# 任务 7:让 AI 做一张 PPT

## 目标
让 AI 生成一个真实可打开的 PPTX 文件。

## 为什么重要
PPT 是职场最常用的文件格式之一。AI 可以帮你从零构建演示文稿的结构和内容,省去排版的痛苦。

## 动手步骤

### 第一步:告诉 AI 主题和需求

> "帮我做一个 5 页的 PPT,主题是'2026年Q1工作总结'"

> "帮我做一个产品介绍 PPT,产品叫 XX,主要功能是:……"

> "帮我把这篇文章做成 PPT:[粘贴文章内容]"

### 第二步:AI 会做什么

1. 规划 PPT 结构(封面、目录、内容页、总结)
2. 为每页生成内容要点
3. 使用 `python-pptx` 库生成 `.pptx` 文件
4. 保存到 workspace 文件夹

### 示例 PPT 结构

```
第1页:封面 - 2026年Q1工作总结
第2页:目录
第3页:业务成果(数据、亮点)
第4页:遇到的挑战与解决方案
第5页:Q2 展望与计划
```

### 注意
- 生成的 PPT 是标准 .pptx 格式,可用 PowerPoint / Keynote / WPS 打开
- 样式偏简洁,适合进一步在 PPT 软件中美化
- 如需特定模板风格,可提前告知

### 延伸玩法
- "帮我在第 3 页加一个数据图表"
- "帮我把 PPT 内容翻译成英文版"
- "帮我根据这份 PPT 写一个演讲稿"
- "帮我把这个 Excel 数据做成 PPT"

## ✅ 完成标志
workspace 中存在 `.pptx` 文件。

```

### references/task-08-skill.md

```markdown
# 任务 8:安装一个新 Skill

## 目标
从 ClawHub 社区找到一个有用的 Skill,安装并成功使用它。

## 为什么重要
Skill 是 OpenClaw 的扩展系统。社区里有大量现成的 Skill,安装后 AI 就获得了新能力。学会用 ClawHub,相当于解锁了 OpenClaw 的完整生态。

## 动手步骤

### 第一步:让 AI 帮你找 Skill

> "帮我从 ClawHub 找一个有用的 Skill 并安装"

或者你有特定需求:

> "ClawHub 上有没有处理 PDF 的 Skill?"

> "帮我找一个能发邮件的 Skill"

### 第二步:AI 会做什么

1. 搜索 ClawHub:`clawhub search <关键词>`
2. 展示搜索结果,推荐适合的 Skill
3. 安装你选择的 Skill:`clawhub install <skill-name>`
4. 确认安装成功

### 第三步:使用刚安装的 Skill

安装后直接使用,AI 会自动识别并调用。

### 示例对话

```
你:帮我从 ClawHub 找一个有用的 Skill 安装
AI:我来搜索一下...

找到以下 Skill:
1. pdf-reader - 读取和分析 PDF 文件
2. gmail-helper - Gmail 邮件管理
3. notion-sync - 同步到 Notion

推荐安装 pdf-reader,你有 PDF 处理需求吗?

你:好的,安装它
AI:正在安装 pdf-reader...
✅ 安装成功!现在你可以说「帮我读取这个 PDF」来使用它了。
```

### 手动安装方式

如果你知道 Skill 名字,也可以直接说:

> "安装 ClawHub 上的 pdf-reader skill"

AI 会执行:
```bash
clawhub install pdf-reader
```

## ✅ 完成标志
`~/.openclaw/skills/` 目录中存在新安装的 Skill 文件夹。

```

### scripts/setup_reminder_cron.js

```javascript
#!/usr/bin/env node
/**
 * setup_reminder_cron.js - Set up quickstart cron jobs via OpenClaw CLI
 *
 * Creates two cron jobs:
 *   1. quickstart-heartbeat  — every 30 min: scan progress, auto-mark done, cleanup if all done
 *   2. quickstart-reminder   — daily at 9 AM: remind user of pending tasks
 *
 * Usage:
 *   node setup_reminder_cron.js [--hour 9] [--minute 0]
 */

const { spawnSync } = require('child_process');
const path = require('path');

// Parse args
const args = process.argv.slice(2);
function getArg(name, def) {
  const idx = args.indexOf(name);
  return idx !== -1 ? parseInt(args[idx + 1], 10) : def;
}
const hour   = getArg('--hour', 9);
const minute = getArg('--minute', 0);

// Resolve scripts dir (same folder as this file)
const scriptsDir = path.resolve(__dirname);

// ── Cron definitions ──────────────────────────────────────────────────────────
const CRONS = [
  {
    name:     'quickstart-heartbeat',
    schedule: '*/30 * * * *',   // every 30 minutes
    task: [
      `Run: node ${scriptsDir}/check_progress.js --mark-done`,
      'Parse the JSON output.',
      'If all_done is true: run cleanup_crons.js, then send the user a 🎓 graduation congratulations message.',
      'If newly_completed has entries: send a brief "✅ TaskN marked complete!" notification.',
      'Otherwise: do nothing (stay silent).',
    ].join(' '),
  },
  {
    name:     'quickstart-reminder',
    schedule: `${minute} ${hour} * * *`,   // daily at specified time
    task: [
      `Run: node ${scriptsDir}/check_progress.js`,
      'Parse the JSON output.',
      'If all_done is true: run cleanup_crons.js, send 🎓 graduation message, stop.',
      'Otherwise: send the user a friendly reminder listing completed (✅) and pending (⬜) tasks,',
      'highlight the next_task, and encourage them to complete it today.',
    ].join(' '),
  },
];

// ── Register crons ────────────────────────────────────────────────────────────
let allOk = true;

for (const cron of CRONS) {
  console.log(`\nSetting up cron "${cron.name}" [${cron.schedule}]...`);
  const cmd    = ['openclaw', 'cron', 'add', '--name', cron.name, '--cron', cron.schedule, '--message', cron.task];
  const result = spawnSync(cmd[0], cmd.slice(1), { encoding: 'utf8' });

  if (result.error?.code === 'ENOENT') {
    console.error(`  ⚠️  'openclaw' CLI not found in PATH.`);
    allOk = false;
  } else if (result.status === 0) {
    console.log(`  ✅ Created "${cron.name}"`);
    if (result.stdout) console.log(' ', result.stdout.trim());
  } else {
    console.warn(`  ⚠️  Exit code ${result.status}`);
    if (result.stdout) console.log('  stdout:', result.stdout.trim());
    if (result.stderr) console.error('  stderr:', result.stderr.trim());
    allOk = false;
  }
}

console.log('\n' + (allOk
  ? `✅ All crons registered!\n   Heartbeat: every 30 min (auto-detect & mark done)\n   Reminder:  daily at ${String(hour).padStart(2,'0')}:${String(minute).padStart(2,'0')} (push notification)`
  : `⚠️  Some crons failed. Add them manually via: openclaw cron add ...`
));

```

openclaw-quickstart | SkillHub