gigaclaw 1.4.0

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 (249) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +237 -0
  3. package/api/CLAUDE.md +19 -0
  4. package/api/index.js +265 -0
  5. package/bin/cli.js +823 -0
  6. package/bin/local.sh +85 -0
  7. package/bin/postinstall.js +63 -0
  8. package/config/index.js +26 -0
  9. package/config/instrumentation.js +62 -0
  10. package/drizzle/0000_initial.sql +52 -0
  11. package/drizzle/0001_nostalgic_sersi.sql +11 -0
  12. package/drizzle/0002_black_daimon_hellstrom.sql +19 -0
  13. package/drizzle/0003_rename_code_workspaces.sql +5 -0
  14. package/drizzle/meta/0000_snapshot.json +321 -0
  15. package/drizzle/meta/0001_snapshot.json +390 -0
  16. package/drizzle/meta/0002_snapshot.json +411 -0
  17. package/drizzle/meta/0003_snapshot.json +419 -0
  18. package/drizzle/meta/_journal.json +34 -0
  19. package/lib/actions.js +44 -0
  20. package/lib/ai/agent.js +86 -0
  21. package/lib/ai/index.js +342 -0
  22. package/lib/ai/model.js +180 -0
  23. package/lib/ai/tools.js +269 -0
  24. package/lib/ai/web-search.js +42 -0
  25. package/lib/auth/actions.js +28 -0
  26. package/lib/auth/config.js +27 -0
  27. package/lib/auth/edge-config.js +27 -0
  28. package/lib/auth/index.js +27 -0
  29. package/lib/auth/middleware.js +62 -0
  30. package/lib/channels/base.js +56 -0
  31. package/lib/channels/index.js +15 -0
  32. package/lib/channels/telegram.js +148 -0
  33. package/lib/chat/actions.js +579 -0
  34. package/lib/chat/api.js +140 -0
  35. package/lib/chat/components/app-sidebar.js +213 -0
  36. package/lib/chat/components/app-sidebar.jsx +279 -0
  37. package/lib/chat/components/chat-header.js +192 -0
  38. package/lib/chat/components/chat-header.jsx +223 -0
  39. package/lib/chat/components/chat-input.js +236 -0
  40. package/lib/chat/components/chat-input.jsx +249 -0
  41. package/lib/chat/components/chat-nav-context.js +11 -0
  42. package/lib/chat/components/chat-nav-context.jsx +11 -0
  43. package/lib/chat/components/chat-page.js +99 -0
  44. package/lib/chat/components/chat-page.jsx +121 -0
  45. package/lib/chat/components/chat.js +153 -0
  46. package/lib/chat/components/chat.jsx +199 -0
  47. package/lib/chat/components/chats-page.js +367 -0
  48. package/lib/chat/components/chats-page.jsx +394 -0
  49. package/lib/chat/components/code-mode-toggle.js +132 -0
  50. package/lib/chat/components/code-mode-toggle.jsx +163 -0
  51. package/lib/chat/components/crons-page.js +172 -0
  52. package/lib/chat/components/crons-page.jsx +244 -0
  53. package/lib/chat/components/greeting.js +11 -0
  54. package/lib/chat/components/greeting.jsx +16 -0
  55. package/lib/chat/components/icons.js +805 -0
  56. package/lib/chat/components/icons.jsx +751 -0
  57. package/lib/chat/components/index.js +20 -0
  58. package/lib/chat/components/message.js +363 -0
  59. package/lib/chat/components/message.jsx +422 -0
  60. package/lib/chat/components/messages.js +65 -0
  61. package/lib/chat/components/messages.jsx +74 -0
  62. package/lib/chat/components/notifications-page.js +56 -0
  63. package/lib/chat/components/notifications-page.jsx +87 -0
  64. package/lib/chat/components/page-layout.js +21 -0
  65. package/lib/chat/components/page-layout.jsx +28 -0
  66. package/lib/chat/components/pull-requests-page.js +103 -0
  67. package/lib/chat/components/pull-requests-page.jsx +113 -0
  68. package/lib/chat/components/settings-layout.js +39 -0
  69. package/lib/chat/components/settings-layout.jsx +53 -0
  70. package/lib/chat/components/settings-secrets-page.js +216 -0
  71. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  72. package/lib/chat/components/sidebar-history-item.js +138 -0
  73. package/lib/chat/components/sidebar-history-item.jsx +119 -0
  74. package/lib/chat/components/sidebar-history.js +167 -0
  75. package/lib/chat/components/sidebar-history.jsx +220 -0
  76. package/lib/chat/components/sidebar-user-nav.js +61 -0
  77. package/lib/chat/components/sidebar-user-nav.jsx +77 -0
  78. package/lib/chat/components/swarm-page.js +157 -0
  79. package/lib/chat/components/swarm-page.jsx +210 -0
  80. package/lib/chat/components/tool-call.js +89 -0
  81. package/lib/chat/components/tool-call.jsx +107 -0
  82. package/lib/chat/components/triggers-page.js +153 -0
  83. package/lib/chat/components/triggers-page.jsx +221 -0
  84. package/lib/chat/components/ui/combobox.js +98 -0
  85. package/lib/chat/components/ui/combobox.jsx +114 -0
  86. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  87. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  88. package/lib/chat/components/ui/dropdown-menu.js +194 -0
  89. package/lib/chat/components/ui/dropdown-menu.jsx +215 -0
  90. package/lib/chat/components/ui/rename-dialog.js +78 -0
  91. package/lib/chat/components/ui/rename-dialog.jsx +74 -0
  92. package/lib/chat/components/ui/scroll-area.js +13 -0
  93. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  94. package/lib/chat/components/ui/separator.js +21 -0
  95. package/lib/chat/components/ui/separator.jsx +18 -0
  96. package/lib/chat/components/ui/sheet.js +75 -0
  97. package/lib/chat/components/ui/sheet.jsx +95 -0
  98. package/lib/chat/components/ui/sidebar.js +228 -0
  99. package/lib/chat/components/ui/sidebar.jsx +246 -0
  100. package/lib/chat/components/ui/tooltip.js +56 -0
  101. package/lib/chat/components/ui/tooltip.jsx +66 -0
  102. package/lib/chat/components/upgrade-dialog.js +151 -0
  103. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  104. package/lib/chat/utils.js +11 -0
  105. package/lib/code/actions.js +153 -0
  106. package/lib/code/code-page.js +22 -0
  107. package/lib/code/code-page.jsx +25 -0
  108. package/lib/code/index.js +1 -0
  109. package/lib/code/terminal-view.js +201 -0
  110. package/lib/code/terminal-view.jsx +224 -0
  111. package/lib/code/ws-proxy.js +80 -0
  112. package/lib/cron.js +246 -0
  113. package/lib/db/api-keys.js +163 -0
  114. package/lib/db/chats.js +168 -0
  115. package/lib/db/code-workspaces.js +110 -0
  116. package/lib/db/index.js +52 -0
  117. package/lib/db/notifications.js +99 -0
  118. package/lib/db/schema.js +66 -0
  119. package/lib/db/update-check.js +96 -0
  120. package/lib/db/users.js +89 -0
  121. package/lib/paths.js +42 -0
  122. package/lib/tools/create-job.js +97 -0
  123. package/lib/tools/docker.js +146 -0
  124. package/lib/tools/github.js +271 -0
  125. package/lib/tools/openai.js +35 -0
  126. package/lib/tools/telegram.js +292 -0
  127. package/lib/triggers.js +104 -0
  128. package/lib/utils/render-md.js +111 -0
  129. package/package.json +118 -0
  130. package/setup/lib/auth.mjs +81 -0
  131. package/setup/lib/env.mjs +21 -0
  132. package/setup/lib/fs-utils.mjs +20 -0
  133. package/setup/lib/github.mjs +149 -0
  134. package/setup/lib/prerequisites.mjs +155 -0
  135. package/setup/lib/prompts.mjs +267 -0
  136. package/setup/lib/providers.mjs +105 -0
  137. package/setup/lib/sync.mjs +125 -0
  138. package/setup/lib/targets.mjs +45 -0
  139. package/setup/lib/telegram-verify.mjs +63 -0
  140. package/setup/lib/telegram.mjs +76 -0
  141. package/setup/setup-cloud.mjs +833 -0
  142. package/setup/setup-local.mjs +377 -0
  143. package/setup/setup-telegram.mjs +265 -0
  144. package/setup/setup.mjs +87 -0
  145. package/templates/.dockerignore +5 -0
  146. package/templates/.env.example +104 -0
  147. package/templates/.github/workflows/auto-merge.yml +117 -0
  148. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  149. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  150. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  151. package/templates/.github/workflows/run-job.yml +89 -0
  152. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  153. package/templates/.gitignore.template +45 -0
  154. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  155. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  156. package/templates/CLAUDE.md +29 -0
  157. package/templates/CLAUDE.md.template +308 -0
  158. package/templates/app/api/[...gigaclaw]/route.js +1 -0
  159. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  160. package/templates/app/chat/[chatId]/page.js +9 -0
  161. package/templates/app/chats/page.js +7 -0
  162. package/templates/app/code/[codeWorkspaceId]/page.js +9 -0
  163. package/templates/app/components/ascii-logo.jsx +12 -0
  164. package/templates/app/components/login-form.jsx +92 -0
  165. package/templates/app/components/setup-form.jsx +82 -0
  166. package/templates/app/components/theme-provider.jsx +11 -0
  167. package/templates/app/components/theme-toggle.jsx +38 -0
  168. package/templates/app/components/ui/button.jsx +21 -0
  169. package/templates/app/components/ui/card.jsx +23 -0
  170. package/templates/app/components/ui/input.jsx +10 -0
  171. package/templates/app/components/ui/label.jsx +10 -0
  172. package/templates/app/crons/page.js +5 -0
  173. package/templates/app/globals.css +90 -0
  174. package/templates/app/layout.js +33 -0
  175. package/templates/app/login/page.js +15 -0
  176. package/templates/app/notifications/page.js +7 -0
  177. package/templates/app/page.js +7 -0
  178. package/templates/app/pull-requests/page.js +7 -0
  179. package/templates/app/settings/crons/page.js +5 -0
  180. package/templates/app/settings/layout.js +7 -0
  181. package/templates/app/settings/page.js +5 -0
  182. package/templates/app/settings/secrets/page.js +5 -0
  183. package/templates/app/settings/triggers/page.js +5 -0
  184. package/templates/app/stream/chat/route.js +1 -0
  185. package/templates/app/swarm/page.js +7 -0
  186. package/templates/app/triggers/page.js +5 -0
  187. package/templates/config/CODE_PLANNING.md +14 -0
  188. package/templates/config/CRONS.json +56 -0
  189. package/templates/config/HEARTBEAT.md +3 -0
  190. package/templates/config/JOB_AGENT.md +30 -0
  191. package/templates/config/JOB_PLANNING.md +240 -0
  192. package/templates/config/JOB_SUMMARY.md +130 -0
  193. package/templates/config/SKILL_BUILDING_GUIDE.md +96 -0
  194. package/templates/config/SOUL.md +48 -0
  195. package/templates/config/TRIGGERS.json +58 -0
  196. package/templates/config/WEB_SEARCH_AVAILABLE.md +5 -0
  197. package/templates/config/WEB_SEARCH_UNAVAILABLE.md +3 -0
  198. package/templates/docker/claude-code-job/Dockerfile +34 -0
  199. package/templates/docker/claude-code-job/entrypoint.sh +149 -0
  200. package/templates/docker/claude-code-workspace/.tmux.conf +5 -0
  201. package/templates/docker/claude-code-workspace/Dockerfile +61 -0
  202. package/templates/docker/claude-code-workspace/entrypoint.sh +51 -0
  203. package/templates/docker/event-handler/Dockerfile +20 -0
  204. package/templates/docker/event-handler/ecosystem.config.cjs +7 -0
  205. package/templates/docker/pi-coding-agent-job/Dockerfile +51 -0
  206. package/templates/docker/pi-coding-agent-job/entrypoint.sh +164 -0
  207. package/templates/docker-compose.local.yml +78 -0
  208. package/templates/docker-compose.yml +64 -0
  209. package/templates/instrumentation.js +6 -0
  210. package/templates/middleware.js +23 -0
  211. package/templates/next.config.mjs +3 -0
  212. package/templates/postcss.config.mjs +5 -0
  213. package/templates/public/favicon.ico +0 -0
  214. package/templates/server.js +25 -0
  215. package/templates/skills/LICENSE +21 -0
  216. package/templates/skills/README.md +119 -0
  217. package/templates/skills/brave-search/SKILL.md +79 -0
  218. package/templates/skills/brave-search/content.js +86 -0
  219. package/templates/skills/brave-search/package-lock.json +621 -0
  220. package/templates/skills/brave-search/package.json +14 -0
  221. package/templates/skills/brave-search/search.js +199 -0
  222. package/templates/skills/browser-tools/SKILL.md +196 -0
  223. package/templates/skills/browser-tools/browser-content.js +103 -0
  224. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  225. package/templates/skills/browser-tools/browser-eval.js +53 -0
  226. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  227. package/templates/skills/browser-tools/browser-nav.js +44 -0
  228. package/templates/skills/browser-tools/browser-pick.js +162 -0
  229. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  230. package/templates/skills/browser-tools/browser-start.js +87 -0
  231. package/templates/skills/browser-tools/package-lock.json +2556 -0
  232. package/templates/skills/browser-tools/package.json +19 -0
  233. package/templates/skills/google-docs/SKILL.md +23 -0
  234. package/templates/skills/google-docs/create.sh +69 -0
  235. package/templates/skills/google-drive/SKILL.md +47 -0
  236. package/templates/skills/google-drive/delete.sh +47 -0
  237. package/templates/skills/google-drive/download.sh +50 -0
  238. package/templates/skills/google-drive/list.sh +41 -0
  239. package/templates/skills/google-drive/upload.sh +76 -0
  240. package/templates/skills/kie-ai/SKILL.md +38 -0
  241. package/templates/skills/kie-ai/generate-image.sh +77 -0
  242. package/templates/skills/kie-ai/generate-video.sh +69 -0
  243. package/templates/skills/llm-secrets/SKILL.md +34 -0
  244. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  245. package/templates/skills/modify-self/SKILL.md +12 -0
  246. package/templates/skills/youtube-transcript/SKILL.md +41 -0
  247. package/templates/skills/youtube-transcript/package-lock.json +24 -0
  248. package/templates/skills/youtube-transcript/package.json +8 -0
  249. package/templates/skills/youtube-transcript/transcript.js +84 -0
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "browser-tools",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Minimal CDP tools for collaborative site exploration",
6
+ "author": "Mario Zechner",
7
+ "license": "MIT",
8
+ "dependencies": {
9
+ "@mozilla/readability": "^0.6.0",
10
+ "cheerio": "^1.1.2",
11
+ "jsdom": "^27.0.1",
12
+ "puppeteer": "^24.31.0",
13
+ "puppeteer-core": "^23.11.1",
14
+ "puppeteer-extra": "^3.3.6",
15
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
16
+ "turndown": "^7.2.2",
17
+ "turndown-plugin-gfm": "^1.0.2"
18
+ }
19
+ }
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: google-docs
3
+ description: "Create and manage Google Docs on a shared drive via service account."
4
+ ---
5
+
6
+ # Google Docs
7
+
8
+ Create and manage Google Docs on a shared drive using a service account.
9
+
10
+ ## Environment Variables
11
+
12
+ - `GOOGLE_SERVICE_ACCOUNT_JSON` — Service account credentials JSON
13
+ - `GOOGLE_SHARED_DRIVE_ID` — Shared drive ID
14
+
15
+ ## Commands
16
+
17
+ ### Create a Google Doc
18
+
19
+ ```bash
20
+ skills/google-docs/create.sh <title> <content> <parent_folder_id>
21
+ ```
22
+
23
+ Creates a new Google Doc with the given title and text content in the specified folder. Returns the document ID and URL.
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ TITLE="${1:?Usage: create.sh <title> <content> <parent_folder_id>}"
5
+ CONTENT="${2:?Usage: create.sh <title> <content> <parent_folder_id>}"
6
+ PARENT_FOLDER_ID="${3:?Usage: create.sh <title> <content> <parent_folder_id>}"
7
+
8
+ if [ -z "${GOOGLE_SERVICE_ACCOUNT_JSON:-}" ]; then
9
+ echo "Error: GOOGLE_SERVICE_ACCOUNT_JSON is not set" >&2
10
+ exit 1
11
+ fi
12
+
13
+ # Extract service account email and private key
14
+ SA_EMAIL=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.client_email')
15
+ SA_KEY=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.private_key')
16
+ TOKEN_URI=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.token_uri')
17
+
18
+ # Build JWT with both drive and docs scopes
19
+ NOW=$(date +%s)
20
+ EXP=$((NOW + 3600))
21
+ HEADER=$(echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
22
+ CLAIMS=$(printf '{"iss":"%s","scope":"https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/documents","aud":"%s","iat":%d,"exp":%d}' \
23
+ "$SA_EMAIL" "$TOKEN_URI" "$NOW" "$EXP" | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
24
+ SIGNING_INPUT="${HEADER}.${CLAIMS}"
25
+ SIGNATURE=$(printf '%s' "$SIGNING_INPUT" | openssl dgst -sha256 -sign <(echo "$SA_KEY") | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
26
+ JWT="${SIGNING_INPUT}.${SIGNATURE}"
27
+
28
+ # Exchange JWT for access token
29
+ ACCESS_TOKEN=$(curl -s -X POST "$TOKEN_URI" \
30
+ -H "Content-Type: application/x-www-form-urlencoded" \
31
+ -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${JWT}" | jq -r '.access_token')
32
+
33
+ if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
34
+ echo "Error: Failed to obtain access token" >&2
35
+ exit 1
36
+ fi
37
+
38
+ # Create Google Doc via Drive API (creates empty doc in the shared drive)
39
+ METADATA=$(jq -n --arg title "$TITLE" --arg parent "$PARENT_FOLDER_ID" \
40
+ '{name: $title, mimeType: "application/vnd.google-apps.document", parents: [$parent]}')
41
+
42
+ CREATE_RESPONSE=$(curl -s -X POST \
43
+ "https://www.googleapis.com/drive/v3/files?supportsAllDrives=true&includeItemsFromAllDrives=true" \
44
+ -H "Authorization: Bearer ${ACCESS_TOKEN}" \
45
+ -H "Content-Type: application/json" \
46
+ -d "$METADATA")
47
+
48
+ DOC_ID=$(echo "$CREATE_RESPONSE" | jq -r '.id')
49
+ if [ -z "$DOC_ID" ] || [ "$DOC_ID" = "null" ]; then
50
+ echo "Error: Failed to create document" >&2
51
+ echo "$CREATE_RESPONSE" >&2
52
+ exit 1
53
+ fi
54
+
55
+ # Insert content using Docs API batchUpdate
56
+ if [ -n "$CONTENT" ]; then
57
+ UPDATE_BODY=$(jq -n --arg text "$CONTENT" \
58
+ '{requests: [{insertText: {location: {index: 1}, text: $text}}]}')
59
+
60
+ curl -s -X POST \
61
+ "https://docs.googleapis.com/v1/documents/${DOC_ID}:batchUpdate" \
62
+ -H "Authorization: Bearer ${ACCESS_TOKEN}" \
63
+ -H "Content-Type: application/json" \
64
+ -d "$UPDATE_BODY" > /dev/null
65
+ fi
66
+
67
+ echo "Created Google Doc: $TITLE"
68
+ echo "Doc ID: $DOC_ID"
69
+ echo "URL: https://docs.google.com/document/d/${DOC_ID}/edit"
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: google-drive
3
+ description: "Interact with Google Drive shared drives via service account. Supports list, upload, download, delete operations."
4
+ ---
5
+
6
+ # Google Drive
7
+
8
+ Interact with Google Drive shared drives using a service account.
9
+
10
+ ## Environment Variables
11
+
12
+ - `GOOGLE_SERVICE_ACCOUNT_JSON` — Service account credentials JSON
13
+ - `GOOGLE_SHARED_DRIVE_ID` — Shared drive ID
14
+
15
+ ## Commands
16
+
17
+ ### List files in a folder
18
+
19
+ ```bash
20
+ skills/google-drive/list.sh <folder_id>
21
+ ```
22
+
23
+ Lists files and folders in the given folder. Use `$GOOGLE_SHARED_DRIVE_ID` for the root.
24
+
25
+ ### Upload a file
26
+
27
+ ```bash
28
+ skills/google-drive/upload.sh <local_file> <parent_folder_id> <filename>
29
+ ```
30
+
31
+ Uploads a local file to the specified folder with the given name.
32
+
33
+ ### Download a file
34
+
35
+ ```bash
36
+ skills/google-drive/download.sh <file_id> <local_path>
37
+ ```
38
+
39
+ Downloads a file by ID to a local path.
40
+
41
+ ### Delete a file
42
+
43
+ ```bash
44
+ skills/google-drive/delete.sh <file_id>
45
+ ```
46
+
47
+ Deletes a file by ID.
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ FILE_ID="${1:?Usage: delete.sh <file_id>}"
5
+
6
+ if [ -z "${GOOGLE_SERVICE_ACCOUNT_JSON:-}" ]; then
7
+ echo "Error: GOOGLE_SERVICE_ACCOUNT_JSON is not set" >&2
8
+ exit 1
9
+ fi
10
+
11
+ # Extract service account email and private key
12
+ SA_EMAIL=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.client_email')
13
+ SA_KEY=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.private_key')
14
+ TOKEN_URI=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.token_uri')
15
+
16
+ # Build JWT
17
+ NOW=$(date +%s)
18
+ EXP=$((NOW + 3600))
19
+ HEADER=$(echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
20
+ CLAIMS=$(printf '{"iss":"%s","scope":"https://www.googleapis.com/auth/drive","aud":"%s","iat":%d,"exp":%d}' \
21
+ "$SA_EMAIL" "$TOKEN_URI" "$NOW" "$EXP" | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
22
+ SIGNING_INPUT="${HEADER}.${CLAIMS}"
23
+ SIGNATURE=$(printf '%s' "$SIGNING_INPUT" | openssl dgst -sha256 -sign <(echo "$SA_KEY") | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
24
+ JWT="${SIGNING_INPUT}.${SIGNATURE}"
25
+
26
+ # Exchange JWT for access token
27
+ ACCESS_TOKEN=$(curl -s -X POST "$TOKEN_URI" \
28
+ -H "Content-Type: application/x-www-form-urlencoded" \
29
+ -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${JWT}" | jq -r '.access_token')
30
+
31
+ if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
32
+ echo "Error: Failed to obtain access token" >&2
33
+ exit 1
34
+ fi
35
+
36
+ # Delete file
37
+ HTTP_CODE=$(curl -s -w "%{http_code}" -o /dev/null \
38
+ -X DELETE \
39
+ "https://www.googleapis.com/drive/v3/files/${FILE_ID}?supportsAllDrives=true" \
40
+ -H "Authorization: Bearer ${ACCESS_TOKEN}")
41
+
42
+ if [ "$HTTP_CODE" -ne 204 ] && [ "$HTTP_CODE" -ne 200 ]; then
43
+ echo "Error: Delete failed with HTTP $HTTP_CODE" >&2
44
+ exit 1
45
+ fi
46
+
47
+ echo "Deleted file: $FILE_ID"
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ FILE_ID="${1:?Usage: download.sh <file_id> <local_path>}"
5
+ LOCAL_PATH="${2:?Usage: download.sh <file_id> <local_path>}"
6
+
7
+ if [ -z "${GOOGLE_SERVICE_ACCOUNT_JSON:-}" ]; then
8
+ echo "Error: GOOGLE_SERVICE_ACCOUNT_JSON is not set" >&2
9
+ exit 1
10
+ fi
11
+
12
+ # Extract service account email and private key
13
+ SA_EMAIL=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.client_email')
14
+ SA_KEY=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.private_key')
15
+ TOKEN_URI=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.token_uri')
16
+
17
+ # Build JWT
18
+ NOW=$(date +%s)
19
+ EXP=$((NOW + 3600))
20
+ HEADER=$(echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
21
+ CLAIMS=$(printf '{"iss":"%s","scope":"https://www.googleapis.com/auth/drive","aud":"%s","iat":%d,"exp":%d}' \
22
+ "$SA_EMAIL" "$TOKEN_URI" "$NOW" "$EXP" | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
23
+ SIGNING_INPUT="${HEADER}.${CLAIMS}"
24
+ SIGNATURE=$(printf '%s' "$SIGNING_INPUT" | openssl dgst -sha256 -sign <(echo "$SA_KEY") | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
25
+ JWT="${SIGNING_INPUT}.${SIGNATURE}"
26
+
27
+ # Exchange JWT for access token
28
+ ACCESS_TOKEN=$(curl -s -X POST "$TOKEN_URI" \
29
+ -H "Content-Type: application/x-www-form-urlencoded" \
30
+ -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${JWT}" | jq -r '.access_token')
31
+
32
+ if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
33
+ echo "Error: Failed to obtain access token" >&2
34
+ exit 1
35
+ fi
36
+
37
+ # Download file
38
+ HTTP_CODE=$(curl -s -w "%{http_code}" -o "$LOCAL_PATH" \
39
+ "https://www.googleapis.com/drive/v3/files/${FILE_ID}?alt=media&supportsAllDrives=true&includeItemsFromAllDrives=true" \
40
+ -H "Authorization: Bearer ${ACCESS_TOKEN}")
41
+
42
+ if [ "$HTTP_CODE" -ne 200 ]; then
43
+ echo "Error: Download failed with HTTP $HTTP_CODE" >&2
44
+ cat "$LOCAL_PATH" >&2
45
+ rm -f "$LOCAL_PATH"
46
+ exit 1
47
+ fi
48
+
49
+ FILE_SIZE=$(stat -c%s "$LOCAL_PATH" 2>/dev/null || stat -f%z "$LOCAL_PATH" 2>/dev/null)
50
+ echo "Downloaded to: $LOCAL_PATH ($FILE_SIZE bytes)"
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ FOLDER_ID="${1:?Usage: list.sh <folder_id>}"
5
+
6
+ if [ -z "${GOOGLE_SERVICE_ACCOUNT_JSON:-}" ]; then
7
+ echo "Error: GOOGLE_SERVICE_ACCOUNT_JSON is not set" >&2
8
+ exit 1
9
+ fi
10
+
11
+ # Extract service account email and private key
12
+ SA_EMAIL=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.client_email')
13
+ SA_KEY=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.private_key')
14
+ TOKEN_URI=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.token_uri')
15
+
16
+ # Build JWT
17
+ NOW=$(date +%s)
18
+ EXP=$((NOW + 3600))
19
+ HEADER=$(echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
20
+ CLAIMS=$(printf '{"iss":"%s","scope":"https://www.googleapis.com/auth/drive","aud":"%s","iat":%d,"exp":%d}' \
21
+ "$SA_EMAIL" "$TOKEN_URI" "$NOW" "$EXP" | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
22
+ SIGNING_INPUT="${HEADER}.${CLAIMS}"
23
+ SIGNATURE=$(printf '%s' "$SIGNING_INPUT" | openssl dgst -sha256 -sign <(echo "$SA_KEY") | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
24
+ JWT="${SIGNING_INPUT}.${SIGNATURE}"
25
+
26
+ # Exchange JWT for access token
27
+ ACCESS_TOKEN=$(curl -s -X POST "$TOKEN_URI" \
28
+ -H "Content-Type: application/x-www-form-urlencoded" \
29
+ -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${JWT}" | jq -r '.access_token')
30
+
31
+ if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
32
+ echo "Error: Failed to obtain access token" >&2
33
+ exit 1
34
+ fi
35
+
36
+ # List files
37
+ RESPONSE=$(curl -s -X GET \
38
+ "https://www.googleapis.com/drive/v3/files?q='${FOLDER_ID}'+in+parents&supportsAllDrives=true&includeItemsFromAllDrives=true&corpora=allDrives&fields=files(id,name,mimeType,size,modifiedTime)" \
39
+ -H "Authorization: Bearer ${ACCESS_TOKEN}")
40
+
41
+ echo "$RESPONSE" | jq -r '.files[] | "\(.id)\t\(.name)\t\(.mimeType)\t\(.size // "N/A")\t\(.modifiedTime)"' 2>/dev/null || echo "$RESPONSE"
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ LOCAL_FILE="${1:?Usage: upload.sh <local_file> <parent_folder_id> <filename>}"
5
+ PARENT_FOLDER_ID="${2:?Usage: upload.sh <local_file> <parent_folder_id> <filename>}"
6
+ FILENAME="${3:?Usage: upload.sh <local_file> <parent_folder_id> <filename>}"
7
+
8
+ if [ ! -f "$LOCAL_FILE" ]; then
9
+ echo "Error: File not found: $LOCAL_FILE" >&2
10
+ exit 1
11
+ fi
12
+
13
+ if [ -z "${GOOGLE_SERVICE_ACCOUNT_JSON:-}" ]; then
14
+ echo "Error: GOOGLE_SERVICE_ACCOUNT_JSON is not set" >&2
15
+ exit 1
16
+ fi
17
+
18
+ # Extract service account email and private key
19
+ SA_EMAIL=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.client_email')
20
+ SA_KEY=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.private_key')
21
+ TOKEN_URI=$(echo "$GOOGLE_SERVICE_ACCOUNT_JSON" | jq -r '.token_uri')
22
+
23
+ # Build JWT
24
+ NOW=$(date +%s)
25
+ EXP=$((NOW + 3600))
26
+ HEADER=$(echo -n '{"alg":"RS256","typ":"JWT"}' | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
27
+ CLAIMS=$(printf '{"iss":"%s","scope":"https://www.googleapis.com/auth/drive","aud":"%s","iat":%d,"exp":%d}' \
28
+ "$SA_EMAIL" "$TOKEN_URI" "$NOW" "$EXP" | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
29
+ SIGNING_INPUT="${HEADER}.${CLAIMS}"
30
+ SIGNATURE=$(printf '%s' "$SIGNING_INPUT" | openssl dgst -sha256 -sign <(echo "$SA_KEY") | openssl base64 -e -A | tr '+/' '-_' | tr -d '=')
31
+ JWT="${SIGNING_INPUT}.${SIGNATURE}"
32
+
33
+ # Exchange JWT for access token
34
+ ACCESS_TOKEN=$(curl -s -X POST "$TOKEN_URI" \
35
+ -H "Content-Type: application/x-www-form-urlencoded" \
36
+ -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${JWT}" | jq -r '.access_token')
37
+
38
+ if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
39
+ echo "Error: Failed to obtain access token" >&2
40
+ exit 1
41
+ fi
42
+
43
+ # Detect MIME type from extension
44
+ EXT="${LOCAL_FILE##*.}"
45
+ case "${EXT,,}" in
46
+ jpg|jpeg) MIME_TYPE="image/jpeg" ;;
47
+ png) MIME_TYPE="image/png" ;;
48
+ gif) MIME_TYPE="image/gif" ;;
49
+ webp) MIME_TYPE="image/webp" ;;
50
+ mp4) MIME_TYPE="video/mp4" ;;
51
+ webm) MIME_TYPE="video/webm" ;;
52
+ pdf) MIME_TYPE="application/pdf" ;;
53
+ txt) MIME_TYPE="text/plain" ;;
54
+ json) MIME_TYPE="application/json" ;;
55
+ csv) MIME_TYPE="text/csv" ;;
56
+ *) MIME_TYPE="application/octet-stream" ;;
57
+ esac
58
+
59
+ # Upload file using multipart upload
60
+ METADATA=$(printf '{"name":"%s","parents":["%s"]}' "$FILENAME" "$PARENT_FOLDER_ID")
61
+
62
+ RESPONSE=$(curl -s -X POST \
63
+ "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true&includeItemsFromAllDrives=true" \
64
+ -H "Authorization: Bearer ${ACCESS_TOKEN}" \
65
+ -F "metadata=${METADATA};type=application/json" \
66
+ -F "file=@${LOCAL_FILE};type=${MIME_TYPE}")
67
+
68
+ FILE_ID=$(echo "$RESPONSE" | jq -r '.id')
69
+ if [ -z "$FILE_ID" ] || [ "$FILE_ID" = "null" ]; then
70
+ echo "Error: Upload failed" >&2
71
+ echo "$RESPONSE" >&2
72
+ exit 1
73
+ fi
74
+
75
+ echo "Uploaded: $FILENAME"
76
+ echo "File ID: $FILE_ID"
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: kie-ai
3
+ description: "Generate images and videos using kie.ai API. Use for AI image and video generation tasks."
4
+ ---
5
+
6
+ # KIE AI
7
+
8
+ Generate images and videos using the kie.ai API.
9
+
10
+ ## Environment Variables
11
+
12
+ - `KIE_AI_API_KEY` — API key for kie.ai
13
+
14
+ ## Commands
15
+
16
+ ### Generate an image
17
+
18
+ ```bash
19
+ skills/kie-ai/generate-image.sh <prompt> [aspect_ratio] [resolution] [output_format]
20
+ ```
21
+
22
+ - `prompt` (required) — Text prompt for image generation
23
+ - `aspect_ratio` (optional, default: `auto`) — One of: `auto, 1:1, 1:4, 16:9, 1:8, 21:9, 2:3, 3:2, 3:4, 4:1, 4:3, 4:5, 5:4, 8:1, 9:16`
24
+ - `resolution` (optional, default: `1K`) — One of: `1K, 2K, 4K`
25
+ - `output_format` (optional, default: `jpg`) — One of: `jpg, png`
26
+
27
+ Downloads the generated image to a local file and prints the path.
28
+
29
+ ### Generate a video
30
+
31
+ ```bash
32
+ skills/kie-ai/generate-video.sh <prompt> [aspect_ratio]
33
+ ```
34
+
35
+ - `prompt` (required) — Text prompt for video generation
36
+ - `aspect_ratio` (optional, default: `16:9`) — One of: `16:9, 9:16, Auto`
37
+
38
+ Downloads the generated video to a local file and prints the path.
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ PROMPT="${1:?Usage: generate-image.sh <prompt> [aspect_ratio] [resolution] [output_format]}"
5
+ ASPECT_RATIO="${2:-auto}"
6
+ RESOLUTION="${3:-1K}"
7
+ OUTPUT_FORMAT="${4:-jpg}"
8
+
9
+ if [ -z "${KIE_AI_API_KEY:-}" ]; then
10
+ echo "Error: KIE_AI_API_KEY is not set" >&2
11
+ exit 1
12
+ fi
13
+
14
+ # Create task
15
+ BODY=$(jq -n \
16
+ --arg prompt "$PROMPT" \
17
+ --arg ar "$ASPECT_RATIO" \
18
+ --arg res "$RESOLUTION" \
19
+ --arg fmt "$OUTPUT_FORMAT" \
20
+ '{
21
+ model: "nano-banana-2",
22
+ input: {
23
+ prompt: $prompt,
24
+ aspect_ratio: $ar,
25
+ resolution: $res,
26
+ output_format: $fmt
27
+ }
28
+ }')
29
+
30
+ echo "Creating image generation task..." >&2
31
+ CREATE_RESPONSE=$(curl -s -X POST \
32
+ "https://api.kie.ai/api/v1/jobs/createTask" \
33
+ -H "Authorization: Bearer ${KIE_AI_API_KEY}" \
34
+ -H "Content-Type: application/json" \
35
+ -d "$BODY")
36
+
37
+ TASK_ID=$(echo "$CREATE_RESPONSE" | jq -r '.data.taskId // .taskId // empty')
38
+ if [ -z "$TASK_ID" ]; then
39
+ echo "Error: Failed to create task" >&2
40
+ echo "$CREATE_RESPONSE" >&2
41
+ exit 1
42
+ fi
43
+
44
+ echo "Task ID: $TASK_ID — polling for completion..." >&2
45
+
46
+ # Poll for completion
47
+ while true; do
48
+ sleep 15
49
+ STATUS_RESPONSE=$(curl -s -X GET \
50
+ "https://api.kie.ai/api/v1/jobs/recordInfo?taskId=${TASK_ID}" \
51
+ -H "Authorization: Bearer ${KIE_AI_API_KEY}" \
52
+ -H "Content-Type: application/json")
53
+
54
+ STATE=$(echo "$STATUS_RESPONSE" | jq -r '.data.state // empty')
55
+ echo "State: $STATE" >&2
56
+
57
+ if [ "$STATE" = "success" ]; then
58
+ # .data.resultJson is a JSON string — parse it, then get .resultUrls[0]
59
+ IMAGE_URL=$(echo "$STATUS_RESPONSE" | jq -r '.data.resultJson' | jq -r '.resultUrls[0]')
60
+ if [ -z "$IMAGE_URL" ] || [ "$IMAGE_URL" = "null" ]; then
61
+ echo "Error: Could not extract image URL" >&2
62
+ echo "$STATUS_RESPONSE" >&2
63
+ exit 1
64
+ fi
65
+
66
+ # Download
67
+ OUTPUT_FILE="/tmp/kie-image-${TASK_ID}.${OUTPUT_FORMAT}"
68
+ curl -s -o "$OUTPUT_FILE" "$IMAGE_URL"
69
+ FILE_SIZE=$(stat -c%s "$OUTPUT_FILE" 2>/dev/null || stat -f%z "$OUTPUT_FILE" 2>/dev/null)
70
+ echo "Image downloaded: $OUTPUT_FILE ($FILE_SIZE bytes)"
71
+ exit 0
72
+ elif [ "$STATE" = "failed" ] || [ "$STATE" = "error" ]; then
73
+ echo "Error: Image generation failed" >&2
74
+ echo "$STATUS_RESPONSE" >&2
75
+ exit 1
76
+ fi
77
+ done
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ PROMPT="${1:?Usage: generate-video.sh <prompt> [aspect_ratio]}"
5
+ ASPECT_RATIO="${2:-16:9}"
6
+
7
+ if [ -z "${KIE_AI_API_KEY:-}" ]; then
8
+ echo "Error: KIE_AI_API_KEY is not set" >&2
9
+ exit 1
10
+ fi
11
+
12
+ # Create task
13
+ BODY=$(jq -n \
14
+ --arg prompt "$PROMPT" \
15
+ --arg ar "$ASPECT_RATIO" \
16
+ '{
17
+ prompt: $prompt,
18
+ model: "veo3_fast",
19
+ generationType: "TEXT_2_VIDEO",
20
+ aspect_ratio: $ar
21
+ }')
22
+
23
+ echo "Creating video generation task..." >&2
24
+ CREATE_RESPONSE=$(curl -s -X POST \
25
+ "https://api.kie.ai/api/v1/veo/generate" \
26
+ -H "Authorization: Bearer ${KIE_AI_API_KEY}" \
27
+ -H "Content-Type: application/json" \
28
+ -d "$BODY")
29
+
30
+ TASK_ID=$(echo "$CREATE_RESPONSE" | jq -r '.data.taskId // .taskId // empty')
31
+ if [ -z "$TASK_ID" ]; then
32
+ echo "Error: Failed to create task" >&2
33
+ echo "$CREATE_RESPONSE" >&2
34
+ exit 1
35
+ fi
36
+
37
+ echo "Task ID: $TASK_ID — polling for completion..." >&2
38
+
39
+ # Poll for completion
40
+ while true; do
41
+ sleep 15
42
+ STATUS_RESPONSE=$(curl -s -X GET \
43
+ "https://api.kie.ai/api/v1/veo/record-info?taskId=${TASK_ID}" \
44
+ -H "Authorization: Bearer ${KIE_AI_API_KEY}" \
45
+ -H "Content-Type: application/json")
46
+
47
+ SUCCESS_FLAG=$(echo "$STATUS_RESPONSE" | jq -r '.data.successFlag // empty')
48
+ echo "Status check... successFlag=$SUCCESS_FLAG" >&2
49
+
50
+ if [ "$SUCCESS_FLAG" = "1" ]; then
51
+ VIDEO_URL=$(echo "$STATUS_RESPONSE" | jq -r '.data.response.resultUrls[0]')
52
+ if [ -z "$VIDEO_URL" ] || [ "$VIDEO_URL" = "null" ]; then
53
+ echo "Error: Could not extract video URL" >&2
54
+ echo "$STATUS_RESPONSE" >&2
55
+ exit 1
56
+ fi
57
+
58
+ # Download
59
+ OUTPUT_FILE="/tmp/kie-video-${TASK_ID}.mp4"
60
+ curl -s -o "$OUTPUT_FILE" "$VIDEO_URL"
61
+ FILE_SIZE=$(stat -c%s "$OUTPUT_FILE" 2>/dev/null || stat -f%z "$OUTPUT_FILE" 2>/dev/null)
62
+ echo "Video downloaded: $OUTPUT_FILE ($FILE_SIZE bytes)"
63
+ exit 0
64
+ elif [ "$SUCCESS_FLAG" = "-1" ]; then
65
+ echo "Error: Video generation failed" >&2
66
+ echo "$STATUS_RESPONSE" >&2
67
+ exit 1
68
+ fi
69
+ done
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: llm-secrets
3
+ description: List available LLM-accessible credentials. Use when you need API keys, passwords, or other secrets that have been made available to you.
4
+ ---
5
+
6
+ # List Available Secrets
7
+
8
+ ```bash
9
+ skills/llm-secrets/llm-secrets.js
10
+ ```
11
+
12
+ Shows the names of available secret keys (not values). Output example:
13
+
14
+ ```
15
+ Available secrets:
16
+ - BROWSER_PASSWORD
17
+ - SOME_API_KEY
18
+
19
+ To get a value: echo $KEY_NAME
20
+ ```
21
+
22
+ ## Get a Secret Value
23
+
24
+ ```bash
25
+ echo $KEY_NAME
26
+ ```
27
+
28
+ Replace `KEY_NAME` with one of the available secret names.
29
+
30
+ ## When to Use
31
+
32
+ - When a skill or tool needs authentication credentials
33
+ - When logging into a website via browser tools
34
+ - When calling an external API that requires a key
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * llm-secrets.js - List available LLM-accessible secret keys
5
+ *
6
+ * Usage: llm-secrets.js
7
+ *
8
+ * Lists the key names from LLM_SECRETS (not the values).
9
+ * To get a value, use: echo $KEY_NAME
10
+ */
11
+
12
+ const secretsJson = process.env.LLM_SECRETS;
13
+
14
+ if (!secretsJson) {
15
+ console.log('No LLM_SECRETS configured.');
16
+ process.exit(0);
17
+ }
18
+
19
+ try {
20
+ const parsed = JSON.parse(secretsJson);
21
+ const keys = Object.keys(parsed);
22
+
23
+ if (keys.length === 0) {
24
+ console.log('LLM_SECRETS is empty.');
25
+ } else {
26
+ console.log('Available secrets:');
27
+ keys.forEach(key => console.log(` - ${key}`));
28
+ console.log('\nTo get a value: echo $KEY_NAME');
29
+ }
30
+ } catch (e) {
31
+ console.error('Error parsing LLM_SECRETS:', e.message);
32
+ process.exit(1);
33
+ }
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: modify-self
3
+ description: Use when a job requires modifying the agent's own code, configuration, personality, cron jobs, skills, or operating system files.
4
+ ---
5
+
6
+ # Modify Self
7
+
8
+ Before making any changes, read the project documentation for a full understanding of the system architecture:
9
+
10
+ ```bash
11
+ cat CLAUDE.md
12
+ ```