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
package/bin/cli.js ADDED
@@ -0,0 +1,823 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync, execFileSync } from 'child_process';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { createDirLink } from '../setup/lib/fs-utils.mjs';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ const command = process.argv[2];
13
+ const args = process.argv.slice(3);
14
+
15
+ // Handle --version / -v flag
16
+ if (command === '--version' || command === '-v') {
17
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
18
+ console.log(`gigaclaw v${pkg.version}`);
19
+ process.exit(0);
20
+ }
21
+
22
+ // Files tightly coupled to the package version that are auto-updated by init.
23
+ // These live in the user's project because GitHub/Docker require them at specific paths,
24
+ // but they shouldn't drift from the package version.
25
+ const MANAGED_PATHS = [
26
+ '.github/workflows/',
27
+ 'docker/event-handler/',
28
+ 'docker-compose.yml',
29
+ 'docker-compose.local.yml',
30
+ '.dockerignore',
31
+ 'CLAUDE.md',
32
+ // middleware.js must always be kept in sync with the package template because
33
+ // Next.js / Turbopack requires the `config` export to be a static literal
34
+ // object defined directly in this file — it cannot be re-exported from a
35
+ // module. Keeping it managed ensures users always get the correct pattern.
36
+ 'middleware.js',
37
+ ];
38
+
39
+ // Files that are only relevant in cloud mode (GitHub + ngrok + Telegram).
40
+ // In local mode these are skipped during scaffolding to keep the project clean.
41
+ const CLOUD_ONLY_PATHS = [
42
+ '.github/workflows/',
43
+ ];
44
+
45
+ function isManaged(relPath) {
46
+ return MANAGED_PATHS.some(p => relPath === p || relPath.startsWith(p));
47
+ }
48
+
49
+ // Files that must never be scaffolded directly (use .template suffix instead).
50
+ const EXCLUDED_FILENAMES = ['CLAUDE.md'];
51
+
52
+ // Files ending in .template are scaffolded with the suffix stripped.
53
+ // e.g. .gitignore.template → .gitignore, CLAUDE.md.template → CLAUDE.md
54
+ function destPath(templateRelPath) {
55
+ if (templateRelPath.endsWith('.template')) {
56
+ return templateRelPath.slice(0, -'.template'.length);
57
+ }
58
+ return templateRelPath;
59
+ }
60
+
61
+ function templatePath(userPath, templatesDir) {
62
+ const withSuffix = userPath + '.template';
63
+ if (fs.existsSync(path.join(templatesDir, withSuffix))) {
64
+ return withSuffix;
65
+ }
66
+ return userPath;
67
+ }
68
+
69
+ /**
70
+ * Parse upgrade target from CLI arg into an npm install specifier.
71
+ * Examples: undefined → "latest", "@beta" → "beta", "@rc" → "rc", "1.2.72" → "1.2.72"
72
+ */
73
+ function parseUpgradeTarget(arg) {
74
+ if (!arg) return 'latest';
75
+ if (arg.startsWith('@')) return arg.slice(1); // @beta → beta, @rc → rc, @latest → latest
76
+ return arg; // bare version like 1.2.72
77
+ }
78
+
79
+ function printUsage() {
80
+ console.log(`
81
+ Usage: gigaclaw <command>
82
+
83
+ Commands:
84
+ init Scaffold a new gigaclaw project
85
+ upgrade|update [@beta|version] Upgrade gigaclaw (install, init, build, commit, push)
86
+ setup Run interactive setup wizard
87
+ setup-telegram Reconfigure Telegram webhook
88
+ reset-auth Regenerate AUTH_SECRET (invalidates all sessions)
89
+ reset [file] Restore a template file (or list available templates)
90
+ diff [file] Show differences between project files and package templates
91
+ set-agent-secret <KEY> [VALUE] Set a GitHub secret with AGENT_ prefix (also updates .env)
92
+ set-agent-llm-secret <KEY> [VALUE] Set a GitHub secret with AGENT_LLM_ prefix
93
+ set-var <KEY> [VALUE] Set a GitHub repository variable
94
+ --version, -v Show gigaclaw version
95
+
96
+ Powered by Gignaati — https://www.gignaati.com
97
+ `);
98
+ }
99
+
100
+ /**
101
+ * Collect all template files as relative paths.
102
+ */
103
+ function getTemplateFiles(templatesDir) {
104
+ const files = [];
105
+ function walk(dir) {
106
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
107
+ for (const entry of entries) {
108
+ const fullPath = path.join(dir, entry.name);
109
+ if (entry.isDirectory()) {
110
+ walk(fullPath);
111
+ } else if (!EXCLUDED_FILENAMES.includes(entry.name)) {
112
+ files.push(path.relative(templatesDir, fullPath));
113
+ }
114
+ }
115
+ }
116
+ walk(templatesDir);
117
+ return files;
118
+ }
119
+
120
+ async function init() {
121
+ let cwd = process.cwd();
122
+ const packageDir = path.join(__dirname, '..');
123
+ const templatesDir = path.join(packageDir, 'templates');
124
+ const noManaged = args.includes('--no-managed');
125
+
126
+ // Guard: warn if the directory is not empty (unless it's an existing gigaclaw project)
127
+ const entries = fs.readdirSync(cwd);
128
+ if (entries.length > 0) {
129
+ const pkgPath = path.join(cwd, 'package.json');
130
+ let isExistingProject = false;
131
+ if (fs.existsSync(pkgPath)) {
132
+ try {
133
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
134
+ const deps = pkg.dependencies || {};
135
+ const devDeps = pkg.devDependencies || {};
136
+ if (deps.gigaclaw || devDeps.gigaclaw) {
137
+ isExistingProject = true;
138
+ }
139
+ } catch {}
140
+ }
141
+
142
+ if (!isExistingProject) {
143
+ console.log('\nThis directory is not empty.');
144
+ const { text, isCancel } = await import('@clack/prompts');
145
+ const dirName = await text({
146
+ message: 'Project directory name:',
147
+ defaultValue: 'my-gigaclaw',
148
+ });
149
+ if (isCancel(dirName)) {
150
+ console.log('\nCancelled.\n');
151
+ process.exit(0);
152
+ }
153
+ const newDir = path.resolve(cwd, dirName);
154
+ fs.mkdirSync(newDir, { recursive: true });
155
+ process.chdir(newDir);
156
+ cwd = newDir;
157
+ console.log(`\nCreated ${dirName}/`);
158
+ }
159
+ }
160
+
161
+ console.log('\nScaffolding gigaclaw project...\n');
162
+
163
+ const templateFiles = getTemplateFiles(templatesDir);
164
+ const created = [];
165
+ const skipped = [];
166
+ const changed = [];
167
+ const updated = [];
168
+
169
+ // Detect mode from existing .env (if any) so re-running init respects the chosen mode
170
+ const existingEnvPath = path.join(cwd, '.env');
171
+ let gigaclawMode = 'cloud';
172
+ if (fs.existsSync(existingEnvPath)) {
173
+ const envContent = fs.readFileSync(existingEnvPath, 'utf-8');
174
+ const modeMatch = envContent.match(/^GIGACLAW_MODE=(.*)$/m);
175
+ if (modeMatch && modeMatch[1].trim() === 'local') gigaclawMode = 'local';
176
+ }
177
+
178
+ for (const relPath of templateFiles) {
179
+ const src = path.join(templatesDir, relPath);
180
+ const outPath = destPath(relPath);
181
+ const dest = path.join(cwd, outPath);
182
+
183
+ // In local mode, skip cloud-only files (GitHub Actions workflows etc.)
184
+ if (gigaclawMode === 'local' && CLOUD_ONLY_PATHS.some(p => outPath === p || outPath.startsWith(p))) {
185
+ continue;
186
+ }
187
+
188
+ if (!fs.existsSync(dest)) {
189
+ // File doesn't exist — create it
190
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
191
+ fs.copyFileSync(src, dest);
192
+ created.push(outPath);
193
+ console.log(` Created ${outPath}`);
194
+ } else {
195
+ // File exists — check if template has changed
196
+ const srcContent = fs.readFileSync(src);
197
+ const destContent = fs.readFileSync(dest);
198
+ if (srcContent.equals(destContent)) {
199
+ skipped.push(outPath);
200
+ } else if (!noManaged && isManaged(outPath)) {
201
+ // Managed file differs — auto-update to match package
202
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
203
+ fs.copyFileSync(src, dest);
204
+ updated.push(outPath);
205
+ console.log(` Updated ${outPath}`);
206
+ } else {
207
+ changed.push(outPath);
208
+ console.log(` Skipped ${outPath} (already exists)`);
209
+ }
210
+ }
211
+ }
212
+
213
+ // Create package.json if it doesn't exist
214
+ const pkgPath = path.join(cwd, 'package.json');
215
+ if (!fs.existsSync(pkgPath)) {
216
+ const dirName = path.basename(cwd);
217
+ const { version } = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'));
218
+ // Use the exact current version as the minimum — not ^1.0.0 which would
219
+ // resolve to the oldest published version and miss all recent bug fixes.
220
+ const gigaclawDep = version.includes('-') ? version : `^${version}`;
221
+ const pkg = {
222
+ name: dirName,
223
+ private: true,
224
+ scripts: {
225
+ dev: 'next dev --turbopack',
226
+ build: 'next build',
227
+ start: 'next start',
228
+ setup: 'gigaclaw setup',
229
+ 'setup-telegram': 'gigaclaw setup-telegram',
230
+ 'reset-auth': 'gigaclaw reset-auth',
231
+ },
232
+ dependencies: {
233
+ gigaclaw: gigaclawDep,
234
+ next: '^15.5.12',
235
+ 'next-auth': '5.0.0-beta.30',
236
+ 'next-themes': '^0.4.0',
237
+ react: '^19.0.0',
238
+ 'react-dom': '^19.0.0',
239
+ tailwindcss: '^4.0.0',
240
+ '@tailwindcss/postcss': '^4.0.0',
241
+ },
242
+ };
243
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
244
+ console.log(' Created package.json');
245
+ } else {
246
+ console.log(' Skipped package.json (already exists)');
247
+ }
248
+
249
+ // Create .gitkeep files for empty dirs
250
+ const gitkeepDirs = ['cron', 'triggers', 'logs', 'tmp', 'data'];
251
+ for (const dir of gitkeepDirs) {
252
+ const gitkeep = path.join(cwd, dir, '.gitkeep');
253
+ if (!fs.existsSync(gitkeep)) {
254
+ fs.mkdirSync(path.join(cwd, dir), { recursive: true });
255
+ fs.writeFileSync(gitkeep, '');
256
+ }
257
+ }
258
+
259
+ // Create default skill activation symlinks
260
+ const defaultSkills = ['browser-tools', 'llm-secrets', 'modify-self'];
261
+ const activeDir = path.join(cwd, 'skills', 'active');
262
+ fs.mkdirSync(activeDir, { recursive: true });
263
+ for (const skill of defaultSkills) {
264
+ const symlink = path.join(activeDir, skill);
265
+ if (!fs.existsSync(symlink)) {
266
+ createDirLink(`../${skill}`, symlink);
267
+ console.log(` Created skills/active/${skill} → ../${skill}`);
268
+ }
269
+ }
270
+
271
+ // Create .pi/skills → ../skills/active symlink
272
+ const piSkillsLink = path.join(cwd, '.pi', 'skills');
273
+ if (!fs.existsSync(piSkillsLink)) {
274
+ fs.mkdirSync(path.dirname(piSkillsLink), { recursive: true });
275
+ createDirLink('../skills/active', piSkillsLink);
276
+ console.log(' Created .pi/skills → ../skills/active');
277
+ }
278
+
279
+ // Create .claude/skills → ../skills/active symlink
280
+ const claudeSkillsLink = path.join(cwd, '.claude', 'skills');
281
+ if (!fs.existsSync(claudeSkillsLink)) {
282
+ fs.mkdirSync(path.dirname(claudeSkillsLink), { recursive: true });
283
+ createDirLink('../skills/active', claudeSkillsLink);
284
+ console.log(' Created .claude/skills → ../skills/active');
285
+ }
286
+
287
+ // Report updated managed files
288
+ if (updated.length > 0) {
289
+ console.log('\n Updated managed files:');
290
+ for (const file of updated) {
291
+ console.log(` ${file}`);
292
+ }
293
+ }
294
+
295
+ // Report changed templates
296
+ if (changed.length > 0) {
297
+ console.log('\n Updated templates available:');
298
+ console.log(' These files differ from the current package templates.');
299
+ console.log(' This may be from your edits, or from a gigaclaw update.\n');
300
+ for (const file of changed) {
301
+ console.log(` ${file}`);
302
+ }
303
+ console.log('\n To view differences: npx gigaclaw diff <file>');
304
+ console.log(' To reset to default: npx gigaclaw reset <file>');
305
+ }
306
+
307
+ // Run npm install
308
+ console.log('\nInstalling dependencies...\n');
309
+ // shell:true is required on Windows so npm resolves via PATH (npm.cmd)
310
+ execSync('npm install', { stdio: 'inherit', cwd, shell: true });
311
+
312
+ // Create or update .env with auto-generated infrastructure values
313
+ const envPath = path.join(cwd, '.env');
314
+ const { randomBytes } = await import('crypto');
315
+ const gigaclawPkg = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8'));
316
+ const version = gigaclawPkg.version;
317
+
318
+ if (!fs.existsSync(envPath)) {
319
+ // Seed .env for new projects
320
+ // base64url avoids +, /, and = chars that break dotenv parsing on Windows
321
+ const authSecret = randomBytes(32).toString('base64url');
322
+ const seedEnv = `# gigaclaw Configuration
323
+ # Run "npm run setup" to complete configuration
324
+
325
+ AUTH_SECRET=${authSecret}
326
+ AUTH_TRUST_HOST=true
327
+ GIGACLAW_VERSION=${version}
328
+ `;
329
+ fs.writeFileSync(envPath, seedEnv);
330
+ console.log(` Created .env (AUTH_SECRET, GIGACLAW_VERSION=${version})`);
331
+ } else {
332
+ // Update GIGACLAW_VERSION in existing .env
333
+ try {
334
+ let envContent = fs.readFileSync(envPath, 'utf8');
335
+ if (envContent.match(/^GIGACLAW_VERSION=.*/m)) {
336
+ envContent = envContent.replace(/^GIGACLAW_VERSION=.*/m, `GIGACLAW_VERSION=${version}`);
337
+ } else {
338
+ envContent = envContent.trimEnd() + `\nGIGACLAW_VERSION=${version}\n`;
339
+ }
340
+ fs.writeFileSync(envPath, envContent);
341
+ console.log(` Updated GIGACLAW_VERSION to ${version}`);
342
+ } catch {}
343
+ }
344
+
345
+ console.log('\nDone! Run: npm run setup\n');
346
+ }
347
+
348
+ /**
349
+ * List all available template files, or restore a specific one.
350
+ */
351
+ function reset(filePath) {
352
+ const packageDir = path.join(__dirname, '..');
353
+ const templatesDir = path.join(packageDir, 'templates');
354
+ const cwd = process.cwd();
355
+
356
+ if (!filePath) {
357
+ console.log('\nAvailable template files:\n');
358
+ const files = getTemplateFiles(templatesDir);
359
+ for (const file of files) {
360
+ console.log(` ${destPath(file)}`);
361
+ }
362
+ console.log('\nUsage: gigaclaw reset <file>');
363
+ console.log('Example: gigaclaw reset config/SOUL.md\n');
364
+ return;
365
+ }
366
+
367
+ const tmplPath = templatePath(filePath, templatesDir);
368
+ const src = path.join(templatesDir, tmplPath);
369
+ const dest = path.join(cwd, filePath);
370
+
371
+ if (!fs.existsSync(src)) {
372
+ console.error(`\nTemplate not found: ${filePath}`);
373
+ console.log('Run "gigaclaw reset" to see available templates.\n');
374
+ process.exit(1);
375
+ }
376
+
377
+ if (fs.statSync(src).isDirectory()) {
378
+ console.log(`\nRestoring ${filePath}/...\n`);
379
+ copyDirSyncForce(src, dest, tmplPath);
380
+ } else {
381
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
382
+ fs.copyFileSync(src, dest);
383
+ console.log(`\nRestored ${filePath}\n`);
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Show the diff between a user's file and the package template.
389
+ */
390
+ function diff(filePath) {
391
+ const packageDir = path.join(__dirname, '..');
392
+ const templatesDir = path.join(packageDir, 'templates');
393
+ const cwd = process.cwd();
394
+
395
+ if (!filePath) {
396
+ // Show all files that differ
397
+ console.log('\nFiles that differ from package templates:\n');
398
+ const files = getTemplateFiles(templatesDir);
399
+ let anyDiff = false;
400
+ for (const file of files) {
401
+ const src = path.join(templatesDir, file);
402
+ const outPath = destPath(file);
403
+ const dest = path.join(cwd, outPath);
404
+ if (fs.existsSync(dest)) {
405
+ const srcContent = fs.readFileSync(src);
406
+ const destContent = fs.readFileSync(dest);
407
+ if (!srcContent.equals(destContent)) {
408
+ console.log(` ${outPath}`);
409
+ anyDiff = true;
410
+ }
411
+ } else {
412
+ console.log(` ${outPath} (missing)`);
413
+ anyDiff = true;
414
+ }
415
+ }
416
+ if (!anyDiff) {
417
+ console.log(' All files match package templates.');
418
+ }
419
+ console.log('\nUsage: gigaclaw diff <file>');
420
+ console.log('Example: gigaclaw diff config/SOUL.md\n');
421
+ return;
422
+ }
423
+
424
+ const tmplPath = templatePath(filePath, templatesDir);
425
+ const src = path.join(templatesDir, tmplPath);
426
+ const dest = path.join(cwd, filePath);
427
+
428
+ if (!fs.existsSync(src)) {
429
+ console.error(`\nTemplate not found: ${filePath}`);
430
+ process.exit(1);
431
+ }
432
+
433
+ if (!fs.existsSync(dest)) {
434
+ console.log(`\n${filePath} does not exist in your project.`);
435
+ console.log(`Run "gigaclaw reset ${filePath}" to create it.\n`);
436
+ return;
437
+ }
438
+
439
+ try {
440
+ // Use git diff for nice colored output, fall back to plain diff
441
+ execSync(`git diff --no-index -- "${dest}" "${src}"`, { stdio: 'inherit', shell: true });
442
+ console.log('\nFiles are identical.\n');
443
+ } catch (e) {
444
+ // git diff exits with 1 when files differ (output already printed)
445
+ console.log(`\n To reset: gigaclaw reset ${filePath}\n`);
446
+ }
447
+ }
448
+
449
+ function copyDirSyncForce(src, dest, templateRelBase = '') {
450
+ fs.mkdirSync(dest, { recursive: true });
451
+ const entries = fs.readdirSync(src, { withFileTypes: true });
452
+ for (const entry of entries) {
453
+ if (EXCLUDED_FILENAMES.includes(entry.name)) continue;
454
+ const srcPath = path.join(src, entry.name);
455
+ const templateRel = templateRelBase
456
+ ? path.join(templateRelBase, entry.name)
457
+ : entry.name;
458
+ const outName = path.basename(destPath(templateRel));
459
+ const destFile = path.join(dest, outName);
460
+ if (entry.isDirectory()) {
461
+ copyDirSyncForce(srcPath, destFile, templateRel);
462
+ } else {
463
+ fs.copyFileSync(srcPath, destFile);
464
+ console.log(` Restored ${path.relative(process.cwd(), destFile)}`);
465
+ }
466
+ }
467
+ }
468
+
469
+ function setup() {
470
+ const setupScript = path.join(__dirname, '..', 'setup', 'setup.mjs');
471
+ try {
472
+ execFileSync(process.execPath, [setupScript], { stdio: 'inherit', cwd: process.cwd() });
473
+ } catch {
474
+ process.exit(1);
475
+ }
476
+ }
477
+
478
+ function setupTelegram() {
479
+ const setupScript = path.join(__dirname, '..', 'setup', 'setup-telegram.mjs');
480
+ try {
481
+ execFileSync(process.execPath, [setupScript], { stdio: 'inherit', cwd: process.cwd() });
482
+ } catch {
483
+ process.exit(1);
484
+ }
485
+ }
486
+
487
+ async function resetAuth() {
488
+ const { randomBytes } = await import('crypto');
489
+ const { updateEnvVariable } = await import(path.join(__dirname, '..', 'setup', 'lib', 'auth.mjs'));
490
+
491
+ const envPath = path.join(process.cwd(), '.env');
492
+ if (!fs.existsSync(envPath)) {
493
+ console.error('\n No .env file found. Run "npm run setup" first.\n');
494
+ process.exit(1);
495
+ }
496
+
497
+ // base64url avoids +, /, and = chars that break dotenv parsing on Windows
498
+ const newSecret = randomBytes(32).toString('base64url');
499
+ updateEnvVariable('AUTH_SECRET', newSecret);
500
+ console.log('\n AUTH_SECRET regenerated.');
501
+ console.log(' All existing sessions have been invalidated.');
502
+ console.log(' Restart your server for the change to take effect.\n');
503
+ }
504
+
505
+ async function upgrade() {
506
+ const cwd = process.cwd();
507
+ const tag = parseUpgradeTarget(args[0]);
508
+ const { confirm, isCancel } = await import('@clack/prompts');
509
+
510
+ // --- Pre-flight: verify this is a gigaclaw project ---
511
+ const pkgPath = path.join(cwd, 'package.json');
512
+ if (!fs.existsSync(pkgPath)) {
513
+ console.error('\n Not a gigaclaw project (no package.json found).\n');
514
+ process.exit(1);
515
+ }
516
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
517
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
518
+ if (!deps.gigaclaw) {
519
+ console.error('\n Not a gigaclaw project (gigaclaw not in dependencies).\n');
520
+ process.exit(1);
521
+ }
522
+
523
+ // Get current installed version
524
+ let currentVersion;
525
+ try {
526
+ const installedPkg = path.join(cwd, 'node_modules', 'gigaclaw', 'package.json');
527
+ currentVersion = JSON.parse(fs.readFileSync(installedPkg, 'utf8')).version;
528
+ } catch {
529
+ currentVersion = 'unknown';
530
+ }
531
+
532
+ // Resolve target version
533
+ let targetVersion;
534
+ try {
535
+ targetVersion = execSync(`npm view gigaclaw@${tag} version`, { encoding: 'utf8', shell: true }).trim();
536
+ } catch {
537
+ console.error(`\n Could not resolve gigaclaw@${tag}. Check the version/tag and try again.\n`);
538
+ process.exit(1);
539
+ }
540
+
541
+ console.log(`\n gigaclaw ${currentVersion} → ${targetVersion}`);
542
+
543
+ if (currentVersion === targetVersion) {
544
+ console.log(' Already up to date. Nothing to do.\n');
545
+ return;
546
+ }
547
+
548
+ // --- Save any local changes ---
549
+ const status = execSync('git status --porcelain', { encoding: 'utf8', cwd, shell: true }).trim();
550
+ if (status) {
551
+ console.log('\n You have local changes. Saving them before upgrading...\n');
552
+ try {
553
+ execSync('git add -A && git commit -m "save local changes before gigaclaw upgrade"', { stdio: 'inherit', cwd, shell: true });
554
+ } catch {
555
+ console.error('\n Could not save your local changes. Please try again.\n');
556
+ return;
557
+ }
558
+ }
559
+
560
+ // --- Pull remote changes ---
561
+ console.log('\n Syncing with remote...\n');
562
+ try {
563
+ execSync('git pull --rebase', { stdio: 'inherit', cwd, shell: true });
564
+ } catch {
565
+ console.error('\n Your local changes conflict with changes on GitHub.');
566
+ console.error(' This means someone (or your bot) changed the same files you did.\n');
567
+ console.error(' To fix this:');
568
+ console.error(' 1. Open the files listed above and look for <<<<<<< markers');
569
+ console.error(' 2. Edit each file to keep the version you want');
570
+ console.error(' 3. Run: git add -A && git rebase --continue');
571
+ console.error(' 4. Then run the upgrade again\n');
572
+ return;
573
+ }
574
+
575
+ // --- Install ---
576
+ console.log(`\n Installing gigaclaw@${targetVersion}...\n`);
577
+ try {
578
+ execSync(`npm install gigaclaw@${targetVersion}`, { stdio: 'inherit', cwd, shell: true });
579
+ } catch {
580
+ console.error('\n Install failed. Check your internet connection and try again.\n');
581
+ process.exit(1);
582
+ }
583
+
584
+ // --- Init (spawn new process to use the NEW version's templates) ---
585
+ console.log('\n Updating project files...\n');
586
+ try {
587
+ execSync('npx gigaclaw init', { stdio: 'inherit', cwd, shell: true });
588
+ } catch {
589
+ console.error('\n Failed to update project files. Try running "npx gigaclaw init" manually.\n');
590
+ process.exit(1);
591
+ }
592
+
593
+ // --- Clear .next ---
594
+ try {
595
+ fs.rmSync(path.join(cwd, '.next'), { recursive: true, force: true });
596
+ } catch {}
597
+
598
+ // --- Build ---
599
+ console.log('\n Building...\n');
600
+ try {
601
+ execSync('npm run build', { stdio: 'inherit', cwd, shell: true });
602
+ } catch {
603
+ console.error('\n Build failed. The upgrade has been applied but the project does not build.');
604
+ console.error(' Fix the build errors, then run:\n');
605
+ console.error(` npm run build`);
606
+ console.error(` git add -A && git commit -m "upgrade gigaclaw to ${targetVersion}"`);
607
+ console.error(' git push\n');
608
+ process.exit(1);
609
+ }
610
+
611
+ // --- Commit upgrade ---
612
+ const changes = execSync('git status --porcelain', { encoding: 'utf8', cwd, shell: true }).trim();
613
+ if (changes) {
614
+ try {
615
+ execSync('git add -A', { cwd, shell: true });
616
+ execSync(`git commit -m "upgrade gigaclaw to ${targetVersion}"`, { stdio: 'inherit', cwd, shell: true });
617
+ } catch {
618
+ console.error('\n Failed to commit upgrade. Try running manually:');
619
+ console.error(` git add -A && git commit -m "upgrade gigaclaw to ${targetVersion}"\n`);
620
+ process.exit(1);
621
+ }
622
+ }
623
+
624
+ // --- Push ---
625
+ console.log('\n Pushing to GitHub...\n');
626
+ try {
627
+ execSync('git push', { stdio: 'inherit', cwd, shell: true });
628
+ } catch {
629
+ console.error('\n Could not push to GitHub. Try running "git push" manually.\n');
630
+ process.exit(1);
631
+ }
632
+
633
+ // --- Docker restart (only if compose file exists, docker available, and containers running) ---
634
+ const composeFile = path.join(cwd, 'docker-compose.yml');
635
+ if (fs.existsSync(composeFile)) {
636
+ try {
637
+ const running = execSync('docker compose ps --status running -q', { encoding: 'utf8', cwd, shell: true }).trim();
638
+ if (running) {
639
+ console.log(' Restarting Docker containers...\n');
640
+ execSync('docker compose down && docker compose up -d', { stdio: 'inherit', cwd, shell: true });
641
+ }
642
+ } catch {
643
+ // Docker not available or not running — skip
644
+ }
645
+ }
646
+
647
+ // --- Summary ---
648
+ console.log(`\n Upgraded gigaclaw ${currentVersion} → ${targetVersion}`);
649
+ console.log(' Done!\n');
650
+ }
651
+
652
+ /**
653
+ * Load GH_OWNER and GH_REPO from .env
654
+ */
655
+ function loadRepoInfo() {
656
+ const envPath = path.join(process.cwd(), '.env');
657
+ if (!fs.existsSync(envPath)) {
658
+ console.error('\n No .env file found. Run "npm run setup" first.\n');
659
+ process.exit(1);
660
+ }
661
+ const content = fs.readFileSync(envPath, 'utf-8');
662
+ const env = {};
663
+ for (const line of content.split('\n')) {
664
+ const match = line.match(/^([^#=]+)=(.*)$/);
665
+ if (match) env[match[1].trim()] = match[2].trim();
666
+ }
667
+ if (!env.GH_OWNER || !env.GH_REPO) {
668
+ console.error('\n GH_OWNER and GH_REPO not found in .env. Run "npm run setup" first.\n');
669
+ process.exit(1);
670
+ }
671
+ return { owner: env.GH_OWNER, repo: env.GH_REPO };
672
+ }
673
+
674
+ /**
675
+ * Read all data from a piped stdin stream.
676
+ * Returns null if stdin is a TTY (interactive terminal).
677
+ */
678
+ function readStdin() {
679
+ return new Promise((resolve, reject) => {
680
+ if (process.stdin.isTTY) return resolve(null);
681
+ let data = '';
682
+ process.stdin.setEncoding('utf-8');
683
+ process.stdin.on('data', (chunk) => { data += chunk; });
684
+ process.stdin.on('end', () => resolve(data.trimEnd() || null));
685
+ process.stdin.on('error', reject);
686
+ });
687
+ }
688
+
689
+ /**
690
+ * Prompt for a secret value interactively if not provided as an argument.
691
+ * Supports piped stdin (e.g. echo "val" | gigaclaw set-var KEY).
692
+ */
693
+ async function promptForValue(key) {
694
+ const stdin = await readStdin();
695
+ if (stdin) return stdin;
696
+
697
+ if (!process.stdin.isTTY) {
698
+ console.error(`\n No value provided for ${key}. Pipe a value or pass it as an argument.\n`);
699
+ process.exit(1);
700
+ }
701
+
702
+ const { password, isCancel } = await import('@clack/prompts');
703
+ const value = await password({
704
+ message: `Enter value for ${key}:`,
705
+ validate: (input) => {
706
+ if (!input) return 'Value is required';
707
+ },
708
+ });
709
+ if (isCancel(value)) {
710
+ console.log('\nCancelled.\n');
711
+ process.exit(0);
712
+ }
713
+ return value;
714
+ }
715
+
716
+ async function setAgentSecret(key, value) {
717
+ if (!key) {
718
+ console.error('\n Usage: gigaclaw set-agent-secret <KEY> [VALUE]\n');
719
+ console.error(' Example: gigaclaw set-agent-secret ANTHROPIC_API_KEY\n');
720
+ process.exit(1);
721
+ }
722
+
723
+ if (!value) value = await promptForValue(key);
724
+
725
+ const { owner, repo } = loadRepoInfo();
726
+ const prefixedName = `AGENT_${key}`;
727
+
728
+ const { setSecret } = await import(path.join(__dirname, '..', 'setup', 'lib', 'github.mjs'));
729
+ const { updateEnvVariable } = await import(path.join(__dirname, '..', 'setup', 'lib', 'auth.mjs'));
730
+
731
+ const result = await setSecret(owner, repo, prefixedName, value);
732
+ if (result.success) {
733
+ console.log(`\n Set GitHub secret: ${prefixedName}`);
734
+ updateEnvVariable(key, value);
735
+ console.log(` Updated .env: ${key}`);
736
+ console.log('');
737
+ } else {
738
+ console.error(`\n Failed to set ${prefixedName}: ${result.error}\n`);
739
+ process.exit(1);
740
+ }
741
+ }
742
+
743
+ async function setAgentLlmSecret(key, value) {
744
+ if (!key) {
745
+ console.error('\n Usage: gigaclaw set-agent-llm-secret <KEY> [VALUE]\n');
746
+ console.error(' Example: gigaclaw set-agent-llm-secret BRAVE_API_KEY\n');
747
+ process.exit(1);
748
+ }
749
+
750
+ if (!value) value = await promptForValue(key);
751
+
752
+ const { owner, repo } = loadRepoInfo();
753
+ const prefixedName = `AGENT_LLM_${key}`;
754
+
755
+ const { setSecret } = await import(path.join(__dirname, '..', 'setup', 'lib', 'github.mjs'));
756
+
757
+ const result = await setSecret(owner, repo, prefixedName, value);
758
+ if (result.success) {
759
+ console.log(`\n Set GitHub secret: ${prefixedName}\n`);
760
+ } else {
761
+ console.error(`\n Failed to set ${prefixedName}: ${result.error}\n`);
762
+ process.exit(1);
763
+ }
764
+ }
765
+
766
+ async function setVar(key, value) {
767
+ if (!key) {
768
+ console.error('\n Usage: gigaclaw set-var <KEY> [VALUE]\n');
769
+ console.error(' Example: gigaclaw set-var LLM_MODEL claude-sonnet-4-5-20250929\n');
770
+ process.exit(1);
771
+ }
772
+
773
+ if (!value) value = await promptForValue(key);
774
+
775
+ const { owner, repo } = loadRepoInfo();
776
+
777
+ const { setVariable } = await import(path.join(__dirname, '..', 'setup', 'lib', 'github.mjs'));
778
+
779
+ const result = await setVariable(owner, repo, key, value);
780
+ if (result.success) {
781
+ console.log(`\n Set GitHub variable: ${key}\n`);
782
+ } else {
783
+ console.error(`\n Failed to set ${key}: ${result.error}\n`);
784
+ process.exit(1);
785
+ }
786
+ }
787
+
788
+ switch (command) {
789
+ case 'init':
790
+ await init();
791
+ break;
792
+ case 'setup':
793
+ setup();
794
+ break;
795
+ case 'setup-telegram':
796
+ setupTelegram();
797
+ break;
798
+ case 'reset-auth':
799
+ await resetAuth();
800
+ break;
801
+ case 'reset':
802
+ reset(args[0]);
803
+ break;
804
+ case 'diff':
805
+ diff(args[0]);
806
+ break;
807
+ case 'upgrade':
808
+ case 'update':
809
+ await upgrade();
810
+ break;
811
+ case 'set-agent-secret':
812
+ await setAgentSecret(args[0], args[1]);
813
+ break;
814
+ case 'set-agent-llm-secret':
815
+ await setAgentLlmSecret(args[0], args[1]);
816
+ break;
817
+ case 'set-var':
818
+ await setVar(args[0], args[1]);
819
+ break;
820
+ default:
821
+ printUsage();
822
+ process.exit(command ? 1 : 0);
823
+ }