@buda-ai/connector 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +22 -1
  2. package/agent-templates/scripts/skill-scanner.sh +125 -0
  3. package/agent-templates/shared/AGENTS.md +220 -0
  4. package/agent-templates/shared/BOOTSTRAP.md +15 -0
  5. package/agent-templates/shared/HEARTBEAT.md +5 -0
  6. package/agent-templates/shared/IDENTITY.md +29 -0
  7. package/agent-templates/shared/MEMORY.md +19 -0
  8. package/agent-templates/shared/SOUL.md +43 -0
  9. package/agent-templates/shared/TOOLS.md +85 -0
  10. package/agent-templates/shared/USER.md +23 -0
  11. package/agent-templates/system-skills/app-builder/README.md +8 -0
  12. package/agent-templates/system-skills/app-builder/SKILL.md +678 -0
  13. package/agent-templates/system-skills/app-builder/buda.app.schema.json +165 -0
  14. package/agent-templates/system-skills/app-builder/evals/evals.json +23 -0
  15. package/agent-templates/system-skills/app-builder/scripts/check-preview-url.mjs +184 -0
  16. package/agent-templates/system-skills/buda-automations/SKILL.md +175 -0
  17. package/agent-templates/system-skills/buda-automations/scripts/create-automation.sh +226 -0
  18. package/agent-templates/system-skills/buda-automations/scripts/delete-automation.sh +73 -0
  19. package/agent-templates/system-skills/buda-automations/scripts/list-automations.sh +77 -0
  20. package/agent-templates/system-skills/buda-far/SKILL.md +221 -0
  21. package/agent-templates/system-skills/buda-far/far_gen.py +1499 -0
  22. package/agent-templates/system-skills/buda-machine-info/SKILL.md +67 -0
  23. package/agent-templates/system-skills/buda-machine-info/scripts/get-info.sh +25 -0
  24. package/agent-templates/system-skills/buda-machine-info/scripts/send-file.mjs +62 -0
  25. package/agent-templates/system-skills/buda-web-preview/SKILL.md +462 -0
  26. package/agent-templates/system-skills/find-skills/SKILL.md +142 -0
  27. package/agent-templates/system-skills/skill-creator/LICENSE.txt +202 -0
  28. package/agent-templates/system-skills/skill-creator/SKILL.md +479 -0
  29. package/agent-templates/system-skills/skill-creator/agents/analyzer.md +274 -0
  30. package/agent-templates/system-skills/skill-creator/agents/comparator.md +202 -0
  31. package/agent-templates/system-skills/skill-creator/agents/grader.md +223 -0
  32. package/agent-templates/system-skills/skill-creator/assets/eval_review.html +146 -0
  33. package/agent-templates/system-skills/skill-creator/eval-viewer/generate_review.py +471 -0
  34. package/agent-templates/system-skills/skill-creator/eval-viewer/viewer.html +1325 -0
  35. package/agent-templates/system-skills/skill-creator/references/schemas.md +430 -0
  36. package/agent-templates/system-skills/skill-creator/scripts/__init__.py +0 -0
  37. package/agent-templates/system-skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  38. package/agent-templates/system-skills/skill-creator/scripts/generate_report.py +326 -0
  39. package/agent-templates/system-skills/skill-creator/scripts/improve_description.py +248 -0
  40. package/agent-templates/system-skills/skill-creator/scripts/package_skill.py +136 -0
  41. package/agent-templates/system-skills/skill-creator/scripts/quick_validate.py +103 -0
  42. package/agent-templates/system-skills/skill-creator/scripts/run_eval.py +310 -0
  43. package/agent-templates/system-skills/skill-creator/scripts/run_loop.py +332 -0
  44. package/agent-templates/system-skills/skill-creator/scripts/utils.py +47 -0
  45. package/dist/cli.js +2747 -180
  46. package/package.json +10 -6
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env bash
2
+ # Create a new scheduled automation in the current agent's workspace.
3
+ #
4
+ # Usage:
5
+ # bash create-automation.sh \
6
+ # --name "Task Name" \
7
+ # --prompt "What the agent should do" \
8
+ # --schedule-type interval \
9
+ # --interval-minutes 60
10
+ #
11
+ # bash create-automation.sh \
12
+ # --name "Task Name" \
13
+ # --prompt "What the agent should do" \
14
+ # --schedule-type cron \
15
+ # --cron "0 9 * * 1-5"
16
+ #
17
+ # bash create-automation.sh \
18
+ # --name "Task Name" \
19
+ # --prompt "What the agent should do" \
20
+ # --schedule-type once \
21
+ # --scheduled-at "2026-03-25T10:00:00.000Z"
22
+ #
23
+ # Options:
24
+ # --name Required. Automation display name.
25
+ # --prompt Required. The prompt the agent receives when triggered.
26
+ # --schedule-type Required. One of: interval, cron, once.
27
+ # --interval-minutes Required for interval type.
28
+ # --cron Required for cron type. Cron expression string.
29
+ # --scheduled-at Required for once type. ISO 8601 datetime.
30
+ # --max-runs Optional. Maximum number of executions.
31
+ # --disabled Optional flag. Create in disabled state.
32
+ # --delivery-mode Optional. One of: session, notify (default: session).
33
+ # --channel-id Optional. Target channel ID for notify mode.
34
+ # --timezone Optional. IANA timezone for cron schedules (e.g. Asia/Shanghai, America/New_York).
35
+ # The cron time is interpreted in this local timezone.
36
+ #
37
+ # Requires: AGENT_KEY, NEXT_PUBLIC_APP_URL (auto-injected in Buda sandbox)
38
+
39
+ set -euo pipefail
40
+
41
+ NAME=""
42
+ PROMPT=""
43
+ SCHEDULE_TYPE=""
44
+ INTERVAL_MINUTES=""
45
+ CRON_EXPR=""
46
+ SCHEDULED_AT=""
47
+ MAX_RUNS=""
48
+ IS_ENABLED="true"
49
+ DELIVERY_MODE="session"
50
+ CHANNEL_ID=""
51
+ TIMEZONE=""
52
+ POSITIONAL_ARGS=()
53
+
54
+ while [[ $# -gt 0 ]]; do
55
+ case "$1" in
56
+ --name) NAME="$2"; shift 2 ;;
57
+ --prompt) PROMPT="$2"; shift 2 ;;
58
+ --schedule-type) SCHEDULE_TYPE="$2"; shift 2 ;;
59
+ --interval-minutes) INTERVAL_MINUTES="$2"; shift 2 ;;
60
+ --cron) CRON_EXPR="$2"; shift 2 ;;
61
+ --scheduled-at) SCHEDULED_AT="$2"; shift 2 ;;
62
+ --max-runs) MAX_RUNS="$2"; shift 2 ;;
63
+ --delivery-mode) DELIVERY_MODE="$2"; shift 2 ;;
64
+ --channel-id) CHANNEL_ID="$2"; shift 2 ;;
65
+ --timezone) TIMEZONE="$2"; shift 2 ;;
66
+ --disabled) IS_ENABLED="false"; shift ;;
67
+ # Be tolerant: free-text arguments are treated as prompt fallback.
68
+ *) POSITIONAL_ARGS+=("$1"); shift ;;
69
+ esac
70
+ done
71
+
72
+ # Fallback for accidental natural-language invocation:
73
+ # bash create-automation.sh remind me tomorrow 9am
74
+ if [[ -z "${PROMPT}" && ${#POSITIONAL_ARGS[@]} -gt 0 ]]; then
75
+ PROMPT="${POSITIONAL_ARGS[*]}"
76
+ fi
77
+ if [[ -z "${NAME}" && -n "${PROMPT}" ]]; then
78
+ NAME="Automation $(date +%s)"
79
+ fi
80
+
81
+ # Validate required fields
82
+ if [[ -z "${NAME}" ]]; then
83
+ echo "ERROR: --name is required." >&2; exit 1
84
+ fi
85
+ if [[ -z "${PROMPT}" ]]; then
86
+ echo "ERROR: --prompt is required." >&2; exit 1
87
+ fi
88
+ if [[ -z "${SCHEDULE_TYPE}" ]]; then
89
+ echo "ERROR: --schedule-type is required (interval, cron, or once)." >&2
90
+ echo "Hint: pass schedule flags explicitly, e.g. --schedule-type cron --cron \"0 9 * * *\"" >&2
91
+ exit 1
92
+ fi
93
+ if [[ -z "${AGENT_KEY:-}" ]]; then
94
+ echo "ERROR: AGENT_KEY environment variable is not set." >&2; exit 1
95
+ fi
96
+ if [[ -z "${NEXT_PUBLIC_APP_URL:-}" ]]; then
97
+ echo "ERROR: NEXT_PUBLIC_APP_URL environment variable is not set." >&2; exit 1
98
+ fi
99
+
100
+ # Validate schedule-type-specific fields
101
+ case "${SCHEDULE_TYPE}" in
102
+ interval)
103
+ if [[ -z "${INTERVAL_MINUTES}" ]]; then
104
+ echo "ERROR: --interval-minutes is required for interval schedule type." >&2; exit 1
105
+ fi
106
+ ;;
107
+ cron)
108
+ if [[ -z "${CRON_EXPR}" ]]; then
109
+ echo "ERROR: --cron is required for cron schedule type." >&2; exit 1
110
+ fi
111
+ if [[ -z "${TIMEZONE}" ]]; then
112
+ echo "ERROR: --timezone is required for cron schedule type." >&2
113
+ echo "Hint: pass the user's IANA timezone, e.g. --timezone \"Asia/Shanghai\"" >&2
114
+ exit 1
115
+ fi
116
+ ;;
117
+ once)
118
+ if [[ -z "${SCHEDULED_AT}" ]]; then
119
+ echo "ERROR: --scheduled-at is required for once schedule type." >&2; exit 1
120
+ fi
121
+ ;;
122
+ *)
123
+ echo "ERROR: Invalid schedule type '${SCHEDULE_TYPE}'. Must be interval, cron, or once." >&2
124
+ exit 1
125
+ ;;
126
+ esac
127
+
128
+ # Validate delivery fields
129
+ if [[ "${DELIVERY_MODE}" != "session" && "${DELIVERY_MODE}" != "notify" ]]; then
130
+ echo "ERROR: --delivery-mode must be 'session' or 'notify'." >&2; exit 1
131
+ fi
132
+
133
+ # Channel-originated automation must deliver back to the same channel.
134
+ # BUDA_SOURCE_CHANNEL_ID is injected by Buda when chat originates from a channel.
135
+ # BUDA_SESSION_ID is the current session ID (maps to a specific group/DM via chatSessions).
136
+ if [[ -n "${BUDA_SOURCE_CHANNEL_ID:-}" ]]; then
137
+ DELIVERY_MODE="notify"
138
+ CHANNEL_ID="${BUDA_SOURCE_CHANNEL_ID}"
139
+ fi
140
+
141
+ SESSION_ID="${BUDA_SESSION_ID:-}"
142
+
143
+ if [[ "${DELIVERY_MODE}" == "notify" && -z "${CHANNEL_ID}" ]]; then
144
+ echo "ERROR: --channel-id is required when --delivery-mode notify is used." >&2; exit 1
145
+ fi
146
+
147
+ # Build JSON payload using jq for proper escaping
148
+ JSON_PAYLOAD=$(jq -n \
149
+ --arg name "${NAME}" \
150
+ --arg prompt "${PROMPT}" \
151
+ --arg scheduleType "${SCHEDULE_TYPE}" \
152
+ --arg cronExpression "${CRON_EXPR}" \
153
+ --arg intervalMinutes "${INTERVAL_MINUTES}" \
154
+ --arg scheduledAt "${SCHEDULED_AT}" \
155
+ --arg maxRuns "${MAX_RUNS}" \
156
+ --arg deliveryMode "${DELIVERY_MODE}" \
157
+ --arg channelId "${CHANNEL_ID}" \
158
+ --arg sessionId "${SESSION_ID}" \
159
+ --arg timezone "${TIMEZONE}" \
160
+ --argjson isEnabled "${IS_ENABLED}" \
161
+ '{
162
+ json: {
163
+ name: $name,
164
+ taskTemplate: {
165
+ title: $name,
166
+ prompt: $prompt,
167
+ delivery: {
168
+ mode: $deliveryMode
169
+ }
170
+ },
171
+ scheduleType: $scheduleType,
172
+ isEnabled: $isEnabled
173
+ }
174
+ }
175
+ | if $deliveryMode == "notify" then .json.taskTemplate.delivery.channelId = $channelId else . end
176
+ | if $sessionId != "" then .json.taskTemplate.delivery.sessionId = $sessionId else . end
177
+ | if $scheduleType == "cron" then .json.cronExpression = $cronExpression else . end
178
+ | if $scheduleType == "interval" then .json.intervalMinutes = ($intervalMinutes | tonumber) else . end
179
+ | if $scheduleType == "once" then .json.scheduledAt = $scheduledAt else . end
180
+ | if $maxRuns != "" then .json.maxRuns = ($maxRuns | tonumber) else . end
181
+ | if $timezone != "" then .json.timezone = $timezone else . end
182
+ ')
183
+
184
+ RESPONSE=$(curl -s \
185
+ "${NEXT_PUBLIC_APP_URL}/api/rpc/agent/automations/create" \
186
+ -X POST \
187
+ -H "Authorization: Bearer ${AGENT_KEY}" \
188
+ -H "Content-Type: application/json" \
189
+ -w '\n%{http_code}' \
190
+ -d "${JSON_PAYLOAD}" 2>/dev/null) || {
191
+ echo "ERROR: Failed to connect to API." >&2
192
+ exit 1
193
+ }
194
+
195
+ HTTP_CODE=$(echo "${RESPONSE}" | tail -1)
196
+ BODY=$(echo "${RESPONSE}" | sed '$d')
197
+
198
+ if [[ "${HTTP_CODE}" -ge 400 ]]; then
199
+ # Extract the human-readable error message from oRPC JSON error response
200
+ ERROR_MSG=$(echo "${BODY}" | jq -r '
201
+ (.json.message // "") as $msg |
202
+ (.json.code // "") as $code |
203
+ (if ($msg | type) == "string" and ($msg | startswith("[")) then
204
+ (try ($msg | fromjson | map((.path | join(".")) + ": " + .message) | join("; ")) catch $msg)
205
+ else
206
+ $msg
207
+ end) as $parsed_msg |
208
+ if $code != "" and $parsed_msg != "" then
209
+ "[\($code)] \($parsed_msg)"
210
+ elif $parsed_msg != "" then
211
+ $parsed_msg
212
+ else
213
+ empty
214
+ end
215
+ ' 2>/dev/null) || ERROR_MSG=""
216
+
217
+ if [[ -n "${ERROR_MSG}" ]]; then
218
+ echo "ERROR: ${ERROR_MSG}" >&2
219
+ else
220
+ echo "ERROR: API call failed (HTTP ${HTTP_CODE})." >&2
221
+ echo "${BODY}" >&2
222
+ fi
223
+ exit 1
224
+ fi
225
+
226
+ echo "${BODY}" | jq -r '.json'
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # Delete a scheduled automation by ID.
3
+ #
4
+ # Usage: bash delete-automation.sh --id "sch_xxxxxxxxxxxxxxxx"
5
+ #
6
+ # Requires: AGENT_KEY, NEXT_PUBLIC_APP_URL (auto-injected in Buda sandbox)
7
+
8
+ set -euo pipefail
9
+
10
+ AUTOMATION_ID=""
11
+
12
+ while [[ $# -gt 0 ]]; do
13
+ case "$1" in
14
+ --id) AUTOMATION_ID="$2"; shift 2 ;;
15
+ *) echo "Unknown option: $1" >&2; exit 1 ;;
16
+ esac
17
+ done
18
+
19
+ if [[ -z "${AUTOMATION_ID}" ]]; then
20
+ echo "ERROR: --id is required." >&2; exit 1
21
+ fi
22
+ if [[ -z "${AGENT_KEY:-}" ]]; then
23
+ echo "ERROR: AGENT_KEY environment variable is not set." >&2; exit 1
24
+ fi
25
+ if [[ -z "${NEXT_PUBLIC_APP_URL:-}" ]]; then
26
+ echo "ERROR: NEXT_PUBLIC_APP_URL environment variable is not set." >&2; exit 1
27
+ fi
28
+
29
+ PAYLOAD=$(jq -n --arg id "${AUTOMATION_ID}" '{ json: { id: $id } }')
30
+
31
+ RESPONSE=$(curl -s \
32
+ "${NEXT_PUBLIC_APP_URL}/api/rpc/agent/automations/delete" \
33
+ -X POST \
34
+ -H "Authorization: Bearer ${AGENT_KEY}" \
35
+ -H "Content-Type: application/json" \
36
+ -w '\n%{http_code}' \
37
+ -d "${PAYLOAD}" 2>/dev/null) || {
38
+ echo "ERROR: Failed to connect to API." >&2
39
+ exit 1
40
+ }
41
+
42
+ HTTP_CODE=$(echo "${RESPONSE}" | tail -1)
43
+ BODY=$(echo "${RESPONSE}" | sed '$d')
44
+
45
+ if [[ "${HTTP_CODE}" -ge 400 ]]; then
46
+ # Extract the human-readable error message from oRPC JSON error response
47
+ ERROR_MSG=$(echo "${BODY}" | jq -r '
48
+ (.json.message // "") as $msg |
49
+ (.json.code // "") as $code |
50
+ (if ($msg | type) == "string" and ($msg | startswith("[")) then
51
+ (try ($msg | fromjson | map((.path | join(".")) + ": " + .message) | join("; ")) catch $msg)
52
+ else
53
+ $msg
54
+ end) as $parsed_msg |
55
+ if $code != "" and $parsed_msg != "" then
56
+ "[\($code)] \($parsed_msg)"
57
+ elif $parsed_msg != "" then
58
+ $parsed_msg
59
+ else
60
+ empty
61
+ end
62
+ ' 2>/dev/null) || ERROR_MSG=""
63
+
64
+ if [[ -n "${ERROR_MSG}" ]]; then
65
+ echo "ERROR: ${ERROR_MSG}" >&2
66
+ else
67
+ echo "ERROR: API call failed (HTTP ${HTTP_CODE})." >&2
68
+ echo "${BODY}" >&2
69
+ fi
70
+ exit 1
71
+ fi
72
+
73
+ echo "${BODY}" | jq -r '.json'
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # List all automations in the current agent's workspace.
3
+ # Usage: bash list-automations.sh [--limit N] [--offset N]
4
+ #
5
+ # Requires: AGENT_KEY, NEXT_PUBLIC_APP_URL (auto-injected in Buda sandbox)
6
+
7
+ set -euo pipefail
8
+
9
+ LIMIT=20
10
+ OFFSET=0
11
+
12
+ while [[ $# -gt 0 ]]; do
13
+ case "$1" in
14
+ --limit) LIMIT="$2"; shift 2 ;;
15
+ --offset) OFFSET="$2"; shift 2 ;;
16
+ *) echo "Unknown option: $1" >&2; exit 1 ;;
17
+ esac
18
+ done
19
+
20
+ if [[ -z "${AGENT_KEY:-}" ]]; then
21
+ echo "ERROR: AGENT_KEY environment variable is not set." >&2
22
+ exit 1
23
+ fi
24
+ if [[ -z "${NEXT_PUBLIC_APP_URL:-}" ]]; then
25
+ echo "ERROR: NEXT_PUBLIC_APP_URL environment variable is not set." >&2
26
+ exit 1
27
+ fi
28
+
29
+ PAYLOAD=$(cat <<EOF
30
+ {"json":{"limit":${LIMIT},"offset":${OFFSET}}}
31
+ EOF
32
+ )
33
+
34
+ RESPONSE=$(curl -s \
35
+ "${NEXT_PUBLIC_APP_URL}/api/rpc/agent/automations/list" \
36
+ -X POST \
37
+ -H "Authorization: Bearer ${AGENT_KEY}" \
38
+ -H "Content-Type: application/json" \
39
+ -w '\n%{http_code}' \
40
+ -d "${PAYLOAD}" 2>/dev/null) || {
41
+ echo "ERROR: Failed to connect to API." >&2
42
+ exit 1
43
+ }
44
+
45
+ HTTP_CODE=$(echo "${RESPONSE}" | tail -1)
46
+ BODY=$(echo "${RESPONSE}" | sed '$d')
47
+
48
+ if [[ "${HTTP_CODE}" -ge 400 ]]; then
49
+ # Extract the human-readable error message from oRPC JSON error response
50
+ ERROR_MSG=$(echo "${BODY}" | jq -r '
51
+ (.json.message // "") as $msg |
52
+ (.json.code // "") as $code |
53
+ (if ($msg | type) == "string" and ($msg | startswith("[")) then
54
+ (try ($msg | fromjson | map((.path | join(".")) + ": " + .message) | join("; ")) catch $msg)
55
+ else
56
+ $msg
57
+ end) as $parsed_msg |
58
+ if $code != "" and $parsed_msg != "" then
59
+ "[\($code)] \($parsed_msg)"
60
+ elif $parsed_msg != "" then
61
+ $parsed_msg
62
+ else
63
+ empty
64
+ end
65
+ ' 2>/dev/null) || ERROR_MSG=""
66
+
67
+ if [[ -n "${ERROR_MSG}" ]]; then
68
+ echo "ERROR: ${ERROR_MSG}" >&2
69
+ else
70
+ echo "ERROR: API call failed (HTTP ${HTTP_CODE})." >&2
71
+ echo "${BODY}" >&2
72
+ fi
73
+ exit 1
74
+ fi
75
+
76
+ # Extract the result data
77
+ echo "${BODY}" | jq -r '.json'
@@ -0,0 +1,221 @@
1
+ ---
2
+ name: far
3
+ description: File-Augmented Retrieval — generate persistent .meta sidecar files for PDFs, images, spreadsheets, videos and more, making every binary file readable to AI coding agents.
4
+ allowed-tools: Read, Write, Edit, Bash, Glob
5
+ user-invocable: true
6
+ ---
7
+
8
+ # 📄 /far - File-Augmented Retrieval
9
+
10
+ > **"Making Every File Readable to Coding Agents via Persistent .meta Sidecars"**
11
+
12
+ FAR is a file augmentation protocol that generates persistent `.meta` sidecar files for binary documents (PDF, DOCX, XLSX, PPTX, Images, etc.). This allows AI coding agents (like OpenClaw, Cursor, GitHub Copilot) to "read" non-text files directly from the file system without requiring external RAG infrastructure.
13
+
14
+ **Current Version:** 1.0.0
15
+ **Author:** Kelly Peilin Chan
16
+
17
+ ---
18
+
19
+ ## 🚀 The Problem: AI "Blindness"
20
+
21
+ AI agents operating in a repository can read code (`.py`, `.js`, `.md`), but they are blind to 30-40% of critical context stored in binary formats:
22
+ - **Product Specs**: `requirements.docx`
23
+ - **Design Mocks**: `architecture.png`
24
+ - **Financial Data**: `budget.xlsx`
25
+ - **Contracts**: `agreement.pdf`
26
+
27
+ When an agent encounters `budget.xlsx`, it sees opaque bytes. It cannot reason about the content.
28
+
29
+ ## 💡 The Solution: Persistent Sidecars
30
+
31
+ FAR solves this by generating a human-readable and machine-readable `.meta` file next to every binary file.
32
+
33
+ **Example:**
34
+ ```
35
+ project/
36
+ ├── budget.xlsx (Binary, opaque)
37
+ └── budget.xlsx.meta (Markdown, readable)
38
+ ```
39
+
40
+ The agent simply reads `budget.xlsx.meta` to understand the spreadsheet. No vector database, no API calls, no runtime overhead.
41
+
42
+ ---
43
+
44
+ ## ✨ Features
45
+
46
+ ### 1. Broad Format Support
47
+ FAR extracts text and structure from a wide range of formats:
48
+ * **📄 PDF** (`.pdf`): Full text extraction with layout preservation. **OCR fallback** for scanned PDFs. **Embedded image extraction** (pdfimages + OCR/Vision).
49
+ * **📝 Word** (`.docx`, `.doc`): Text extraction.
50
+ * **📊 Excel** (`.xlsx`): Sheet data converted to Markdown tables.
51
+ * **📽️ PowerPoint** (`.pptx`): Slide text extraction.
52
+ * **🖼️ Images** (`.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.webp`): **OCR** (Tesseract) + **Apple Vision (on-device, macOS)** + **AI Vision** (GPT-4o) if configured.
53
+ * **🎬 Media** (`.mp4`, `.mov`, `.mp3`, `.wav`, `.m4a`, `.flac`): Technical metadata (FFprobe) + **Apple Vision video-frame analysis (macOS)** + **AI Transcription** through Buda's `whisper` tool when available.
54
+ * **📋 CSV** (`.csv`): Data rendered as Markdown tables (up to 100 rows).
55
+ * **📓 Jupyter Notebook** (`.ipynb`): Markdown cells, code cells, and outputs.
56
+ * **📚 EPUB** (`.epub`): Full text extracted from all chapters in spine order.
57
+ * **📦 Tar** (`.tar`, `.tar.gz`, `.tgz`, `.bz2`, `.xz`): File listing with sizes.
58
+ * **📧 Email** (`.eml`, `.msg`): Headers, body text, and attachment list.
59
+ * **📝 RTF** (`.rtf`): Plain text extraction via control word stripping.
60
+ * **🗄️ SQLite** (`.db`, `.sqlite`, `.sqlite3`): Table schemas + latest 20 rows per table as Markdown tables.
61
+ * **📊 Parquet** (`.parquet`): Schema and row count via `pyarrow` (optional). *[Metadata only]*
62
+ * **🎨 Design** (`.fig`, `.sketch`, `.xd`): File size and page/canvas count. *[Metadata only]*
63
+ * **💻 Code/Text** (`.py`, `.js`, `.ts`, `.go`, `.rs`, `.md`, `.json`, ...): Direct content mirroring.
64
+
65
+ ### 2. Intelligent Caching (Incremental Build)
66
+ FAR is designed for speed. It uses a **two-layer caching mechanism**:
67
+ 1. **Fast Check (mtime & size)**: If the file hasn't been modified, it skips processing instantly (0.003s).
68
+ 2. **Content Check (SHA256)**: If mtime changed, it calculates the file hash. If the content is identical, it updates the timestamp but skips re-extraction.
69
+
70
+ For directory summaries, FAR also avoids noisy rewrites: if `.dir.meta` summary content is unchanged, it keeps the file untouched (no `extracted_at` churn).
71
+
72
+ ### 3. Directory Summaries (`.dir.meta`)
73
+ FAR generates a `.dir.meta` file in every directory, providing a high-level summary of all files within. This allows agents to "browse" a folder and understand its contents without reading every single file.
74
+
75
+ ### 4. Git LFS Support
76
+ FAR automatically handles Git LFS pointer files. It will attempt to pull the real content before processing, ensuring you don't get empty metadata for LFS-tracked files.
77
+
78
+ ---
79
+
80
+ ## 🛠️ Usage
81
+
82
+ ### Installation
83
+ The skill is pre-installed in the OpenClaw workspace:
84
+ ```bash
85
+ ~/.openclaw/workspace/.agents/skills/far/far_gen.py
86
+ ```
87
+ A symlink is available as `far`:
88
+ ```bash
89
+ far [directory_or_file]
90
+ ```
91
+
92
+ ### Commands
93
+
94
+ **Scan current directory (Recursive):**
95
+ ```bash
96
+ far
97
+ ```
98
+
99
+ **Scan specific directory:**
100
+ ```bash
101
+ far ~/Documents/projects/files
102
+ ```
103
+
104
+ **Process single file:**
105
+ ```bash
106
+ far report.pdf
107
+ ```
108
+
109
+ **Force regeneration (Ignore cache):**
110
+ ```bash
111
+ far . --force
112
+ ```
113
+
114
+ ### Configuration (AI Features)
115
+ In Buda sandboxes, audio/video transcription uses the Buda-native `whisper` tool through the auto-injected `AGENT_KEY` and `NEXT_PUBLIC_APP_URL` environment variables. No user OpenAI key is required inside the sandbox for transcription.
116
+
117
+ For standalone FAR usage outside Buda, or for AI Vision image description fallback, create a `.env` file in the skill directory or your home folder `~/.far.env`.
118
+ Copy `.env.example` as a template:
119
+
120
+ ```bash
121
+ OPENAI_API_KEY=sk-your-key-here
122
+ # Optional
123
+ OPENAI_BASE_URL=https://api.openai.com/v1
124
+ # Optional (macOS default: enabled)
125
+ FAR_USE_APPLE_VISION=1
126
+ # Optional (macOS default: enabled)
127
+ FAR_USE_MACOS_METADATA=1
128
+ # Optional tuning
129
+ FAR_APPLE_VISION_TIMEOUT=25
130
+ FAR_APPLE_VISION_MAX_FRAMES=6
131
+ ```
132
+
133
+ If API keys are missing, FAR gracefully falls back to local tools (Tesseract, FFprobe),
134
+ and on macOS it also uses Apple Vision + Spotlight metadata on-device by default.
135
+
136
+ ### Configuration (Ignore)
137
+ Create a `.farignore` file in your project root to exclude files or directories from scanning.
138
+ ```gitignore
139
+ # .farignore
140
+ node_modules
141
+ .git
142
+ secrets/
143
+ *.tmp
144
+ ```
145
+
146
+ ---
147
+
148
+ ## 🤖 Agent Integration
149
+
150
+ Add one rule to `AGENTS.md` or your system prompt:
151
+
152
+ ```
153
+ ## File Reading with FAR
154
+
155
+ When navigating a directory, first read `.dir.meta` for a high-level overview
156
+ before opening individual files.
157
+
158
+ When you encounter a binary file you cannot read directly
159
+ (.png, .pdf, .xlsx, .mp4, .fig, .db, etc.), check for a .meta file
160
+ beside it — it contains the extracted content as Markdown.
161
+
162
+ Navigation strategy:
163
+ 1. Read <dir>/.dir.meta to understand directory structure
164
+ 2. Read <file>.meta to access binary file content
165
+ 3. Fall back to reading the source file only if no .meta exists
166
+ ```
167
+
168
+ ---
169
+
170
+ ## 🏗️ Protocol Specification (v1)
171
+
172
+ Each `.meta` file follows a strict format with a YAML frontmatter header and Markdown body.
173
+
174
+ ```markdown
175
+ --far_version: 1
176
+ source:
177
+ sha256: 5a1cc2b8d...
178
+ mime: application/pdf
179
+ size: 129509
180
+ mtime: 1708845210.5
181
+ extract:
182
+ pipeline: far_gen_v14
183
+ extracted_at: 2026-02-27T10:00:00Z
184
+ deterministic: true
185
+ layout:
186
+ pages: 24
187
+ ---
188
+ # filename.pdf
189
+
190
+ [Extracted Content Here...]
191
+ ```
192
+
193
+ Cache invalidation: a `.meta` is stale if `sha256` or `pipeline` version has changed.
194
+
195
+ ---
196
+
197
+ ## 🤝 Contributing
198
+
199
+ **Roadmap:**
200
+ - [x] PDF/Word/Excel/PowerPoint Support
201
+ - [x] Incremental Caching (SHA-256 + mtime)
202
+ - [x] Directory Summaries (`.dir.meta`)
203
+ - [x] OCR for Images/Scanned PDFs (Tesseract)
204
+ - [x] Media Metadata (FFprobe) + Transcription (Whisper)
205
+ - [x] AI Vision for Images (GPT-4o)
206
+ - [x] Apple Vision for Images (on-device, macOS)
207
+ - [x] Apple Vision for Video Frames (on-device labels/OCR/faces/barcodes/pose)
208
+ - [x] Apple Vision Feature Print fingerprint (on-device embedding hash)
209
+ - [x] macOS Spotlight metadata enrichment (mdls)
210
+ - [x] PDF Embedded Image Extraction
211
+ - [x] CSV → Markdown Tables
212
+ - [x] Jupyter Notebook Support
213
+ - [x] EPUB, ZIP/TAR, Email, RTF
214
+ - [x] SQLite Schema + Data Preview
215
+ - [x] Parquet Schema, Design File Metadata
216
+ - [x] Pipeline version stale detection
217
+ - [x] Layout frontmatter (pages/sheets/slides)
218
+ - [x] Full glob support in `.farignore`
219
+ - [ ] MCP resource server integration
220
+ - [ ] PII redaction rules
221
+ - [ ] Rust implementation for CI/CD speed