JustToThePoint English Website Version
JustToThePoint en español
Skip to main content

OpenClaw: A Practical Guide to Running a Local Autonomous AI Agent — Part 5: Building a Custom Skill — mac-setup-master

Nine people can’t make a baby in a month, (regarding the addition of more programmers to get a project completed faster)– Fred Brooks

OpenClaw

mac-setup-master

Step 1. Directory Layout.

mac-setup-master/
├── SKILL.md
├── requirements.txt                       # required for `uv pip install -r`
├── scripts/
│   └── mysystem.py
├── references/                            # loaded on demand (large / situational)
│   ├── troubleshooting.md                 # fallbacks when files are missing
│   └── glossary.md                        # Zsh / AeroSpace / Espanso / Stow terms
└── assets/                                # loaded on demand 
    ├── zsh-reference.md                   
[...]

Step 2. SKILL.md

---
name: mac-setup-master
description: Explains this Mac's setup, shell shortcuts and functions, AeroSpace keybindings, Espanso text expansions, Raycast commands, and Spotlight behaviour using the user's local configuration files (with GNU Stow symlink awareness).
Trigger: User asks about their macOS setup, shortcuts, keybindings, or configuration. Typical triggers are "How is my Mac configured?", "Show me my AeroSpace keybindings", "What shortcuts do I have?", "Check my Espanso setup", "Review my Zsh aliases and functions", "Run system overview".
compatibility: macOS 13+ (Ventura), Python 3.11+, zsh 5.8+
Last modified: 2026-11-12
---

 Mac Setup Guide & Diagnostics

Help the user understand **their own** macOS setup by running read-only
diagnostic scripts and inspecting local dotfiles. Prefer exact data extracted
via `scripts/mysystem.py` or a terminal command over assumptions or generic
macOS defaults.

> ⚠️ **Read-only contract.** This skill explains configuration. It **never**
> executes shortcuts, never runs structural scripts (`aerospace.toml`,
> `.zshrc`), and never modifies dotfiles. If a user asks "run my AeroSpace
> reload", explain the command instead.

## 0. Quick start — choosing the right path

| User asks… | Do this | Output |
|---|---|---|
| "System overview" / "How is my Mac set up?" | `python scripts/mysystem.py info` | Table of OS, arch, RAM, disk, versions |
| "Show my AeroSpace keybindings" | `python scripts/mysystem.py aerospace` | Grouped by mode |
| "What Espanso triggers do I have?" | `python scripts/mysystem.py espanso` | Trigger → replacement list |
| "List my OpenClaw skills" | `python scripts/mysystem.py skills` | Name + description + path |
| "What aliases do I have?" | `zsh -ic "alias"` | Live alias dump |
| "Show me my `my_func` function" | `zsh -ic "which my_func"` | Function source |
| "What does Cmd+Enter do?" | Read `~/macOS/scripts/help.sh`, `sec_keyboard` | Shortcut → action, with `file#line` |
| "Where is `aerospace.toml`?" | Resolve via `readlink -f` (Stow-aware) | Real path under `~/macOS` |

If the user's question is ambiguous, **prefer `mysystem.py all`** and then
summarize only the relevant section.

## 1. Prerequisites & Installation

`scripts/mysystem.py` needs **Python 3.11+** (for `tomllib`) and `PyYAML`.

```bash
cd ~/macOS/claude/skills/mac-setup-master
python3 -m venv .venv && source .venv/bin/activate
uv pip install -r requirements.txt
```

`requirements.txt` contains:

```text
PyYAML>=6.0
```

**Fallback if the venv is unavailable:** `python3 -m pip install --user pyyaml`
and confirm Python ≥ 3.11 via `python3 -V`. If the machine only has 3.9/3.10,
`mysystem.py` will exit with a clear message — surface that to the user
verbatim instead of guessing.


## 2. System diagnostics — `scripts/mysystem.py`

A single-file Python helper. Run it via the code interpreter or as a
subprocess. It is read-only.

### Available functions

| Function | Returns | Notes |
|---|---|---|
| `get_sys_info()` | `dict` (and prints) | OS, arch, RAM, free disk, Zsh & AeroSpace versions. Uses `subprocess` with `check=False` — a missing tool becomes `"not installed"`, not a crash. |
| `parse_aerospace()` | `dict` of mode → bindings | Reads `~/.config/aerospace/aerospace.toml` via `tomllib`. |
| `parse_espanso()` | `list[dict]` | Recursively scans `$ESPANSO_MATCH_DIR` (default `~/Library/Application Support/espanso/match`) for `*.yml`/`*.yaml`. Handles `form`, `vars`, `image_path` (non-text) matches. |
| `parse_skills(root=...)` | `list[dict]` | Lists OpenClaw skills by parsing YAML frontmatter in every `SKILL.md` under `~/macOS/claude/skills`. |
| `parse_help_sections()` | `dict` of section → text | Extracts `sec_spotlight`, `sec_raycast`, `sec_keyboard` from `~/macOS/scripts/help.sh`. |
| `get_zsh_aliases()` | `dict` | Parses `zsh -ic "alias"` output. |
| `get_zsh_functions()` | `list[str]` | Lists non-underscore function names from `~/.zsh_functions`. |

### CLI

```bash
python scripts/mysystem.py            # = all
python scripts/mysystem.py info
python scripts/mysystem.py aerospace
python scripts/mysystem.py espanso
python scripts/mysystem.py skills
python scripts/mysystem.py help
python scripts/mysystem.py zsh
python scripts/mysystem.py all --json # machine-readable
```

**Agent action:** summarize the output logically — group AeroSpace bindings by
mode (e.g. *Window Management*, *Focus*, *Workspaces*), translate AeroSpace
commands into plain English ("moves the focused window to the left half of
the screen") without executing them.

## 3. Configuration sources & GNU Stow awareness

The user manages dotfiles with **GNU Stow**. Files in standard paths are
often symlinks into `~/macOS`. Always resolve the real path before citing
`file#line` — otherwise the tag points at a symlink, not the source of truth.

| What | Standard path | Stow target (real) |
|---|---|---|
| Shell info | `~/macOS/scripts/help.sh` | (already in repo) |
| AeroSpace | `~/.config/aerospace/aerospace.toml` | `~/macOS/aerospace/aerospace.toml` |
| Zsh | `~/.zshrc`, `~/.zsh_aliases`, `~/.zsh_functions` | `~/macOS/zsh/...` |
| Espanso | `$ESPANSO_MATCH_DIR` (default `~/Library/Application Support/espanso/match`) | `~/macOS/Espanso/` |
| Raycast / Spotlight / Keyboard | `~/macOS/scripts/help.sh` (`sec_raycast`, `sec_spotlight`, `sec_keyboard`) | — |

**Resolve with:** `readlink -f ~/.config/aerospace/aerospace.toml` (macOS 13+
ships BSD `readlink`; use `python3 -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' `
for portability).

## 4. Zsh aliases and functions

Do **not** manually parse `.zshrc` — aliases and functions are often loaded
through conditionals, `source` chains, and plugin managers, so static parsing
lies. Dump the live environment instead.

```bash
# All active aliases (interactive, non-login shell)
zsh -ic "alias"

# Function names defined in ~/.zsh_functions (excludes private _* helpers)
zsh -f -c 'source ~/.zsh_functions; print -l ${(k)functions:#_*}'

# Source + preceding comment for a specific function
grep -n -B 6 'my_function' ~/.zsh_functions

# Full source of any function (works for plugin-defined ones too)
zsh -ic "which my_function"
```

**Agent action:** describe the alias/function in plain language. If the user
asks for the source, quote `zsh -ic "which <name>"` output verbatim.

## 5. Spotlight, Raycast, and keyboard shortcuts

Read `~/macOS/scripts/help.sh` directly and extract the relevant section:

| Section | Contains |
|---|---|
| `sec_spotlight` | Spotlight shortcut, enabled categories |
| `sec_raycast` | Raycast hotkeys, hotkey conflicts, extensions |
| `sec_keyboard` | macOS key remaps, custom shortcuts, text replacements |

**Rules:**
- Read the file — do not `bash` it.
- Note invocation shortcuts explicitly (`Cmd+Space` for Spotlight,
  `Option+Space` for Raycast, etc.).
- Clearly attribute a capability to **Raycast** vs. **Spotlight** so the user
  knows which app to open.

## 6. Response style & formatting

### Tag every claim with its source

- **[Configured]** — statically defined in a dotfile (e.g. `aerospace.toml`).
- **[Detected]** — live data from a script or shell check (RAM, Zsh version).
- **[Unknown]** — data that could not be resolved (file missing, command
  absent, parse error).

### Traceability

Append the **real** (Stow-resolved) path and line number as a subdued tag:

> `Cmd+Enter` → opens Terminal — **[Configured]** (`~/macOS/aerospace/aerospace.toml#L45`)

### Format

- Short, distinct lists.
- Group related items (AeroSpace by mode, Espanso by file, aliases by
  prefix).
- Bold the user-facing shortcut; plain-text the action.

### Execution rule

Explain commands, shortcuts, and scripts. **NEVER** execute a shortcut or a
structural script (AeroSpace reload, Espanso restart, `zsh` re-source) during
a configuration review.

## 7. When to use reference files

| File |	Open when… |
| references/glossary.md |	User uses unfamiliar terms ("what's a Stow package?") |
| references/troubleshooting.md |	A command failed, a file is missing, or output is empty |

> 📖 **Official documentation:** every tool's official docs are collected in
> `references/glossary.md` — the "Official documentation" table at the end,
> plus a link under each term. When the user needs depth beyond this skill's
> scope, send them there instead of expanding the answer inline.

## 8. Examples

**Q: "Show me my AeroSpace keybindings."**

```bash
python scripts/mysystem.py aerospace
```

> ### AeroSpace keybindings
>
> **Mode `main`** — *Window Management* [Configured]
> - `alt-h` → focus left (`~/macOS/aerospace/aerospace.toml#L12`)
> - `alt-shift-h` → move window to left half (`…#L18`)
> - `alt-1` → workspace 1 (`…#L30`)
>
> **Mode `service`** — *System* [Configured]
> - `esc` → back to `main` (`…#L60`)
>
> ℹ️ AeroSpace v0.15.1 [Detected]

**Q: "What does `Cmd+Space` do on my Mac?"**

> `Cmd+Space` opens **Spotlight** (not Raycast) — [Configured]
> (`~/macOS/scripts/help.sh`, `sec_spotlight`, line 42).
> Raycast is bound to `Option+Space` instead.

## 9. Troubleshooting (short list — full list in `references/troubleshooting.md`)

| Symptom | Likely cause | Next step |
|---|---|---|
| `mysystem.py` exits with "Python 3.11+" | System Python is 3.9/3.10 | Ask user to `brew install python@3.12` or activate `.venv` |
| `AeroSpace config not found` | Symlink broken or file absent | `readlink -f ~/.config/aerospace/aerospace.toml` |
| Empty AeroSpace output | TOML syntax error | Surface the `tomllib.TOMLDecodeError` line number |
| Espanso dir not found | `$ESPANSO_MATCH_DIR` unset and default path missing | Check `~/macOS/Espanso` exists; ask user |
| `zsh: command not found: alias` | Non-interactive shell | Re-run with `-i` flag |
| `skills` list empty | `~/macOS/claude/skills` missing | Ask user to confirm the OpenClaw skills root |

## 10. Security & privacy

- All scripts in `scripts/` are **read-only** by design. If you need to add
  one, document the "no writes, no network, no execution of user config"
  contract at the top of the file.
- Never print full file contents unless the user asked for source. Prefer
  excerpts + line references.
- Never send dotfile contents to external services.
- Do not echo `zsh -ic` output containing secrets (tokens, API keys in
  env vars). Redact with `***` and note the redaction.
```

# 3. `requirements.txt`

```text
# mac-setup-master skill dependencies
PyYAML>=6.0,<7.0
```

# 4. Improved `scripts/mysystem.py`

```python
#!/usr/bin/env python3
"""mac-setup-master diagnostic helpers.

READ-ONLY. Never writes to disk, never executes the user's shell config,
never runs shortcuts. Safe to import from the Claude code interpreter.

Requires Python 3.11+ (stdlib ``tomllib``) and PyYAML.

CLI:
    python scripts/mysystem.py [info|aerospace|espanso|skills|help|zsh|all]
                               [--json] [--help]

Import:
    from mysystem import (
        get_sys_info, parse_aerospace, parse_espanso, parse_skills,
        parse_help_sections, get_zsh_aliases, get_zsh_functions,
    )
"""
from __future__ import annotations

import argparse
import json
import os
import re
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Any

try:
    import tomllib  # type: ignore[import-not-found]
except ModuleNotFoundError:  # pragma: no cover
    sys.exit("mysystem.py requires Python 3.11+ (stdlib tomllib). Run `python3 -V`.")

import yaml  # PyYAML


# --------------------------------------------------------------------------- #
# Helpers
# --------------------------------------------------------------------------- #
def _safe_version(cmd: str) -> str:
    """Return --version output, or 'not installed' if the binary is missing."""
    if shutil.which(cmd.split()[0]) is None:
        return "not installed"
    try:
        out = subprocess.run(
            cmd, shell=True, capture_output=True, text=True, timeout=5, check=False
        ).stdout.strip()
    except subprocess.TimeoutExpired:
        return "timed out"
    return out or "unknown"


def _emit(payload: Any, as_json: bool, pretty: bool = True) -> None:
    if as_json:
        print(json.dumps(payload, indent=2, ensure_ascii=False))
    elif pretty:
        # Human-readable handled by callers; this is just a fallback.
        print(payload)


# --------------------------------------------------------------------------- #
# System info
# --------------------------------------------------------------------------- #
def get_sys_info(as_json: bool = False) -> dict[str, str]:
    """Return read-only system metrics. Also prints unless as_json."""
    try:
        ram_bytes = int(subprocess.getoutput("sysctl -n hw.memsize"))
        ram = f"{ram_bytes / (1024 ** 3):.2f} GB"
    except ValueError:
        ram = "unknown"

    info: dict[str, str] = {
        "OS_Version": _safe_version("sw_vers -productVersion"),
        "Architecture": _safe_version("uname -m"),
        "RAM": ram,
        "Disk_Free": f"{shutil.disk_usage('/').free / (1024 ** 3):.2f} GB",
        "Zsh_Version": _safe_version("zsh --version"),
        "AeroSpace": _safe_version("aerospace --version"),
    }

    if as_json:
        return info
    for key, value in info.items():
        print(f"{key}: {value}")
    return info


# --------------------------------------------------------------------------- #
# AeroSpace
# --------------------------------------------------------------------------- #
def parse_aerospace(as_json: bool = False) -> dict[str, dict[str, str]]:
    """Parse ~/.config/aerospace/aerospace.toml -> {mode: {key: action}}."""
    path = Path("~/.config/aerospace/aerospace.toml").expanduser()
    if not path.exists():
        msg = f"AeroSpace config not found at {path}"
        print(msg, file=sys.stderr)
        return {}

    try:
        with path.open("rb") as fh:
            config = tomllib.load(fh)
    except tomllib.TOMLDecodeError as exc:
        print(f"Invalid TOML in {path}: {exc}", file=sys.stderr)
        return {}

    bindings: dict[str, dict[str, str]] = {}
    for mode, body in config.get("mode", {}).items():
        bindings[mode] = dict(body.get("binding", {}))

    if as_json:
        return bindings

    print("--- AeroSpace Keybindings ---")
    for mode, keys in bindings.items():
        print(f"\nMode: [{mode}]")
        for key, action in keys.items():
            print(f"  {key.ljust(18)} -> {action}")
    if not bindings:
        print("(no bindings found)")
    return bindings


# --------------------------------------------------------------------------- #
# Espanso
# --------------------------------------------------------------------------- #
def parse_espanso(as_json: bool = False) -> list[dict[str, str]]:
    """Parse $ESPANSO_MATCH_DIR (default Espanso match dir) for triggers."""
    match_dir = Path(
        os.environ.get(
            "ESPANSO_MATCH_DIR",
            os.path.expanduser("~/Library/Application Support/espanso/match"),
        )
    ).expanduser()

    if not match_dir.exists():
        print(f"Espanso match directory not found: {match_dir}", file=sys.stderr)
        return []

    results: list[dict[str, str]] = []
    for path in sorted(match_dir.rglob("*")):
        if path.suffix.lower() not in {".yml", ".yaml"}:
            continue
        try:
            data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
        except (OSError, yaml.YAMLError) as exc:
            print(f"Could not read {path}: {exc}", file=sys.stderr)
            continue

        for match in data.get("matches", []):
            if not isinstance(match, dict):
                continue
            triggers = match.get("triggers", match.get("trigger", []))
            if isinstance(triggers, str):
                triggers = [triggers]

            replacement = match.get("replace")
            if replacement is None:
                kind = (
                    "form" if "form" in match
                    else "vars" if "vars" in match
                    else "image" if "image_path" in match
                    else "dynamic"
                )
                replacement = f"<{kind} match>"

            for trigger in triggers:
                results.append({
                    "file": path.name,
                    "trigger": str(trigger),
                    "replacement": str(replacement),
                })

    if as_json:
        return results
    for row in results:
        print(f"{row['file']}: {row['trigger']!r} -> {row['replacement']!r}")
    return results


# --------------------------------------------------------------------------- #
# OpenClaw skills
# --------------------------------------------------------------------------- #
_FRONTMATTER_RE = re.compile(r"\A---\s*\n(.*?)\n---(?:\s|\Z)", re.S)


def search_skills(
    query: str | None = None,
    root: str = "~/macOS/claude/skills",
) -> list[dict[str, str]]:
    """Return OpenClaw skills whose name/description match *query* (case-insensitive)."""
    root_path = Path(root).expanduser()
    if not root_path.exists():
        print(f"Skills root not found: {root_path}", file=sys.stderr)
        return []

    q = query.casefold() if query else None
    results: list[dict[str, str]] = []

    for skill_file in root_path.rglob("SKILL.md"):
        text = skill_file.read_text(encoding="utf-8")
        match = _FRONTMATTER_RE.match(text)
        if not match:
            continue
        try:
            meta = yaml.safe_load(match.group(1)) or {}
        except yaml.YAMLError:
            continue

        name = meta.get("name", skill_file.parent.name)
        description = (meta.get("description") or "").strip()
        if q and q not in name.casefold() and q not in description.casefold():
            continue

        results.append({
            "name": name,
            "description": description,
            "path": str(skill_file.parent),
        })
    return results


def parse_skills(as_json: bool = False) -> list[dict[str, str]]:
    skills = search_skills()
    if as_json:
        return skills
    if not skills:
        print("(no skills found under ~/macOS/claude/skills)")
        return skills
    for skill in skills:
        print(f"{skill['name']}: {skill['description']}\n  {skill['path']}")
    return skills


# --------------------------------------------------------------------------- #
# help.sh sections (Spotlight / Raycast / Keyboard)
# --------------------------------------------------------------------------- #
_HELP_SECTION_RE = re.compile(
    r"^sec_(?Pspotlight|raycast|keyboard)\s*\(\)\s*\{(?P.*?)^\}",
    re.S | re.M,
)


def parse_help_sections(as_json: bool = False) -> dict[str, str]:
    """Extract sec_spotlight / sec_raycast / sec_keyboard bodies from help.sh."""
    path = Path("~/macOS/scripts/help.sh").expanduser()
    if not path.exists():
        print(f"help.sh not found at {path}", file=sys.stderr)
        return {}

    text = path.read_text(encoding="utf-8")
    sections = {
        m.group("name"): m.group("body").strip()
        for m in _HELP_SECTION_RE.finditer(text)
    }

    if as_json:
        return sections
    for name, body in sections.items():
        print(f"\n=== sec_{name} ===\n{body}")
    return sections


# --------------------------------------------------------------------------- #
# Zsh helpers
# --------------------------------------------------------------------------- #
def get_zsh_aliases(as_json: bool = False) -> dict[str, str]:
    """Dump active aliases via an interactive, non-login shell."""
    out = subprocess.run(
        ["zsh", "-ic", "alias"], capture_output=True, text=True, check=False
    ).stdout
    aliases: dict[str, str] = {}
    for line in out.splitlines():
        # Format: name='value'  or  name=value
        match = re.match(r"^(\S+)=(?:'([^']*)'|\"([^\"]*)\"|(.*))$", line.strip())
        if not match:
            continue
        name = match.group(1)
        value = next(g for g in match.groups()[1:] if g is not None)
        aliases[name] = value
    if as_json:
        return aliases
    for name, value in aliases.items():
        print(f"{name}={value}")
    return aliases


def get_zsh_functions(as_json: bool = False) -> list[str]:
    """List public (non-underscore) function names from ~/.zsh_functions."""
    out = subprocess.run(
        ["zsh", "-f", "-c",
         "source ~/.zsh_functions; print -l ${(k)functions:#_*}"],
        capture_output=True, text=True, check=False,
    ).stdout
    names = sorted({line.strip() for line in out.splitlines() if line.strip()})
    if as_json:
        return names
    for name in names:
        print(name)
    return names


# --------------------------------------------------------------------------- #
# CLI
# --------------------------------------------------------------------------- #
_COMMANDS = {
    "info": get_sys_info,
    "aerospace": parse_aerospace,
    "espanso": parse_espanso,
    "skills": parse_skills,
    "help": parse_help_sections,
    "zsh": lambda as_json=False: {
        "aliases": get_zsh_aliases(as_json=True) if as_json else None,
        "functions": get_zsh_functions(as_json=True) if as_json else None,
    },
    "all": None,  # special-cased
}


def _run_all(as_json: bool) -> None:
    if as_json:
        print(json.dumps({
            "info": get_sys_info(as_json=True),
            "aerospace": parse_aerospace(as_json=True),
            "espanso": parse_espanso(as_json=True),
            "skills": parse_skills(as_json=True),
            "help_sections": parse_help_sections(as_json=True),
            "zsh_aliases": get_zsh_aliases(as_json=True),
            "zsh_functions": get_zsh_functions(as_json=True),
        }, indent=2, ensure_ascii=False))
        return
    get_sys_info()
    parse_aerospace()
    parse_espanso()
    parse_skills()
    parse_help_sections()
    print("\n--- Zsh aliases ---")
    get_zsh_aliases()
    print("\n--- Zsh functions ---")
    get_zsh_functions()


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(
        prog="mysystem.py",
        description="Read-only diagnostics for the mac-setup-master skill.",
    )
    parser.add_argument(
        "command",
        nargs="?",
        default="all",
        choices=sorted(_COMMANDS),
        help="section to run (default: all)",
    )
    parser.add_argument("--json", action="store_true", help="machine-readable output")
    args = parser.parse_args(argv)

    if args.command == "all":
        _run_all(args.json)
        return 0

    fn = _COMMANDS[args.command]
    if args.command == "zsh":
        if args.json:
            print(json.dumps({
                "aliases": get_zsh_aliases(as_json=True),
                "functions": get_zsh_functions(as_json=True),
            }, indent=2, ensure_ascii=False))
        else:
            print("--- Zsh aliases ---"); get_zsh_aliases()
            print("\n--- Zsh functions ---"); get_zsh_functions()
        return 0
    fn(as_json=args.json)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

6. Suggested references/ files

references/troubleshooting.md

# Troubleshooting — mac-setup-master

Indexed by symptom. Each entry follows **Symptom → Cause → Fix → Verify**.
Official documentation links are collected at the bottom of this file.

---

## 1. `mysystem.py` won't start

### 1.1 `No module named 'tomllib'`

| | |
|---|---|
| **Symptom** | Running `python scripts/mysystem.py` raises `ModuleNotFoundError: No module named 'tomllib'` |
| **Cause** | macOS ships `/usr/bin/python3` at **3.9**. `tomllib` only entered the standard library in **3.11**, so even if `python3 -V` reports 3.9, `tomllib` is absent. |
| **Fix** | Activate the skill's `.venv` (recommended), or install 3.11+: `brew install python@3.12` |
| **Verify** | `python3 -c "import tomllib; print('ok')"` prints `ok` |
[...]

references/glossary.md

# Glossary — mac-setup-master

Grouped by category. Every entry gives a **one-line definition** + **where/how it's used in this skill** + **official docs link**.

---

## Shell / Zsh

**Zsh**
: The UNIX command interpreter; the default interactive shell on macOS since 10.15. This skill reads aliases and functions via `zsh -ic` and `zsh -f -c` rather than statically parsing `.zshrc`.
: 📖 [zsh.sourceforge.io/Doc](https://zsh.sourceforge.io/Doc/)
[...]
Bitcoin donation
Children reading and learning

JustToThePoint Copyright © 2011 - 2026 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. About us., Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.