@bahulam/code 2.6.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 (273) hide show
  1. package/README.md +80 -0
  2. package/package.json +49 -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 +222 -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 +306 -0
  136. package/src/commands/agent.mjs +220 -0
  137. package/src/commands/workflow.mjs +581 -0
  138. package/src/config/cli-args.mjs +200 -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/settings-loader.mjs +45 -0
  143. package/src/config/settings.mjs +132 -0
  144. package/src/context/ast-parser.mjs +298 -0
  145. package/src/context/bm25.mjs +85 -0
  146. package/src/context/retriever.mjs +308 -0
  147. package/src/context/skeleton.mjs +134 -0
  148. package/src/context/symbol-indexer.mjs +375 -0
  149. package/src/core/agent-history.mjs +111 -0
  150. package/src/core/agent-loop.mjs +486 -0
  151. package/src/core/approval-log.mjs +104 -0
  152. package/src/core/approval.mjs +476 -0
  153. package/src/core/attachments.mjs +380 -0
  154. package/src/core/backend-url.mjs +55 -0
  155. package/src/core/cache-control.mjs +92 -0
  156. package/src/core/cache.mjs +105 -0
  157. package/src/core/callback-client.mjs +180 -0
  158. package/src/core/checkpoints.mjs +142 -0
  159. package/src/core/compact-history.mjs +127 -0
  160. package/src/core/context-envelope.mjs +54 -0
  161. package/src/core/context-manager.mjs +198 -0
  162. package/src/core/error-guidance.mjs +311 -0
  163. package/src/core/file-diff.mjs +217 -0
  164. package/src/core/headless.mjs +448 -0
  165. package/src/core/hooks-manager.mjs +87 -0
  166. package/src/core/jsonl-writer.mjs +449 -0
  167. package/src/core/local-agent.mjs +537 -0
  168. package/src/core/local-store.mjs +836 -0
  169. package/src/core/mode-selector.mjs +51 -0
  170. package/src/core/output-filter.mjs +177 -0
  171. package/src/core/paths.mjs +190 -0
  172. package/src/core/policy-resolver.mjs +156 -0
  173. package/src/core/pricing.mjs +336 -0
  174. package/src/core/project-artifacts.mjs +39 -0
  175. package/src/core/project-context-loader.mjs +139 -0
  176. package/src/core/providers.mjs +219 -0
  177. package/src/core/rate-limit-display.mjs +121 -0
  178. package/src/core/rate-limiter.mjs +119 -0
  179. package/src/core/resume-mode.mjs +192 -0
  180. package/src/core/risk-tier.mjs +337 -0
  181. package/src/core/safety.mjs +203 -0
  182. package/src/core/scheduler.mjs +173 -0
  183. package/src/core/session-manager.mjs +360 -0
  184. package/src/core/session.mjs +143 -0
  185. package/src/core/settings-sync.mjs +85 -0
  186. package/src/core/stagnation.mjs +57 -0
  187. package/src/core/stream-client.mjs +829 -0
  188. package/src/core/streaming.mjs +182 -0
  189. package/src/core/system-prompt.mjs +140 -0
  190. package/src/core/tasks.mjs +196 -0
  191. package/src/core/tool-executor.mjs +1950 -0
  192. package/src/core/trust.mjs +158 -0
  193. package/src/core/work-scope.mjs +248 -0
  194. package/src/hooks/engine.mjs +162 -0
  195. package/src/index.mjs +426 -0
  196. package/src/mcp/client.mjs +253 -0
  197. package/src/mcp/transport-shttp.mjs +130 -0
  198. package/src/mcp/transport-sse.mjs +131 -0
  199. package/src/mcp/transport-ws.mjs +134 -0
  200. package/src/onboarding/preflight.mjs +360 -0
  201. package/src/permissions/checker.mjs +57 -0
  202. package/src/permissions/command-classifier.mjs +652 -0
  203. package/src/permissions/injection-check.mjs +60 -0
  204. package/src/permissions/path-check.mjs +102 -0
  205. package/src/permissions/prompt.mjs +73 -0
  206. package/src/permissions/sandbox.mjs +112 -0
  207. package/src/plugins/loader.mjs +138 -0
  208. package/src/skills/installer.mjs +188 -0
  209. package/src/skills/loader.mjs +252 -0
  210. package/src/skills/runner.mjs +55 -0
  211. package/src/state/orbit.mjs +263 -0
  212. package/src/state/verbosity.mjs +99 -0
  213. package/src/telemetry/index.mjs +96 -0
  214. package/src/terminal/agents.mjs +177 -0
  215. package/src/terminal/analytics.mjs +292 -0
  216. package/src/terminal/ansi.mjs +695 -0
  217. package/src/terminal/init.mjs +145 -0
  218. package/src/terminal/main.mjs +269 -0
  219. package/src/terminal/repl-explore.mjs +35 -0
  220. package/src/terminal/repl-format.mjs +257 -0
  221. package/src/terminal/repl-render.mjs +561 -0
  222. package/src/terminal/repl-resume.mjs +625 -0
  223. package/src/terminal/repl-state.mjs +103 -0
  224. package/src/terminal/repl-utils.mjs +34 -0
  225. package/src/terminal/repl.mjs +3832 -0
  226. package/src/terminal/skills.mjs +54 -0
  227. package/src/terminal/tool-display.mjs +240 -0
  228. package/src/tools/agent.mjs +137 -0
  229. package/src/tools/ask-user.mjs +61 -0
  230. package/src/tools/bash.mjs +231 -0
  231. package/src/tools/cron-create.mjs +120 -0
  232. package/src/tools/cron-delete.mjs +49 -0
  233. package/src/tools/cron-list.mjs +37 -0
  234. package/src/tools/edit.mjs +82 -0
  235. package/src/tools/enter-worktree.mjs +69 -0
  236. package/src/tools/exit-worktree.mjs +57 -0
  237. package/src/tools/glob.mjs +117 -0
  238. package/src/tools/grep.mjs +129 -0
  239. package/src/tools/lint.mjs +71 -0
  240. package/src/tools/ls.mjs +58 -0
  241. package/src/tools/lsp.mjs +115 -0
  242. package/src/tools/multi-edit.mjs +94 -0
  243. package/src/tools/notebook-edit.mjs +96 -0
  244. package/src/tools/project-overview.mjs +641 -0
  245. package/src/tools/read-mcp-resource.mjs +57 -0
  246. package/src/tools/read.mjs +138 -0
  247. package/src/tools/registry.mjs +116 -0
  248. package/src/tools/remote-trigger.mjs +84 -0
  249. package/src/tools/send-message.mjs +64 -0
  250. package/src/tools/skill.mjs +52 -0
  251. package/src/tools/test-runner.mjs +49 -0
  252. package/src/tools/todo-write.mjs +68 -0
  253. package/src/tools/tool-search.mjs +77 -0
  254. package/src/tools/web-fetch.mjs +65 -0
  255. package/src/tools/web-search.mjs +89 -0
  256. package/src/tools/write.mjs +55 -0
  257. package/src/ui/approval.mjs +263 -0
  258. package/src/ui/banner.mjs +235 -0
  259. package/src/ui/commands.mjs +537 -0
  260. package/src/ui/formatter.mjs +409 -0
  261. package/src/ui/icons.mjs +164 -0
  262. package/src/ui/input-dock.mjs +444 -0
  263. package/src/ui/markdown.mjs +278 -0
  264. package/src/ui/mission-report.mjs +296 -0
  265. package/src/ui/palette.mjs +189 -0
  266. package/src/ui/slash-commands.mjs +245 -0
  267. package/src/ui/spinner.mjs +116 -0
  268. package/src/ui/sub-agent.mjs +152 -0
  269. package/src/ui/term.mjs +159 -0
  270. package/src/ui/text-layout.mjs +127 -0
  271. package/src/ui/tool-card.mjs +463 -0
  272. package/src/ui/tool-details.mjs +312 -0
  273. package/src/ui/transcript-block.mjs +21 -0
@@ -0,0 +1,145 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+
4
+ const FILES = {
5
+ 'README.md': `# .bahulam/ - project agent context
6
+
7
+ Bahulam Code reads and writes here to keep state between sessions.
8
+
9
+ ## Files Bahulam Code writes
10
+ - \`plan.md\` - current agent plan
11
+ - \`goal.md\` - durable session goal
12
+ - \`tasks/\` - task list
13
+ - \`reports/*.md\` - end-of-turn mission reports
14
+ - \`sessions/*.jsonl\` - turn transcripts
15
+ - \`commands.log\` - command executions
16
+ - \`approvals.log\` - HITL decisions
17
+ - \`trust.json\` - approved patterns
18
+
19
+ ## Files you can write
20
+ - \`config.json\` - project policy
21
+ - \`project.md\` - durable project context
22
+ - \`style.md\` - codebase conventions
23
+ - \`hitl.md\` - approval guidance
24
+ - \`skills/<name>/SKILL.md\` - reusable domain skills
25
+
26
+ Format v1. Markdown files are intentionally hand-editable.
27
+ `,
28
+ 'config.json': JSON.stringify({
29
+ version: 1,
30
+ context: {
31
+ loadEveryTurn: ['KEPLER.md', 'project.md', 'style.md', 'goal.md', 'plan.md', 'tasks/*.md'],
32
+ showReloadNotice: true,
33
+ },
34
+ planning: { owner: 'auto', onUserEditedPlan: 'prefer_user_plan' },
35
+ tasks: { storage: 'project_markdown', syncTodoWrite: true, resumePrompt: true },
36
+ hitl: {
37
+ defaultScope: 'once',
38
+ allowSessionTrust: true,
39
+ allowProjectTrust: false,
40
+ reaskAfterMinutes: 30,
41
+ },
42
+ commands: {
43
+ enabled: ['map', 'probe', 'footprint', 'heal', 'align', 'distill', 'brief', 'rewind'],
44
+ dryRunDefault: false,
45
+ },
46
+ }, null, 2) + '\n',
47
+ 'settings.json': JSON.stringify({
48
+ env: {},
49
+ permissions: {
50
+ shellAllowlist: ['git', 'npm', 'pnpm'],
51
+ editDenylist: ['**/*.env', 'secrets/**'],
52
+ },
53
+ hooks: {
54
+ UserPromptSubmit: [],
55
+ PreToolUse: [],
56
+ PostToolUse: [],
57
+ Stop: [],
58
+ },
59
+ }, null, 2) + '\n',
60
+ 'KEPLER.md': `# Project
61
+
62
+ ## Quick Facts
63
+ - Stack:
64
+ - Test command:
65
+ - Lint command:
66
+ - Build command:
67
+
68
+ ## Key Directories
69
+
70
+ ## Code Style
71
+
72
+ ## Critical Rules
73
+ `,
74
+ 'project.md': '# Project Context\n\nAdd durable project context here.\n',
75
+ 'style.md': '# Style\n\nAdd code and communication conventions here.\n',
76
+ 'hitl.md': '# HITL Guidance\n\nAdd project-specific approval guidance here.\n',
77
+ 'trust.json': JSON.stringify({ version: 1, rules: [] }, null, 2) + '\n',
78
+ 'tasks/README.md': `# Bahulam Tasks
79
+
80
+ Checklist files are read every turn.
81
+
82
+ - \`backlog.md\` - pending tasks
83
+ - \`active.md\` - current task
84
+ - \`done.md\` - completed tasks
85
+ - \`blocked.md\` - waiting on input
86
+ `,
87
+ 'tasks/backlog.md': '# Backlog\n\n',
88
+ 'tasks/active.md': '# Active\n\n',
89
+ 'tasks/done.md': '# Done\n\n',
90
+ 'tasks/blocked.md': '# Blocked\n\n',
91
+ 'skills/starter/SKILL.md': `---
92
+ name: starter
93
+ description: Project-specific conventions and setup notes. Use when onboarding to this repo.
94
+ ---
95
+
96
+ # Starter Skill
97
+
98
+ Add reusable project knowledge here.
99
+ `,
100
+ 'commands/onboard.md': `---
101
+ name: onboard
102
+ description: Explore the project and record onboarding notes.
103
+ ---
104
+
105
+ # Onboard
106
+
107
+ Context:
108
+ $ARGUMENTS
109
+
110
+ Explore the codebase, ask clarifying questions, and record useful notes in .bahulam/tasks/active.md.
111
+ `,
112
+ };
113
+
114
+ export function scaffoldKeplerProject({ cwd = process.cwd(), force = false } = {}) {
115
+ const root = path.join(cwd, '.bahulam');
116
+ const written = [];
117
+ const skipped = [];
118
+ for (const [rel, content] of Object.entries(FILES)) {
119
+ const filePath = path.join(root, rel);
120
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
121
+ if (fs.existsSync(filePath) && !force) {
122
+ skipped.push(filePath);
123
+ continue;
124
+ }
125
+ fs.writeFileSync(filePath, content);
126
+ written.push(filePath);
127
+ }
128
+
129
+ const gitignore = path.join(root, '.gitignore');
130
+ if (!fs.existsSync(gitignore) || force) {
131
+ fs.writeFileSync(gitignore, 'settings.local.json\nsessions/\nreports/\n*.log\n');
132
+ written.push(gitignore);
133
+ }
134
+
135
+ return { root, written, skipped };
136
+ }
137
+
138
+ export async function runInitCommand(args = [], { cwd = process.cwd() } = {}) {
139
+ const force = args.includes('--force');
140
+ const result = scaffoldKeplerProject({ cwd, force });
141
+ process.stderr.write(`\x1b[32m✓\x1b[0m Initialized .bahulam at ${result.root}\n`);
142
+ process.stderr.write(` wrote ${result.written.length} files`);
143
+ if (result.skipped.length) process.stderr.write(`, skipped ${result.skipped.length} existing files`);
144
+ process.stderr.write('\n');
145
+ }
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Bahulam Code CLI — ANSI Terminal UI. Bahulam's coding agent.
4
+ * Zero React. Zero Ink. Zero flickering.
5
+ */
6
+
7
+ import { startTerminalRepl } from './repl.mjs';
8
+ import {
9
+ runSessionsCommand,
10
+ runStatsCommand,
11
+ runHistoryCommand,
12
+ } from './analytics.mjs';
13
+ import { parseArgs } from '../config/cli-args.mjs';
14
+
15
+ // ── Subcommands ──
16
+
17
+ const subcommand = process.argv[2];
18
+ const subcommandArgs = process.argv.slice(3);
19
+
20
+ function parseKeplerSubcommandArgs(command, argv) {
21
+ const parsed = {
22
+ command,
23
+ workflowSubcommand: null,
24
+ workflowSlug: null,
25
+ workflowFile: null,
26
+ workflowDir: null,
27
+ agentSubcommand: null,
28
+ agentSlug: null,
29
+ agentDir: null,
30
+ instruction: null,
31
+ pattern: null,
32
+ yes: false,
33
+ verbose: false,
34
+ debug: false,
35
+ };
36
+
37
+ for (let i = 0; i < argv.length; i++) {
38
+ const arg = argv[i];
39
+ switch (arg) {
40
+ case '--file':
41
+ case '-f':
42
+ parsed.workflowFile = argv[++i];
43
+ break;
44
+ case '--dir':
45
+ if (command === 'agent') parsed.agentDir = argv[++i];
46
+ else parsed.workflowDir = argv[++i];
47
+ break;
48
+ case '--instruction':
49
+ case '--input':
50
+ case '--print':
51
+ case '-p':
52
+ parsed.instruction = argv[++i];
53
+ break;
54
+ case '--pattern':
55
+ parsed.pattern = argv[++i];
56
+ break;
57
+ case '--yes':
58
+ case '-y':
59
+ parsed.yes = true;
60
+ break;
61
+ case '--verbose':
62
+ case '-v':
63
+ parsed.verbose = true;
64
+ break;
65
+ case '--debug':
66
+ case '-d':
67
+ parsed.debug = true;
68
+ parsed.verbose = true;
69
+ break;
70
+ default:
71
+ if (arg.startsWith('-')) break;
72
+ if (command === 'workflow') {
73
+ if (!parsed.workflowSubcommand) parsed.workflowSubcommand = arg;
74
+ else if (!parsed.workflowSlug) parsed.workflowSlug = arg;
75
+ } else if (command === 'agent') {
76
+ if (!parsed.agentSubcommand) parsed.agentSubcommand = arg;
77
+ else if (!parsed.agentSlug) parsed.agentSlug = arg;
78
+ }
79
+ break;
80
+ }
81
+ }
82
+
83
+ return parsed;
84
+ }
85
+
86
+ async function main() {
87
+ if (subcommand === 'dashboard') {
88
+ // Launch Bahulam Pulse Next.js dashboard
89
+ const { spawn } = await import('node:child_process');
90
+ const { fileURLToPath } = await import('node:url');
91
+ const path = await import('node:path');
92
+ const cliPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'pulse', 'cli.js');
93
+ const child = spawn(process.execPath, [cliPath, ...subcommandArgs], {
94
+ stdio: 'inherit',
95
+ env: process.env,
96
+ });
97
+ child.on('exit', (code) => process.exit(code ?? 0));
98
+ return;
99
+ }
100
+
101
+ if (subcommand === 'sessions') {
102
+ await runSessionsCommand(subcommandArgs);
103
+ return;
104
+ }
105
+
106
+ if (subcommand === 'stats') {
107
+ await runStatsCommand(subcommandArgs);
108
+ return;
109
+ }
110
+
111
+ if (subcommand === 'history') {
112
+ await runHistoryCommand(subcommandArgs);
113
+ return;
114
+ }
115
+
116
+ if (subcommand === 'init') {
117
+ const { runInitCommand } = await import('./init.mjs');
118
+ await runInitCommand(subcommandArgs);
119
+ return;
120
+ }
121
+
122
+ if (subcommand === 'skills' || subcommand === 'skill') {
123
+ const { runSkillsCommand } = await import('./skills.mjs');
124
+ try {
125
+ await runSkillsCommand(subcommandArgs);
126
+ } catch (err) {
127
+ process.stderr.write(`\x1b[31m✗ Skills command failed: ${err.message}\x1b[0m\n`);
128
+ process.exitCode = 1;
129
+ }
130
+ return;
131
+ }
132
+
133
+ if (subcommand === 'login') {
134
+ const { TarangAuth } = await import('../auth/tarang-auth.mjs');
135
+ const auth = new TarangAuth();
136
+ try {
137
+ await auth.login();
138
+ process.stderr.write('\x1b[32m✓ Login successful!\x1b[0m\n');
139
+ return;
140
+ } catch (err) {
141
+ process.stderr.write(`\x1b[31m✗ Login failed: ${err.message}\x1b[0m\n`);
142
+ process.exit(1);
143
+ }
144
+ }
145
+
146
+ if (subcommand === 'logout') {
147
+ const { TarangAuth } = await import('../auth/tarang-auth.mjs');
148
+ const auth = new TarangAuth();
149
+ const success = auth.logout();
150
+ if (success) {
151
+ process.stderr.write('\x1b[32m✓ Signed out. Credentials cleared.\x1b[0m\n');
152
+ } else {
153
+ process.stderr.write('\x1b[33m! No credentials to clear.\x1b[0m\n');
154
+ }
155
+ return;
156
+ }
157
+
158
+ if (subcommand === 'workflow') {
159
+ const { handleWorkflowCommand } = await import('../commands/workflow.mjs');
160
+ await handleWorkflowCommand(parseKeplerSubcommandArgs('workflow', subcommandArgs));
161
+ return;
162
+ }
163
+
164
+ if (subcommand === 'agent') {
165
+ const { handleAgentCommand } = await import('../commands/agent.mjs');
166
+ await handleAgentCommand(parseKeplerSubcommandArgs('agent', subcommandArgs));
167
+ return;
168
+ }
169
+
170
+ if (subcommand === 'version' || subcommand === '--version' || subcommand === '-v') {
171
+ const { createRequire } = await import('node:module');
172
+ const require = createRequire(import.meta.url);
173
+ const { version } = require('../../package.json');
174
+ process.stdout.write(`Bahulam Code v${version}\n`);
175
+ return;
176
+ }
177
+
178
+ if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
179
+ process.stderr.write(`
180
+ \x1b[1m\x1b[36mBahulam Code\x1b[0m — Bahulam's coding agent — bahulam.ai
181
+
182
+ \x1b[1mUsage:\x1b[0m
183
+ bahulam-code Start interactive REPL
184
+ bahulam-code "instruction" Run a single instruction
185
+ bahulam-code --headless -p "x" Non-interactive: auto-approve, JSONL output
186
+ bahulam-code --headless -p "x" --vision screenshot.png
187
+ Attach an image via the vision analysis pipeline
188
+ bahulam-code --resume Resume last conversation
189
+ bahulam-code dashboard Open analytics dashboard
190
+ bahulam-code login Sign in via browser
191
+ bahulam-code logout Sign out and clear credentials
192
+ bahulam-code init Scaffold .bahulam config, memory, hooks, tasks
193
+ bahulam-code version Show version
194
+
195
+ \x1b[1mAnalytics:\x1b[0m
196
+ bahulam-code sessions List recent local sessions
197
+ bahulam-code stats Show aggregate local session stats
198
+ bahulam-code history Show recent prompt history
199
+
200
+ \x1b[1mSkills:\x1b[0m
201
+ bahulam-code skills list [--all|--project]
202
+ bahulam-code skills view <name> [resource]
203
+ bahulam-code skills install <path-or-git-url> [--project] [--force]
204
+ bahulam-code skills update <name> [--project]
205
+ bahulam-code skills remove <name> [--project]
206
+
207
+ \x1b[1mREPL Commands:\x1b[0m
208
+ /help Show available commands
209
+ /stats Session metrics (tokens, cost, tools)
210
+ /cost Detailed cost breakdown by model
211
+ /model [role] [model] Show or set session model override
212
+ /history Conversation history
213
+ /new Start a new session
214
+ /clear Clear conversation history
215
+ /safety Show safety guardrail status
216
+ /revoke Revoke auto-approvals
217
+ /explore <query> Spawn read-only codebase explorer
218
+ /review <query> Spawn code review agent
219
+ /architect <query> Spawn architecture planning agent
220
+ /agents create <name> Create project-local user-defined agent YAML
221
+ /agents edit <name> Open a local agent YAML in your editor
222
+ /agents sync [name] Sync all or one local agent to Supabase
223
+ /attach <image-path> Attach an image to next prompt
224
+ /attach clipboard Attach image copied to macOS/Windows clipboard
225
+ /exit Exit the REPL
226
+
227
+ \x1b[1mKeyboard:\x1b[0m
228
+ Esc Cancel current execution
229
+ Space Pause / resume execution
230
+ Ctrl+C Exit
231
+
232
+ \x1b[1mEnvironment:\x1b[0m
233
+ TARANG_ENV Set backend (local, treetop, production)
234
+ ANTHROPIC_API_KEY Direct Anthropic API key
235
+ OPENROUTER_API_KEY OpenRouter API key
236
+ BAHULAM_CONFIG_DIR Override config directory (default: ~/.bahulam)
237
+ KEPLER_CONFIG_DIR Legacy config directory override
238
+ KEPLER_RECONNECT_MAX_ELAPSED_MS
239
+ Max reconnect window for dropped streams
240
+ KEPLER_BLOCK_SEPARATOR Tool/content separator: space, dotted, or off
241
+
242
+ \x1b[2mDocs: https://0xb0.ai\x1b[0m
243
+ `);
244
+ return;
245
+ }
246
+
247
+ // ── Headless mode (benchmarks, automation) ──
248
+ const args = parseArgs(process.argv.slice(2));
249
+ if (args.prompt && (process.argv.includes('--headless') || !process.stdin.isTTY)) {
250
+ const { runHeadless } = await import('../core/headless.mjs');
251
+ await runHeadless({
252
+ instruction: args.prompt,
253
+ model: args.model,
254
+ timeout: args.timeout || (args.maxTurns ? args.maxTurns * 60 : 600),
255
+ verbose: args.verbose,
256
+ cacheReport: args.cacheReport,
257
+ local: args.local,
258
+ vision: args.vision,
259
+ });
260
+ return;
261
+ }
262
+
263
+ await startTerminalRepl();
264
+ }
265
+
266
+ main().catch(err => {
267
+ process.stderr.write(`\x1b[31mFatal: ${err.message}\x1b[0m\n`);
268
+ process.exit(1);
269
+ });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Explore-run classifier — pure category lookup for the read/list/search/index
3
+ * tool bursts that get collapsed into one animated summary line during a
4
+ * sub-agent run.
5
+ *
6
+ * These functions are stateless. The mutable run state (counts, recent
7
+ * paths, lineActive flag) still lives in repl.mjs during the split.
8
+ */
9
+
10
+ const EXPLORE_TOOL_CATEGORY = new Map([
11
+ ['read_file', 'read'], ['read', 'read'], ['read_files', 'read'],
12
+ ['read_batch', 'read'], ['get_file_info', 'read'],
13
+ ['list_files', 'list'], ['glob', 'list'], ['ls', 'list'],
14
+ ['search_code', 'search'], ['search_files', 'search'], ['grep', 'search'],
15
+ ['index_project', 'index'], ['register_project', 'index'],
16
+ ]);
17
+
18
+ export function exploreCollapseEnabled() {
19
+ return process.env.KEPLER_EXPLORE_COLLAPSE !== '0';
20
+ }
21
+
22
+ export function isExploreTool(tool) {
23
+ if (!exploreCollapseEnabled()) return false;
24
+ return EXPLORE_TOOL_CATEGORY.has(String(tool || '').toLowerCase());
25
+ }
26
+
27
+ export function exploreCategory(tool) {
28
+ return EXPLORE_TOOL_CATEGORY.get(String(tool || '').toLowerCase()) || 'explore';
29
+ }
30
+
31
+ // Test-only accessor so unit tests can enumerate the recognized tools
32
+ // without importing the private Map.
33
+ export function _knownExploreTools() {
34
+ return [...EXPLORE_TOOL_CATEGORY.keys()];
35
+ }