← Back to Skills Marketplace
trongnguyen29

focusnoteapp

by trongnguyen29 · GitHub ↗ · v1.0.0
cross-platform ✓ Security Clean
957
Downloads
0
Stars
0
Active Installs
1
Versions
Install in OpenClaw
/install focusnoteapp
Description
Add text to today's daily note in FocusNote as a new bullet point
README (SKILL.md)

FocusNote: Add to Daily Note

This skill adds user-provided text to today's daily note in FocusNote as a new bullet point.

How It Works

  1. Reads the FocusNote documents path from ~/.lucia/documents-path.txt
  2. Generates today's date in YYYY-MM-DD format
  3. Locates or creates today's daily note
  4. Adds the user's text as a new bullet point
  5. Updates the document's JSON structure files

Prerequisites

  • FocusNote app must be running (it creates ~/.lucia/documents-path.txt on startup)
  • Node.js installed for running the helper script

Implementation

When the user asks to add text to their daily note, follow these steps:

Step 1: Read Documents Path

const fs = require("fs");
const path = require("path");
const os = require("os");

// Read the documents path from FocusNote's config file
const focusnoteConfigPath = path.join(
  os.homedir(),
  ".lucia",
  "documents-path.txt",
);
const documentsPath = fs.readFileSync(focusnoteConfigPath, "utf-8").trim();

Step 2: Generate Today's Date

const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0");
const day = String(today.getDate()).padStart(2, "0");
const todayDocName = `${year}-${month}-${day}`;

Step 3: Locate Daily Note Folder

const dailyNotePath = path.join(documentsPath, "notes", todayDocName);
const structurePath = path.join(dailyNotePath, "_structure.json");
const metadataPath = path.join(dailyNotePath, "_metadata.json");
const nodesDir = path.join(dailyNotePath, ".nodes");

Step 4: Create Daily Note If It Doesn't Exist

if (!fs.existsSync(dailyNotePath)) {
  // Create directory structure
  fs.mkdirSync(dailyNotePath, { recursive: true });
  fs.mkdirSync(nodesDir, { recursive: true });

  // Create metadata
  const metadata = {
    name: todayDocName,
    createdAt: Date.now(),
    updatedAt: Date.now(),
  };
  fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));

  // Create empty structure
  const structure = {
    rootNodeIds: [],
    deletedNodeIds: [],
    nodes: {},
  };
  fs.writeFileSync(structurePath, JSON.stringify(structure, null, 2));
}

Step 5: Create New Bullet Node

const { v4: uuidv4 } = require("uuid"); // npm install uuid

// Generate unique node ID
const nodeId = uuidv4();
const timestamp = Date.now();

// Create Lexical bullet structure
const lexicalContent = {
  root: {
    children: [
      {
        children: [
          {
            children: [
              {
                detail: 0,
                format: 0,
                mode: "normal",
                style: "",
                text: userText, // The text from the user
                type: "text",
                version: 1,
              },
            ],
            direction: "ltr",
            format: "",
            indent: 0,
            type: "listitem",
            version: 1,
            value: 1,
          },
        ],
        direction: "ltr",
        format: "",
        indent: 0,
        type: "list",
        version: 1,
        listType: "bullet",
        start: 1,
        tag: "ul",
      },
    ],
    direction: "ltr",
    format: "",
    indent: 0,
    type: "root",
    version: 1,
  },
};

// Create node object
const newNode = {
  id: nodeId,
  content: JSON.stringify(lexicalContent),
  isFolded: false,
  isTodo: false,
  isDone: false,
  isInProgress: false,
  isBlurred: false,
  backgroundColor: null,
  createdAt: timestamp,
  updatedAt: timestamp,
};

Step 6: Save Node to Sharded Directory

// Shard by first 2 characters of node ID
const shard = nodeId.substring(0, 2);
const shardDir = path.join(nodesDir, shard);

if (!fs.existsSync(shardDir)) {
  fs.mkdirSync(shardDir, { recursive: true });
}

const nodeFilePath = path.join(shardDir, `node-${nodeId}.json`);
fs.writeFileSync(nodeFilePath, JSON.stringify(newNode, null, 2));

Step 7: Update Structure File

// Read current structure
const structure = JSON.parse(fs.readFileSync(structurePath, "utf-8"));

// Add node to structure
structure.rootNodeIds.push(nodeId);
structure.nodes[nodeId] = {
  parentId: null,
  orderIndex: structure.rootNodeIds.length - 1,
  childIds: [],
};

// Update timestamp
structure.updatedAt = timestamp;

// Save updated structure
fs.writeFileSync(structurePath, JSON.stringify(structure, null, 2));

Complete Script Example

Here's a complete Node.js script you can use:

#!/usr/bin/env node

const fs = require("fs");
const path = require("path");
const os = require("os");
const { v4: uuidv4 } = require("uuid");

function addToDailyNote(userText) {
  try {
    // Step 1: Read documents path
    const focusnoteConfigPath = path.join(
      os.homedir(),
      ".lucia",
      "documents-path.txt",
    );
    if (!fs.existsSync(focusnoteConfigPath)) {
      throw new Error(
        "FocusNote config file not found. Make sure FocusNote is running.",
      );
    }
    const documentsPath = fs.readFileSync(focusnoteConfigPath, "utf-8").trim();

    // Step 2: Generate today's date
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0");
    const day = String(today.getDate()).padStart(2, "0");
    const todayDocName = `${year}-${month}-${day}`;

    // Step 3: Set up paths
    const dailyNotePath = path.join(documentsPath, "notes", todayDocName);
    const structurePath = path.join(dailyNotePath, "_structure.json");
    const metadataPath = path.join(dailyNotePath, "_metadata.json");
    const nodesDir = path.join(dailyNotePath, ".nodes");

    // Step 4: Create daily note if needed
    if (!fs.existsSync(dailyNotePath)) {
      fs.mkdirSync(dailyNotePath, { recursive: true });
      fs.mkdirSync(nodesDir, { recursive: true });

      const metadata = {
        name: todayDocName,
        createdAt: Date.now(),
        updatedAt: Date.now(),
      };
      fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));

      const structure = {
        rootNodeIds: [],
        deletedNodeIds: [],
        nodes: {},
      };
      fs.writeFileSync(structurePath, JSON.stringify(structure, null, 2));
    }

    // Step 5: Create new bullet node
    const nodeId = uuidv4();
    const timestamp = Date.now();

    const lexicalContent = {
      root: {
        children: [
          {
            children: [
              {
                children: [
                  {
                    detail: 0,
                    format: 0,
                    mode: "normal",
                    style: "",
                    text: userText,
                    type: "text",
                    version: 1,
                  },
                ],
                direction: "ltr",
                format: "",
                indent: 0,
                type: "listitem",
                version: 1,
                value: 1,
              },
            ],
            direction: "ltr",
            format: "",
            indent: 0,
            type: "list",
            version: 1,
            listType: "bullet",
            start: 1,
            tag: "ul",
          },
        ],
        direction: "ltr",
        format: "",
        indent: 0,
        type: "root",
        version: 1,
      },
    };

    const newNode = {
      id: nodeId,
      content: JSON.stringify(lexicalContent),
      isFolded: false,
      isTodo: false,
      isDone: false,
      isInProgress: false,
      isBlurred: false,
      backgroundColor: null,
      createdAt: timestamp,
      updatedAt: timestamp,
    };

    // Step 6: Save node file
    const shard = nodeId.substring(0, 2);
    const shardDir = path.join(nodesDir, shard);

    if (!fs.existsSync(shardDir)) {
      fs.mkdirSync(shardDir, { recursive: true });
    }

    const nodeFilePath = path.join(shardDir, `node-${nodeId}.json`);
    fs.writeFileSync(nodeFilePath, JSON.stringify(newNode, null, 2));

    // Step 7: Update structure
    const structure = JSON.parse(fs.readFileSync(structurePath, "utf-8"));
    structure.rootNodeIds.push(nodeId);
    structure.nodes[nodeId] = {
      parentId: null,
      orderIndex: structure.rootNodeIds.length - 1,
      childIds: [],
    };
    structure.updatedAt = timestamp;
    fs.writeFileSync(structurePath, JSON.stringify(structure, null, 2));

    console.log(`✅ Added bullet to ${todayDocName}: "${userText}"`);
    return { success: true, documentName: todayDocName, nodeId };
  } catch (error) {
    console.error("❌ Error adding to daily note:", error.message);
    return { success: false, error: error.message };
  }
}

// Example usage
if (require.main === module) {
  const userText = process.argv.slice(2).join(" ") || "New bullet point";
  addToDailyNote(userText);
}

module.exports = { addToDailyNote };

Usage Examples

User: "Add to my daily note: Finished the OpenClaw skill implementation"

Assistant: I'll add that to your daily note.

# Run the script
node add-to-daily-note.js "Finished the OpenClaw skill implementation"

Output: ✅ Added bullet to 2026-02-11: "Finished the OpenClaw skill implementation"


User: "Add a reminder to call mom tomorrow"

Assistant: I'll add that to today's note.

node add-to-daily-note.js "Reminder to call mom tomorrow"

Installation

  1. Save the script as add-to-daily-note.js in your OpenClaw skills directory
  2. Install dependencies: npm install uuid
  3. Make it executable: chmod +x add-to-daily-note.js

Notes

  • The skill creates a new bullet point each time it's called
  • Bullets are added to the end of the daily note
  • If the daily note doesn't exist, it will be created automatically
  • The FocusNote app must be running for the documents path file to exist
  • Changes are immediately visible when you open/refresh the daily note in FocusNote

Troubleshooting

Error: "FocusNote config file not found"

  • Make sure FocusNote is running
  • Check that ~/.lucia/documents-path.txt exists

Bullets not appearing in FocusNote

  • Try closing and reopening the daily note
  • Check that the node files were created in .nodes/ directory
  • Verify _structure.json was updated correctly
Usage Guidance
This skill appears to do what it says: it will read ~/.lucia/documents-path.txt and then create or modify files inside your FocusNote documents folder to add a bullet point. Before installing or running it, consider: ensure Node.js is available and install the 'uuid' npm package (the skill's metadata doesn't declare this); review the script to confirm you are comfortable with it writing to your notes; back up your FocusNote data if you are concerned about accidental corruption; verify the skill source (there's no homepage or author info); and only grant it access on systems where you trust the agent to edit your local notes.
Capability Analysis
Type: OpenClaw Skill Name: focusnoteapp Version: 1.0.0 The skill is designed to add user-provided text as a bullet point to a daily note within the FocusNote application. All file system operations (reading a config path from `~/.lucia/documents-path.txt`, creating directories, and writing JSON files) are consistent with this stated purpose. User input (`userText`) is safely embedded as a string within a JSON structure, preventing code injection. There is no evidence of data exfiltration, malicious execution, persistence mechanisms, or prompt injection attempts against the AI agent in the `SKILL.md` instructions.
Capability Assessment
Purpose & Capability
The name/description (add text to today's FocusNote daily note) align with the actions described: reading FocusNote's documents-path, locating/creating today's note, and writing a new bullet node.
Instruction Scope
Instructions explicitly read ~/.lucia/documents-path.txt and then create/modify files under the FocusNote documents path (notes/<YYYY-MM-DD>, _structure.json, .nodes/*). Reading and writing those files is necessary for the stated task, but this means the skill will access and modify local note data — users should expect disk writes to their notes.
Install Mechanism
This is an instruction-only skill (no install spec). However the SKILL.md requires Node.js and the npm 'uuid' package; the registry metadata lists no required binaries or install steps — a metadata/instructions mismatch. The lack of an install step means the agent or user must supply Node/npm and install dependencies manually.
Credentials
No environment variables, credentials, or external service tokens are requested. The only data accessed is the FocusNote config file and files inside the user's FocusNote documents path, which is proportional to the stated purpose.
Persistence & Privilege
The skill is not force-included (always:false) and does not request elevated or cross-skill configuration. Autonomous invocation is allowed (platform default) but that is expected for a user-invokable helper.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install focusnoteapp
  3. After installation, invoke the skill by name or use /focusnoteapp
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
FocusNote: Add to Daily Note skill, initial release. - Adds user-provided text as a new bullet point to today's daily note in FocusNote. - Automatically creates today's daily note if it doesn't exist. - Ensures notes are stored and structured in FocusNote's native directory and JSON format. - Requires Node.js and a running FocusNote app for use.
Metadata
Slug focusnoteapp
Version 1.0.0
License
All-time Installs 0
Active Installs 0
Total Versions 1
Frequently Asked Questions

What is focusnoteapp?

Add text to today's daily note in FocusNote as a new bullet point. It is an AI Agent Skill for Claude Code / OpenClaw, with 957 downloads so far.

How do I install focusnoteapp?

Run "/install focusnoteapp" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is focusnoteapp free?

Yes, focusnoteapp is completely free (open-source). You can download, install and use it at no cost.

Which platforms does focusnoteapp support?

focusnoteapp is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created focusnoteapp?

It is built and maintained by trongnguyen29 (@trongnguyen29); the current version is v1.0.0.

💬 Comments