@bahulam/code 0.1.1

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 (278) hide show
  1. package/README.md +93 -0
  2. package/package.json +56 -0
  3. package/pulse/app/activity/page.tsx +190 -0
  4. package/pulse/app/api/activity/route.ts +138 -0
  5. package/pulse/app/api/benchmark/route.ts +113 -0
  6. package/pulse/app/api/benchmarks/route.ts +195 -0
  7. package/pulse/app/api/costs/route.ts +88 -0
  8. package/pulse/app/api/export/route.ts +77 -0
  9. package/pulse/app/api/history/route.ts +11 -0
  10. package/pulse/app/api/import/route.ts +31 -0
  11. package/pulse/app/api/memory/route.ts +50 -0
  12. package/pulse/app/api/plans/route.ts +9 -0
  13. package/pulse/app/api/projects/[slug]/route.ts +96 -0
  14. package/pulse/app/api/projects/route.ts +121 -0
  15. package/pulse/app/api/sessions/[id]/replay/route.ts +20 -0
  16. package/pulse/app/api/sessions/[id]/route.ts +31 -0
  17. package/pulse/app/api/sessions/route.ts +112 -0
  18. package/pulse/app/api/settings/route.ts +14 -0
  19. package/pulse/app/api/stats/route.ts +143 -0
  20. package/pulse/app/api/todos/route.ts +9 -0
  21. package/pulse/app/api/tools/route.ts +160 -0
  22. package/pulse/app/benchmarks/page.tsx +224 -0
  23. package/pulse/app/costs/page.tsx +179 -0
  24. package/pulse/app/export/page.tsx +465 -0
  25. package/pulse/app/favicon.ico +0 -0
  26. package/pulse/app/globals.css +263 -0
  27. package/pulse/app/help/page.tsx +143 -0
  28. package/pulse/app/history/page.tsx +157 -0
  29. package/pulse/app/layout.tsx +46 -0
  30. package/pulse/app/memory/page.tsx +365 -0
  31. package/pulse/app/overview-client.tsx +393 -0
  32. package/pulse/app/page.tsx +14 -0
  33. package/pulse/app/plans/page.tsx +308 -0
  34. package/pulse/app/projects/[slug]/page.tsx +390 -0
  35. package/pulse/app/projects/page.tsx +110 -0
  36. package/pulse/app/sessions/[id]/page.tsx +243 -0
  37. package/pulse/app/sessions/page.tsx +39 -0
  38. package/pulse/app/settings/page.tsx +188 -0
  39. package/pulse/app/todos/page.tsx +211 -0
  40. package/pulse/app/tools/page.tsx +249 -0
  41. package/pulse/cli.js +164 -0
  42. package/pulse/components/activity/day-of-week-chart.tsx +35 -0
  43. package/pulse/components/activity/streak-card.tsx +36 -0
  44. package/pulse/components/costs/cache-efficiency-panel.tsx +76 -0
  45. package/pulse/components/costs/cost-by-project-chart.tsx +48 -0
  46. package/pulse/components/costs/cost-over-time-chart.tsx +95 -0
  47. package/pulse/components/costs/model-token-table.tsx +60 -0
  48. package/pulse/components/global-search.tsx +193 -0
  49. package/pulse/components/keyboard-nav-provider.tsx +23 -0
  50. package/pulse/components/layout/bottom-nav.tsx +53 -0
  51. package/pulse/components/layout/client-layout.tsx +31 -0
  52. package/pulse/components/layout/sidebar-context.tsx +50 -0
  53. package/pulse/components/layout/sidebar.tsx +183 -0
  54. package/pulse/components/layout/top-bar.tsx +121 -0
  55. package/pulse/components/overview/activity-heatmap.tsx +107 -0
  56. package/pulse/components/overview/conversation-table.tsx +148 -0
  57. package/pulse/components/overview/model-breakdown-donut.tsx +95 -0
  58. package/pulse/components/overview/peak-hours-chart.tsx +87 -0
  59. package/pulse/components/overview/project-activity-donut.tsx +96 -0
  60. package/pulse/components/overview/stat-card.tsx +102 -0
  61. package/pulse/components/overview/usage-over-time-chart.tsx +166 -0
  62. package/pulse/components/projects/project-card.tsx +175 -0
  63. package/pulse/components/sessions/replay/assistant-markdown.tsx +94 -0
  64. package/pulse/components/sessions/replay/compaction-card.tsx +25 -0
  65. package/pulse/components/sessions/replay/session-sidebar.tsx +231 -0
  66. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +98 -0
  67. package/pulse/components/sessions/replay/tool-call-badge.tsx +127 -0
  68. package/pulse/components/sessions/replay/turn-cards.tsx +220 -0
  69. package/pulse/components/sessions/replay/user-tool-result.tsx +158 -0
  70. package/pulse/components/sessions/session-badges.tsx +49 -0
  71. package/pulse/components/sessions/session-table.tsx +299 -0
  72. package/pulse/components/theme-provider.tsx +44 -0
  73. package/pulse/components/tools/feature-adoption-table.tsx +58 -0
  74. package/pulse/components/tools/mcp-server-panel.tsx +45 -0
  75. package/pulse/components/tools/tool-ranking-chart.tsx +57 -0
  76. package/pulse/components/tools/version-history-table.tsx +32 -0
  77. package/pulse/components/ui/alert.tsx +66 -0
  78. package/pulse/components/ui/badge.tsx +48 -0
  79. package/pulse/components/ui/breadcrumb.tsx +109 -0
  80. package/pulse/components/ui/button.tsx +64 -0
  81. package/pulse/components/ui/calendar.tsx +220 -0
  82. package/pulse/components/ui/card.tsx +92 -0
  83. package/pulse/components/ui/command.tsx +158 -0
  84. package/pulse/components/ui/dialog.tsx +158 -0
  85. package/pulse/components/ui/input.tsx +21 -0
  86. package/pulse/components/ui/popover.tsx +89 -0
  87. package/pulse/components/ui/progress.tsx +31 -0
  88. package/pulse/components/ui/select.tsx +190 -0
  89. package/pulse/components/ui/separator.tsx +28 -0
  90. package/pulse/components/ui/sheet.tsx +143 -0
  91. package/pulse/components/ui/skeleton.tsx +13 -0
  92. package/pulse/components/ui/table.tsx +116 -0
  93. package/pulse/components/ui/tabs.tsx +91 -0
  94. package/pulse/components/ui/tooltip.tsx +57 -0
  95. package/pulse/components/use-global-keyboard-nav.ts +79 -0
  96. package/pulse/components.json +23 -0
  97. package/pulse/eslint.config.mjs +18 -0
  98. package/pulse/lib/bahulam-paths.ts +23 -0
  99. package/pulse/lib/claude-reader.ts +592 -0
  100. package/pulse/lib/decode.ts +129 -0
  101. package/pulse/lib/pricing.ts +102 -0
  102. package/pulse/lib/replay-parser.ts +165 -0
  103. package/pulse/lib/tool-categories.ts +127 -0
  104. package/pulse/lib/utils.ts +6 -0
  105. package/pulse/next-env.d.ts +6 -0
  106. package/pulse/next.config.ts +16 -0
  107. package/pulse/package.json +45 -0
  108. package/pulse/postcss.config.mjs +7 -0
  109. package/pulse/public/activity.png +0 -0
  110. package/pulse/public/cc-lens.png +0 -0
  111. package/pulse/public/command-k.png +0 -0
  112. package/pulse/public/costs.png +0 -0
  113. package/pulse/public/dashboard-dark.png +0 -0
  114. package/pulse/public/dashboard-white.png +0 -0
  115. package/pulse/public/export.png +0 -0
  116. package/pulse/public/file.svg +1 -0
  117. package/pulse/public/globe.svg +1 -0
  118. package/pulse/public/next.svg +1 -0
  119. package/pulse/public/projects.png +0 -0
  120. package/pulse/public/session-chat.png +0 -0
  121. package/pulse/public/todos.png +0 -0
  122. package/pulse/public/tools.png +0 -0
  123. package/pulse/public/vercel.svg +1 -0
  124. package/pulse/public/window.svg +1 -0
  125. package/pulse/tsconfig.json +34 -0
  126. package/pulse/types/claude.ts +294 -0
  127. package/src/agents/loader.mjs +94 -0
  128. package/src/agents/multi_workflow_loader.mjs +330 -0
  129. package/src/agents/parser.mjs +205 -0
  130. package/src/agents/scaffold.mjs +223 -0
  131. package/src/agents/teams.mjs +123 -0
  132. package/src/agents/workflow_loader.mjs +122 -0
  133. package/src/agents/workflow_scaffold.mjs +249 -0
  134. package/src/auth/oauth.mjs +220 -0
  135. package/src/auth/tarang-auth.mjs +312 -0
  136. package/src/commands/agent.mjs +221 -0
  137. package/src/commands/workflow.mjs +581 -0
  138. package/src/config/cli-args.mjs +202 -0
  139. package/src/config/env.mjs +263 -0
  140. package/src/config/hook-runner.mjs +100 -0
  141. package/src/config/memory-loader.mjs +32 -0
  142. package/src/config/model-catalog.mjs +57 -0
  143. package/src/config/settings-loader.mjs +45 -0
  144. package/src/config/settings.mjs +132 -0
  145. package/src/context/ast-parser.mjs +298 -0
  146. package/src/context/bm25.mjs +85 -0
  147. package/src/context/prose-chunker.mjs +255 -0
  148. package/src/context/retriever.mjs +425 -0
  149. package/src/context/skeleton.mjs +134 -0
  150. package/src/context/symbol-indexer.mjs +375 -0
  151. package/src/core/agent-history.mjs +111 -0
  152. package/src/core/agent-loop.mjs +486 -0
  153. package/src/core/approval-log.mjs +145 -0
  154. package/src/core/approval.mjs +700 -0
  155. package/src/core/attachments.mjs +666 -0
  156. package/src/core/backend-url.mjs +68 -0
  157. package/src/core/bundled-runtime.mjs +418 -0
  158. package/src/core/cache-control.mjs +92 -0
  159. package/src/core/cache.mjs +105 -0
  160. package/src/core/callback-client.mjs +180 -0
  161. package/src/core/checkpoints.mjs +142 -0
  162. package/src/core/compact-history.mjs +127 -0
  163. package/src/core/context-envelope.mjs +54 -0
  164. package/src/core/context-manager.mjs +198 -0
  165. package/src/core/error-guidance.mjs +331 -0
  166. package/src/core/file-diff.mjs +217 -0
  167. package/src/core/headless.mjs +460 -0
  168. package/src/core/hooks-manager.mjs +87 -0
  169. package/src/core/jsonl-writer.mjs +449 -0
  170. package/src/core/local-agent.mjs +538 -0
  171. package/src/core/local-store.mjs +836 -0
  172. package/src/core/mode-selector.mjs +51 -0
  173. package/src/core/output-filter.mjs +177 -0
  174. package/src/core/paths.mjs +190 -0
  175. package/src/core/policy-resolver.mjs +156 -0
  176. package/src/core/pricing.mjs +336 -0
  177. package/src/core/project-artifacts.mjs +39 -0
  178. package/src/core/project-context-loader.mjs +139 -0
  179. package/src/core/providers.mjs +219 -0
  180. package/src/core/rate-limit-display.mjs +121 -0
  181. package/src/core/rate-limiter.mjs +119 -0
  182. package/src/core/resume-mode.mjs +192 -0
  183. package/src/core/risk-tier.mjs +388 -0
  184. package/src/core/safety.mjs +260 -0
  185. package/src/core/scheduler.mjs +173 -0
  186. package/src/core/session-manager.mjs +360 -0
  187. package/src/core/session.mjs +143 -0
  188. package/src/core/settings-sync.mjs +85 -0
  189. package/src/core/stagnation.mjs +57 -0
  190. package/src/core/stream-client.mjs +957 -0
  191. package/src/core/streaming.mjs +182 -0
  192. package/src/core/system-prompt.mjs +140 -0
  193. package/src/core/tasks.mjs +196 -0
  194. package/src/core/tool-executor.mjs +2231 -0
  195. package/src/core/trust.mjs +160 -0
  196. package/src/core/work-scope.mjs +248 -0
  197. package/src/hooks/engine.mjs +162 -0
  198. package/src/mcp/client.mjs +253 -0
  199. package/src/mcp/transport-shttp.mjs +130 -0
  200. package/src/mcp/transport-sse.mjs +131 -0
  201. package/src/mcp/transport-ws.mjs +134 -0
  202. package/src/onboarding/preflight.mjs +374 -0
  203. package/src/permissions/checker.mjs +57 -0
  204. package/src/permissions/command-classifier.mjs +700 -0
  205. package/src/permissions/injection-check.mjs +60 -0
  206. package/src/permissions/path-check.mjs +102 -0
  207. package/src/permissions/prompt.mjs +73 -0
  208. package/src/permissions/sandbox.mjs +112 -0
  209. package/src/plugins/loader.mjs +138 -0
  210. package/src/skills/installer.mjs +188 -0
  211. package/src/skills/loader.mjs +252 -0
  212. package/src/skills/runner.mjs +55 -0
  213. package/src/state/orbit.mjs +263 -0
  214. package/src/state/verbosity.mjs +99 -0
  215. package/src/telemetry/index.mjs +122 -0
  216. package/src/terminal/agents.mjs +353 -0
  217. package/src/terminal/analytics.mjs +292 -0
  218. package/src/terminal/ansi.mjs +695 -0
  219. package/src/terminal/init.mjs +145 -0
  220. package/src/terminal/main.mjs +310 -0
  221. package/src/terminal/repl-ask-form.mjs +120 -0
  222. package/src/terminal/repl-explore.mjs +44 -0
  223. package/src/terminal/repl-format.mjs +317 -0
  224. package/src/terminal/repl-model-form.mjs +132 -0
  225. package/src/terminal/repl-render.mjs +833 -0
  226. package/src/terminal/repl-resume.mjs +640 -0
  227. package/src/terminal/repl-state.mjs +120 -0
  228. package/src/terminal/repl-utils.mjs +34 -0
  229. package/src/terminal/repl.mjs +5032 -0
  230. package/src/terminal/skills.mjs +54 -0
  231. package/src/terminal/tool-display.mjs +392 -0
  232. package/src/tools/agent.mjs +137 -0
  233. package/src/tools/ask-user.mjs +61 -0
  234. package/src/tools/bash.mjs +231 -0
  235. package/src/tools/cron-create.mjs +120 -0
  236. package/src/tools/cron-delete.mjs +49 -0
  237. package/src/tools/cron-list.mjs +37 -0
  238. package/src/tools/edit.mjs +82 -0
  239. package/src/tools/enter-worktree.mjs +69 -0
  240. package/src/tools/exit-worktree.mjs +57 -0
  241. package/src/tools/glob.mjs +117 -0
  242. package/src/tools/grep.mjs +129 -0
  243. package/src/tools/lint.mjs +71 -0
  244. package/src/tools/ls.mjs +58 -0
  245. package/src/tools/lsp.mjs +115 -0
  246. package/src/tools/multi-edit.mjs +94 -0
  247. package/src/tools/notebook-edit.mjs +96 -0
  248. package/src/tools/project-overview.mjs +703 -0
  249. package/src/tools/read-mcp-resource.mjs +57 -0
  250. package/src/tools/read.mjs +138 -0
  251. package/src/tools/registry.mjs +116 -0
  252. package/src/tools/remote-trigger.mjs +84 -0
  253. package/src/tools/send-message.mjs +64 -0
  254. package/src/tools/skill.mjs +52 -0
  255. package/src/tools/test-runner.mjs +49 -0
  256. package/src/tools/todo-write.mjs +68 -0
  257. package/src/tools/tool-search.mjs +77 -0
  258. package/src/tools/web-fetch.mjs +65 -0
  259. package/src/tools/web-search.mjs +89 -0
  260. package/src/tools/write.mjs +55 -0
  261. package/src/ui/approval.mjs +510 -0
  262. package/src/ui/banner.mjs +232 -0
  263. package/src/ui/commands.mjs +537 -0
  264. package/src/ui/formatter.mjs +409 -0
  265. package/src/ui/icons.mjs +170 -0
  266. package/src/ui/input-dock.mjs +772 -0
  267. package/src/ui/markdown.mjs +278 -0
  268. package/src/ui/mission-report.mjs +296 -0
  269. package/src/ui/palette.mjs +189 -0
  270. package/src/ui/render-queue.mjs +500 -0
  271. package/src/ui/slash-commands.mjs +257 -0
  272. package/src/ui/spinner.mjs +116 -0
  273. package/src/ui/sub-agent.mjs +167 -0
  274. package/src/ui/term.mjs +174 -0
  275. package/src/ui/text-layout.mjs +127 -0
  276. package/src/ui/tool-card.mjs +740 -0
  277. package/src/ui/tool-details.mjs +504 -0
  278. package/src/ui/transcript-block.mjs +20 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * CLI Argument Parser — supports all major Claude Code flags.
3
+ *
4
+ * Flags:
5
+ * --model, -m Model to use
6
+ * --permission-mode Permission mode
7
+ * --print, -p Print mode (non-interactive prompt)
8
+ * --output-format json, text, stream-json
9
+ * --system-prompt Override system prompt
10
+ * --add-dir Additional CLAUDE.md directories
11
+ * --max-turns Maximum conversation turns
12
+ * --allowedTools Comma-separated allowed tools
13
+ * --disallowedTools Comma-separated denied tools
14
+ * --verbose, -v Verbose output
15
+ * --debug, -d Debug mode
16
+ * --version Show version
17
+ * --help, -h Show help
18
+ */
19
+
20
+ export function parseArgs(args) {
21
+ const result = {
22
+ prompt: null,
23
+ model: null,
24
+ permissionMode: null,
25
+ outputFormat: null,
26
+ systemPrompt: null,
27
+ addDirs: [],
28
+ maxTurns: null,
29
+ timeout: null,
30
+ allowedTools: null,
31
+ disallowedTools: null,
32
+ route: null,
33
+ resume: false,
34
+ resumeSessionId: null,
35
+ headless: false,
36
+ skipPermissions: false,
37
+ vision: [],
38
+ verbose: false,
39
+ debug: false,
40
+ showVersion: false,
41
+ showHelp: false,
42
+ };
43
+
44
+ for (let i = 0; i < args.length; i++) {
45
+ const arg = args[i];
46
+
47
+ switch (arg) {
48
+ case '--model':
49
+ case '-m':
50
+ result.model = args[++i];
51
+ break;
52
+
53
+ case '--route':
54
+ result.route = args[++i];
55
+ break;
56
+
57
+ case '--permission-mode':
58
+ result.permissionMode = args[++i];
59
+ break;
60
+
61
+ case '--print':
62
+ case '-p':
63
+ result.prompt = args[++i];
64
+ break;
65
+
66
+ case '--output-format':
67
+ result.outputFormat = args[++i];
68
+ break;
69
+
70
+ case '--system-prompt':
71
+ result.systemPrompt = args[++i];
72
+ break;
73
+
74
+ case '--add-dir':
75
+ result.addDirs.push(args[++i]);
76
+ break;
77
+
78
+ case '--max-turns':
79
+ result.maxTurns = parseInt(args[++i], 10);
80
+ break;
81
+
82
+ case '--timeout':
83
+ result.timeout = parseInt(args[++i], 10);
84
+ break;
85
+
86
+ case '--allowedTools':
87
+ result.allowedTools = args[++i]?.split(',').map(s => s.trim());
88
+ break;
89
+
90
+ case '--disallowedTools':
91
+ result.disallowedTools = args[++i]?.split(',').map(s => s.trim());
92
+ break;
93
+
94
+ case '--resume':
95
+ case '--continue':
96
+ case '-r': {
97
+ result.resume = true;
98
+ // Optional: --resume <sessionId>
99
+ const next = args[i + 1];
100
+ if (next && !next.startsWith('-')) {
101
+ result.resumeSessionId = next;
102
+ i++;
103
+ }
104
+ break;
105
+ }
106
+
107
+ case '--headless':
108
+ result.headless = true;
109
+ result.skipPermissions = true; // headless implies skip permissions
110
+ break;
111
+
112
+ case '--cache-report':
113
+ // PRD-071 §1.5 — write a machine-readable cache summary to
114
+ // <path> at end of run. Consumed by benchmark/cache-check.sh.
115
+ result.cacheReport = args[++i];
116
+ break;
117
+
118
+ case '--local':
119
+ // Force LocalAgent path (bypass backend). Meant for benchmarks
120
+ // that need to exercise the CLI's own LLM code — cache_control
121
+ // wiring, prompt-cache stats, etc.
122
+ result.local = true;
123
+ break;
124
+
125
+ case '--vision': {
126
+ const value = args[++i];
127
+ if (value) result.vision.push(value);
128
+ break;
129
+ }
130
+
131
+ case '--dangerously-skip-permissions':
132
+ result.skipPermissions = true;
133
+ break;
134
+
135
+ case '--verbose':
136
+ case '-v':
137
+ result.verbose = true;
138
+ break;
139
+
140
+ case '--debug':
141
+ case '-d':
142
+ result.debug = true;
143
+ break;
144
+
145
+ case '--version':
146
+ result.showVersion = true;
147
+ break;
148
+
149
+ case '--help':
150
+ case '-h':
151
+ result.showHelp = true;
152
+ break;
153
+
154
+ default:
155
+ // Bare argument becomes prompt
156
+ if (!arg.startsWith('-')) {
157
+ result.prompt = arg;
158
+ }
159
+ break;
160
+ }
161
+ }
162
+
163
+ return result;
164
+ }
165
+
166
+ /**
167
+ * Print usage/help text.
168
+ * @returns {string}
169
+ */
170
+ export function getUsageText() {
171
+ return `
172
+ Usage: occ [options] [prompt]
173
+
174
+ Options:
175
+ --model, -m <id|role=id> Session model override (also named modes: fast|thinking|extra|max)
176
+ --route <platform|byok> Model route; platform validates against the curated catalog
177
+ --permission-mode <mode> Permission mode (bypassPermissions, acceptEdits, plan, auto, dontAsk)
178
+ --print, -p <prompt> Non-interactive mode: run prompt and exit
179
+ --output-format <fmt> Output format: text, json, stream-json
180
+ --system-prompt <text> Override system prompt
181
+ --add-dir <dir> Additional directory to search for CLAUDE.md
182
+ --max-turns <n> Maximum conversation turns
183
+ --allowedTools <tools> Comma-separated list of allowed tools
184
+ --disallowedTools <tools> Comma-separated list of denied tools
185
+ --resume, -r [sessionId] Resume last session (or specific session)
186
+ --continue Alias for --resume
187
+ --headless Non-interactive mode: auto-approve, JSONL output
188
+ --cache-report <file> Write prompt-cache summary JSON to <file> (headless only)
189
+ --vision <image-path> Attach image path in headless mode
190
+ --dangerously-skip-permissions Skip ALL approval prompts, including dangerous tiers
191
+ --verbose, -v Verbose output
192
+ --debug, -d Debug mode
193
+ --version Show version
194
+ --help, -h Show this help
195
+
196
+ Examples:
197
+ occ Start interactive REPL
198
+ occ -p "What is 2+2?" Run prompt and exit
199
+ occ -m claude-haiku-4-5 Use Haiku model
200
+ occ --debug -p "Fix bug" Debug mode with prompt
201
+ `.trim();
202
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Environment Variables — support for Claude Code env vars.
3
+ *
4
+ * Reads and normalizes the ~50 most important environment variables
5
+ * that control Claude Code behavior.
6
+ */
7
+
8
+ /**
9
+ * All supported environment variables with defaults and descriptions.
10
+ */
11
+ export const ENV_SCHEMA = {
12
+ // API Configuration
13
+ ANTHROPIC_API_KEY: { type: 'string', description: 'Anthropic API key' },
14
+ ANTHROPIC_BASE_URL: { type: 'string', default: 'https://api.anthropic.com', description: 'Anthropic API base URL' },
15
+ ANTHROPIC_MODEL: { type: 'string', description: 'Override default model' },
16
+ OPENAI_API_KEY: { type: 'string', description: 'OpenAI API key for compatible models' },
17
+ OPENAI_BASE_URL: { type: 'string', default: 'https://api.openai.com/v1', description: 'OpenAI-compatible base URL' },
18
+ GOOGLE_API_KEY: { type: 'string', description: 'Google AI API key' },
19
+ GEMINI_API_KEY: { type: 'string', description: 'Alias for GOOGLE_API_KEY' },
20
+
21
+ // Model Configuration
22
+ CLAUDE_CODE_MAX_OUTPUT_TOKENS: { type: 'number', default: 16384, description: 'Max output tokens' },
23
+ CLAUDE_CODE_SUBAGENT_MODEL: { type: 'string', description: 'Model for subagents' },
24
+ CLAUDE_CODE_EFFORT_LEVEL: { type: 'string', default: 'normal', description: 'Effort level (low/normal/high)' },
25
+
26
+ // Behavior Flags
27
+ CLAUDE_CODE_BRIEF: { type: 'boolean', default: false, description: 'Brief output mode' },
28
+ CLAUDE_CODE_DISABLE_CRON: { type: 'boolean', default: false, description: 'Disable cron tasks' },
29
+ CLAUDE_CODE_ENABLE_TASKS: { type: 'boolean', default: false, description: 'Enable task system' },
30
+ KEPLER_STAGNATION_DETECTION: { type: 'boolean', default: false, description: 'Detect consecutive identical tool-call loops' },
31
+ KEPLER_STAGNATION_THRESHOLD: { type: 'number', default: 3, description: 'Consecutive identical calls before warning' },
32
+ CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: { type: 'boolean', default: false, description: 'Enable agent teams' },
33
+ CLAUDE_CODE_DEBUG: { type: 'boolean', default: false, description: 'Debug mode' },
34
+ CLAUDE_CODE_DISABLE_TELEMETRY: { type: 'boolean', default: false, description: 'Disable telemetry' },
35
+
36
+ // Permission and Security
37
+ CLAUDE_CODE_PERMISSION_MODE: { type: 'string', default: 'default', description: 'Permission mode' },
38
+ CLAUDE_CODE_SANDBOX: { type: 'boolean', default: true, description: 'Enable sandbox' },
39
+
40
+ // Context and Memory
41
+ CLAUDE_CODE_MAX_CONTEXT_TOKENS: { type: 'number', default: 180000, description: 'Max context window tokens' },
42
+ CLAUDE_CODE_AUTO_COMPACT: { type: 'boolean', default: true, description: 'Auto-compact context' },
43
+
44
+ // UI and Display
45
+ SHOW_THINKING: { type: 'boolean', default: false, description: 'Show thinking blocks' },
46
+ SHOW_TOOL_RESULTS: { type: 'boolean', default: false, description: 'Show tool results in REPL' },
47
+ NO_COLOR: { type: 'boolean', default: false, description: 'Disable colored output' },
48
+ TERM: { type: 'string', description: 'Terminal type' },
49
+
50
+ // MCP
51
+ MCP_DEBUG: { type: 'boolean', default: false, description: 'MCP debug logging' },
52
+
53
+ // Remote
54
+ REMOTE_AGENT_URL: { type: 'string', description: 'Remote agent endpoint' },
55
+ REMOTE_AGENT_TOKEN: { type: 'string', description: 'Remote agent auth token' },
56
+
57
+ // Search
58
+ BRAVE_API_KEY: { type: 'string', description: 'Brave Search API key' },
59
+ SEARXNG_URL: { type: 'string', description: 'SearXNG instance URL' },
60
+
61
+ // Networking
62
+ HTTP_PROXY: { type: 'string', description: 'HTTP proxy URL' },
63
+ HTTPS_PROXY: { type: 'string', description: 'HTTPS proxy URL' },
64
+ NO_PROXY: { type: 'string', description: 'No-proxy list' },
65
+
66
+ // Agent Identity
67
+ AGENT_ID: { type: 'string', default: 'main', description: 'Agent identifier for teams' },
68
+
69
+ // Feature Flags
70
+ CLAUDE_CODE_THINKING: { type: 'boolean', default: false, description: 'Enable extended thinking' },
71
+ CLAUDE_CODE_THINKING_BUDGET: { type: 'number', default: 10000, description: 'Thinking token budget' },
72
+ CLAUDE_CODE_STREAMING: { type: 'boolean', default: true, description: 'Enable streaming' },
73
+
74
+ // Paths
75
+ CLAUDE_CONFIG_DIR: { type: 'string', description: 'Custom config directory' },
76
+ CLAUDE_CACHE_DIR: { type: 'string', description: 'Custom cache directory' },
77
+
78
+ // Extended: OAuth & Auth
79
+ CLAUDE_OAUTH_CLIENT_ID: { type: 'string', description: 'OAuth client ID' },
80
+ CLAUDE_OAUTH_REDIRECT_URI: { type: 'string', default: 'http://localhost:9876/callback', description: 'OAuth redirect URI' },
81
+ ANTHROPIC_AUTH_TOKEN: { type: 'string', description: 'Anthropic auth token (OAuth)' },
82
+
83
+ // Extended: Sandbox & Security
84
+ CLAUDE_CODE_SANDBOX_PLATFORM: { type: 'string', description: 'Override sandbox platform (linux/darwin)' },
85
+ CLAUDE_CODE_INJECTION_CHECK: { type: 'boolean', default: true, description: 'Enable command injection checks' },
86
+ CLAUDE_CODE_PATH_CHECK: { type: 'boolean', default: true, description: 'Enable file path validation' },
87
+
88
+ // Extended: Rate Limiting
89
+ CLAUDE_CODE_MAX_RETRIES: { type: 'number', default: 5, description: 'Max API retries on 429/529' },
90
+ CLAUDE_CODE_RETRY_BASE_DELAY: { type: 'number', default: 1000, description: 'Base retry delay in ms' },
91
+ CLAUDE_CODE_RETRY_MAX_DELAY: { type: 'number', default: 60000, description: 'Max retry delay in ms' },
92
+
93
+ // Extended: Agent Teams
94
+ CLAUDE_CODE_TEAM_SIZE: { type: 'number', default: 5, description: 'Max team size for multi-agent' },
95
+ CLAUDE_CODE_TEAM_BROADCAST: { type: 'boolean', default: false, description: 'Broadcast messages to all teammates' },
96
+
97
+ // Extended: Providers
98
+ AWS_ACCESS_KEY_ID: { type: 'string', description: 'AWS access key for Bedrock' },
99
+ AWS_SECRET_ACCESS_KEY: { type: 'string', description: 'AWS secret key for Bedrock' },
100
+ AWS_REGION: { type: 'string', default: 'us-east-1', description: 'AWS region for Bedrock' },
101
+ GOOGLE_APPLICATION_CREDENTIALS: { type: 'string', description: 'GCP service account credentials path' },
102
+ VERTEX_PROJECT: { type: 'string', description: 'GCP project for Vertex AI' },
103
+ VERTEX_REGION: { type: 'string', default: 'us-central1', description: 'GCP region for Vertex AI' },
104
+
105
+ // Extended: Cron & Scheduling
106
+ CLAUDE_CODE_CRON_INTERVAL: { type: 'number', default: 60000, description: 'Cron check interval in ms' },
107
+ CLAUDE_CODE_SCHEDULED_TASKS_FILE: { type: 'string', description: 'Path to scheduled tasks JSON' },
108
+
109
+ // Extended: Plugins
110
+ CLAUDE_CODE_PLUGIN_DIR: { type: 'string', description: 'Custom plugin directory' },
111
+ CLAUDE_CODE_DISABLE_PLUGINS: { type: 'boolean', default: false, description: 'Disable all plugins' },
112
+
113
+ // Extended: Session
114
+ CLAUDE_CODE_SESSION_TTL: { type: 'number', default: 86400000, description: 'Session TTL in ms (default 24h)' },
115
+ CLAUDE_CODE_AUTO_SAVE: { type: 'boolean', default: true, description: 'Auto-save sessions' },
116
+
117
+ // Extended: Logging
118
+ CLAUDE_CODE_LOG_LEVEL: { type: 'string', default: 'info', description: 'Log level (debug/info/warn/error)' },
119
+ CLAUDE_CODE_LOG_FILE: { type: 'string', description: 'Log file path' },
120
+
121
+ // Extended: Hooks
122
+ CLAUDE_CODE_HOOK_TIMEOUT: { type: 'number', default: 10000, description: 'Hook execution timeout in ms' },
123
+ CLAUDE_CODE_HOOK_FAIL_OPEN: { type: 'boolean', default: true, description: 'Allow on hook failure' },
124
+
125
+ // Extended: Context
126
+ CLAUDE_CODE_COMPACT_THRESHOLD: { type: 'number', default: 0.8, description: 'Context compaction threshold (0-1)' },
127
+ CLAUDE_CODE_MAX_MESSAGES: { type: 'number', default: 200, description: 'Max messages in context' },
128
+
129
+ // Extended: Cache
130
+ CLAUDE_CODE_PROMPT_CACHE: { type: 'boolean', default: true, description: 'Enable prompt caching' },
131
+ CLAUDE_CODE_CACHE_TTL: { type: 'number', default: 300000, description: 'Cache TTL in ms (default 5min)' },
132
+
133
+ // Extended: Output
134
+ CLAUDE_CODE_MAX_LINES: { type: 'number', default: 200, description: 'Max output lines for tools' },
135
+ CLAUDE_CODE_TRUNCATE: { type: 'boolean', default: true, description: 'Truncate long outputs' },
136
+ CLAUDE_CODE_JSON_OUTPUT: { type: 'boolean', default: false, description: 'Output JSON instead of text' },
137
+
138
+ // Extended: Git
139
+ CLAUDE_CODE_GIT_ENABLED: { type: 'boolean', default: true, description: 'Enable git operations' },
140
+ CLAUDE_CODE_GIT_AUTO_COMMIT: { type: 'boolean', default: false, description: 'Auto-commit changes' },
141
+
142
+ // Extended: Editor
143
+ EDITOR: { type: 'string', description: 'Default text editor' },
144
+ VISUAL: { type: 'string', description: 'Default visual editor' },
145
+
146
+ // Extended: Language
147
+ LANG: { type: 'string', description: 'System locale' },
148
+ LC_ALL: { type: 'string', description: 'Locale override' },
149
+
150
+ // Extended: CI/CD
151
+ CI: { type: 'boolean', default: false, description: 'Running in CI environment' },
152
+ GITHUB_ACTIONS: { type: 'boolean', default: false, description: 'Running in GitHub Actions' },
153
+ GITLAB_CI: { type: 'boolean', default: false, description: 'Running in GitLab CI' },
154
+
155
+ // Extended: Container
156
+ CONTAINER: { type: 'boolean', default: false, description: 'Running in container' },
157
+ DOCKER: { type: 'boolean', default: false, description: 'Running in Docker' },
158
+ CODESPACE_NAME: { type: 'string', description: 'GitHub Codespace name' },
159
+
160
+ // Extended: Notification
161
+ CLAUDE_CODE_NOTIFY: { type: 'boolean', default: false, description: 'Enable desktop notifications' },
162
+ CLAUDE_CODE_WEBHOOK_URL: { type: 'string', description: 'Webhook URL for notifications' },
163
+
164
+ // Extended: Experimental
165
+ CLAUDE_CODE_EXPERIMENTAL_MCP: { type: 'boolean', default: false, description: 'Enable experimental MCP features' },
166
+ CLAUDE_CODE_EXPERIMENTAL_TOOLS: { type: 'boolean', default: false, description: 'Enable experimental tools' },
167
+ CLAUDE_CODE_EXPERIMENTAL_VISION: { type: 'boolean', default: false, description: 'Enable vision/image support' },
168
+ CLAUDE_CODE_EXPERIMENTAL_MEMORY: { type: 'boolean', default: false, description: 'Enable persistent memory' },
169
+
170
+ // Extended: Worktree
171
+ CLAUDE_CODE_WORKTREE_DIR: { type: 'string', description: 'Default worktree base directory' },
172
+ CLAUDE_CODE_AUTO_WORKTREE: { type: 'boolean', default: false, description: 'Auto-create worktrees for branches' },
173
+
174
+ // Extended: Notebook
175
+ CLAUDE_CODE_NOTEBOOK_KERNEL: { type: 'string', default: 'python3', description: 'Default Jupyter kernel' },
176
+
177
+ // Extended: LSP
178
+ CLAUDE_CODE_LSP_ENABLED: { type: 'boolean', default: false, description: 'Enable LSP integration' },
179
+ CLAUDE_CODE_LSP_PORT: { type: 'number', default: 0, description: 'LSP server port (0 = auto)' },
180
+
181
+ // Extended: Timeouts
182
+ CLAUDE_CODE_TOOL_TIMEOUT: { type: 'number', default: 120000, description: 'Tool execution timeout in ms' },
183
+ CLAUDE_CODE_API_TIMEOUT: { type: 'number', default: 300000, description: 'API call timeout in ms' },
184
+ CLAUDE_CODE_MCP_TIMEOUT: { type: 'number', default: 30000, description: 'MCP operation timeout in ms' },
185
+
186
+ // Extended: Permissions (additional)
187
+ CLAUDE_CODE_ALLOWED_TOOLS: { type: 'string', description: 'Comma-separated list of allowed tools' },
188
+ CLAUDE_CODE_DISALLOWED_TOOLS: { type: 'string', description: 'Comma-separated list of disallowed tools' },
189
+
190
+ // Extended: Telemetry (additional)
191
+ CLAUDE_CODE_TELEMETRY_ENDPOINT: { type: 'string', description: 'Custom telemetry endpoint URL' },
192
+ CLAUDE_CODE_SENTRY_DSN: { type: 'string', description: 'Sentry DSN for error reporting' },
193
+ };
194
+
195
+ /**
196
+ * Read and normalize all environment variables.
197
+ * @returns {object} normalized env config
198
+ */
199
+ export function readEnv() {
200
+ const env = {};
201
+
202
+ for (const [key, schema] of Object.entries(ENV_SCHEMA)) {
203
+ const raw = process.env[key];
204
+ if (raw === undefined) {
205
+ if (schema.default !== undefined) {
206
+ env[key] = schema.default;
207
+ }
208
+ continue;
209
+ }
210
+
211
+ switch (schema.type) {
212
+ case 'boolean':
213
+ env[key] = raw === '1' || raw === 'true' || raw === 'yes';
214
+ break;
215
+ case 'number':
216
+ env[key] = parseInt(raw, 10);
217
+ if (isNaN(env[key])) env[key] = schema.default;
218
+ break;
219
+ default:
220
+ env[key] = raw;
221
+ }
222
+ }
223
+
224
+ return env;
225
+ }
226
+
227
+ /**
228
+ * Get a specific env var with type coercion.
229
+ * @param {string} key
230
+ * @param {*} [defaultValue]
231
+ */
232
+ export function getEnv(key, defaultValue) {
233
+ const schema = ENV_SCHEMA[key];
234
+ const raw = process.env[key];
235
+
236
+ if (raw === undefined) return defaultValue ?? schema?.default;
237
+
238
+ if (schema?.type === 'boolean') return raw === '1' || raw === 'true';
239
+ if (schema?.type === 'number') {
240
+ const n = parseInt(raw, 10);
241
+ return isNaN(n) ? defaultValue : n;
242
+ }
243
+ return raw;
244
+ }
245
+
246
+ /**
247
+ * List all supported env vars with their current values.
248
+ */
249
+ export function listEnvVars() {
250
+ const result = [];
251
+ for (const [key, schema] of Object.entries(ENV_SCHEMA)) {
252
+ const value = process.env[key];
253
+ result.push({
254
+ key,
255
+ type: schema.type,
256
+ value: value || undefined,
257
+ default: schema.default,
258
+ description: schema.description,
259
+ isSet: value !== undefined,
260
+ });
261
+ }
262
+ return result;
263
+ }
@@ -0,0 +1,100 @@
1
+ import { execFile } from 'node:child_process';
2
+ import * as path from 'node:path';
3
+ import { loadKeplerSettings } from './settings-loader.mjs';
4
+
5
+ function asArray(value) {
6
+ if (!value) return [];
7
+ return Array.isArray(value) ? value : [value];
8
+ }
9
+
10
+ function matchesHook(hook, toolName) {
11
+ const matcher = hook.matcher || hook.toolName || hook.tool;
12
+ if (!matcher) return true;
13
+ try {
14
+ return new RegExp(matcher).test(toolName);
15
+ } catch {
16
+ return matcher === toolName;
17
+ }
18
+ }
19
+
20
+ function runCommand(command, { cwd, env, timeoutMs, input }) {
21
+ return new Promise((resolve) => {
22
+ const child = execFile('/bin/sh', ['-lc', command], {
23
+ cwd,
24
+ env,
25
+ timeout: timeoutMs,
26
+ windowsHide: true,
27
+ }, (error, stdout, stderr) => {
28
+ let parsed = null;
29
+ try { parsed = stdout.trim() ? JSON.parse(stdout.trim()) : null; }
30
+ catch { parsed = stdout.trim() ? { output: stdout.trim() } : null; }
31
+ resolve({
32
+ ok: !error,
33
+ code: error?.code ?? 0,
34
+ signal: error?.signal || null,
35
+ stdout,
36
+ stderr,
37
+ parsed,
38
+ error,
39
+ });
40
+ });
41
+ if (input) child.stdin?.end(JSON.stringify(input));
42
+ });
43
+ }
44
+
45
+ export class HookRunner {
46
+ constructor({ cwd = process.cwd(), settings = null, sessionId = null } = {}) {
47
+ this.cwd = cwd;
48
+ this.sessionId = sessionId;
49
+ this.settings = settings || loadKeplerSettings({ cwd }).settings;
50
+ }
51
+
52
+ reload() {
53
+ this.settings = loadKeplerSettings({ cwd: this.cwd }).settings;
54
+ }
55
+
56
+ hooksFor(event) {
57
+ return asArray(this.settings?.hooks?.[event]);
58
+ }
59
+
60
+ async run(event, payload = {}) {
61
+ const results = [];
62
+ for (const hook of this.hooksFor(event)) {
63
+ const toolName = payload.toolName || payload.tool || '';
64
+ if ((event === 'PreToolUse' || event === 'PostToolUse') && !matchesHook(hook, toolName)) continue;
65
+ const timeoutSeconds = hook.timeout ?? this.settings?.hooks?.timeout ?? 5;
66
+ const input = {
67
+ event,
68
+ tool_name: toolName,
69
+ tool_input: payload.input || payload.args || {},
70
+ tool_result: payload.result || null,
71
+ project_dir: this.cwd,
72
+ session_id: this.sessionId || '',
73
+ };
74
+ const env = {
75
+ ...process.env,
76
+ ...(this.settings?.env || {}),
77
+ KEPLER_TOOL_NAME: toolName,
78
+ KEPLER_TOOL_INPUT_FILE_PATH: input.tool_input.file_path || input.tool_input.path || '',
79
+ KEPLER_PROJECT_DIR: this.cwd,
80
+ KEPLER_SESSION_ID: this.sessionId || '',
81
+ KEPLER_TURN_ID: payload.turnId || '',
82
+ };
83
+ const result = await runCommand(hook.command, {
84
+ cwd: path.resolve(this.cwd),
85
+ env,
86
+ timeoutMs: timeoutSeconds * 1000,
87
+ input,
88
+ });
89
+ results.push({ hook, ...result });
90
+ if (result.code === 2 || result.parsed?.block === true || result.parsed?.decision === 'deny') {
91
+ return {
92
+ blocked: true,
93
+ message: result.parsed?.message || result.stderr?.trim() || `Blocked by hook: ${hook.command}`,
94
+ results,
95
+ };
96
+ }
97
+ }
98
+ return { blocked: false, results };
99
+ }
100
+ }
@@ -0,0 +1,32 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { bahulamHome, projectConfigDir } from '../core/paths.mjs';
4
+
5
+ function readIfExists(filePath, maxChars = 12000) {
6
+ try {
7
+ if (!fs.existsSync(filePath)) return null;
8
+ const content = fs.readFileSync(filePath, 'utf-8');
9
+ return {
10
+ path: filePath,
11
+ content: content.length > maxChars
12
+ ? content.slice(0, maxChars) + '\n\n[...truncated...]'
13
+ : content,
14
+ };
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ export function loadKeplerMemory({ cwd = process.cwd() } = {}) {
21
+ const files = [];
22
+ const global = readIfExists(path.join(bahulamHome(), 'KEPLER.md'));
23
+ if (global) files.push({ source: 'global', ...global });
24
+
25
+ const topLevel = readIfExists(path.join(cwd, 'KEPLER.md'));
26
+ if (topLevel) files.push({ source: 'project-top-level', ...topLevel });
27
+
28
+ const project = readIfExists(path.join(projectConfigDir(cwd), 'KEPLER.md'));
29
+ if (project) files.push({ source: 'project', ...project });
30
+
31
+ return files;
32
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Shipped model catalog snapshot — Node side of PRD-076 W7.
3
+ *
4
+ * The canonical source is `codekepler-backend/app/services/
5
+ * model_catalog_snapshot.json` (admin dashboard edits it and publishes to
6
+ * Supabase). `stage-runtime-app.sh` copies that file into every runtime
7
+ * wheel at `runtime/app/services/model_catalog_snapshot.json`, so when
8
+ * `@bahulam/runtime-<platform>-<arch>` is installed as a sibling optional
9
+ * dep of `@bahulam/code`, the file is already on disk — we read from
10
+ * there instead of maintaining a third copy in the CLI package.
11
+ */
12
+
13
+ import * as fs from 'node:fs';
14
+ import { runtimeSnapshotPath } from '../core/bundled-runtime.mjs';
15
+
16
+ let _cache = null;
17
+
18
+ // Snapshot rows are camelCase (matches how the backend delivers the
19
+ // static file); the /api/models response is snake_case. Normalize once
20
+ // here so downstream consumers can treat both sources identically.
21
+ function normalizeSnapshotRow(row) {
22
+ const id = row.value || row.id;
23
+ if (!id) return null;
24
+ return {
25
+ id,
26
+ provider: row.provider || (id.includes('/') ? id.split('/', 1)[0] : 'unknown'),
27
+ label: row.label || id,
28
+ input_cost_usd_per_m: row.inputCost ?? row.input_cost_usd_per_m ?? null,
29
+ output_cost_usd_per_m: row.outputCost ?? row.output_cost_usd_per_m ?? null,
30
+ context_length: row.context ?? row.context_length ?? null,
31
+ max_output: row.maxOutput ?? row.max_output ?? null,
32
+ supports_tools: row.supportsTools ?? row.supports_tools ?? false,
33
+ supports_reasoning: row.supportsReasoning ?? row.supports_reasoning ?? false,
34
+ harness_validated: row.harnessValidated ?? row.harness_validated ?? false,
35
+ cache_profile: row.cacheProfile ?? row.cache_profile ?? null,
36
+ platform_access_tier: row.platformAccessTier ?? row.platform_access_tier ?? [],
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Read the shipped catalog snapshot from the installed runtime bundle.
42
+ * Returns an array of normalized rows (API-shape) or null if the runtime
43
+ * isn't installed / the file is unreadable. Cached after first success.
44
+ */
45
+ export function readShippedCatalog() {
46
+ if (_cache !== null) return _cache;
47
+ try {
48
+ const raw = fs.readFileSync(runtimeSnapshotPath(), 'utf-8');
49
+ const data = JSON.parse(raw);
50
+ const rows = Array.isArray(data?.models) ? data.models : [];
51
+ _cache = rows.map(normalizeSnapshotRow).filter(Boolean);
52
+ return _cache;
53
+ } catch {
54
+ _cache = null;
55
+ return null;
56
+ }
57
+ }