@axplusb/kepler 0.0.1 → 1.0.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 (218) hide show
  1. package/README.md +82 -0
  2. package/package.json +36 -4
  3. package/pulse/app/activity/page.tsx +190 -0
  4. package/pulse/app/api/activity/route.ts +138 -0
  5. package/pulse/app/api/costs/route.ts +88 -0
  6. package/pulse/app/api/export/route.ts +77 -0
  7. package/pulse/app/api/history/route.ts +11 -0
  8. package/pulse/app/api/import/route.ts +31 -0
  9. package/pulse/app/api/memory/route.ts +52 -0
  10. package/pulse/app/api/plans/route.ts +9 -0
  11. package/pulse/app/api/projects/[slug]/route.ts +96 -0
  12. package/pulse/app/api/projects/route.ts +121 -0
  13. package/pulse/app/api/sessions/[id]/replay/route.ts +20 -0
  14. package/pulse/app/api/sessions/[id]/route.ts +31 -0
  15. package/pulse/app/api/sessions/route.ts +112 -0
  16. package/pulse/app/api/settings/route.ts +14 -0
  17. package/pulse/app/api/stats/route.ts +143 -0
  18. package/pulse/app/api/todos/route.ts +9 -0
  19. package/pulse/app/api/tools/route.ts +160 -0
  20. package/pulse/app/costs/page.tsx +179 -0
  21. package/pulse/app/export/page.tsx +465 -0
  22. package/pulse/app/favicon.ico +0 -0
  23. package/pulse/app/globals.css +263 -0
  24. package/pulse/app/help/page.tsx +142 -0
  25. package/pulse/app/history/page.tsx +157 -0
  26. package/pulse/app/layout.tsx +46 -0
  27. package/pulse/app/memory/page.tsx +365 -0
  28. package/pulse/app/overview-client.tsx +393 -0
  29. package/pulse/app/page.tsx +14 -0
  30. package/pulse/app/plans/page.tsx +308 -0
  31. package/pulse/app/projects/[slug]/page.tsx +390 -0
  32. package/pulse/app/projects/page.tsx +110 -0
  33. package/pulse/app/sessions/[id]/page.tsx +243 -0
  34. package/pulse/app/sessions/page.tsx +39 -0
  35. package/pulse/app/settings/page.tsx +188 -0
  36. package/pulse/app/todos/page.tsx +211 -0
  37. package/pulse/app/tools/page.tsx +249 -0
  38. package/pulse/cli.js +159 -0
  39. package/pulse/components/activity/day-of-week-chart.tsx +35 -0
  40. package/pulse/components/activity/streak-card.tsx +36 -0
  41. package/pulse/components/costs/cache-efficiency-panel.tsx +76 -0
  42. package/pulse/components/costs/cost-by-project-chart.tsx +48 -0
  43. package/pulse/components/costs/cost-over-time-chart.tsx +95 -0
  44. package/pulse/components/costs/model-token-table.tsx +60 -0
  45. package/pulse/components/global-search.tsx +193 -0
  46. package/pulse/components/keyboard-nav-provider.tsx +23 -0
  47. package/pulse/components/layout/bottom-nav.tsx +52 -0
  48. package/pulse/components/layout/client-layout.tsx +31 -0
  49. package/pulse/components/layout/sidebar-context.tsx +50 -0
  50. package/pulse/components/layout/sidebar.tsx +182 -0
  51. package/pulse/components/layout/top-bar.tsx +121 -0
  52. package/pulse/components/overview/activity-heatmap.tsx +107 -0
  53. package/pulse/components/overview/conversation-table.tsx +148 -0
  54. package/pulse/components/overview/model-breakdown-donut.tsx +95 -0
  55. package/pulse/components/overview/peak-hours-chart.tsx +87 -0
  56. package/pulse/components/overview/project-activity-donut.tsx +96 -0
  57. package/pulse/components/overview/stat-card.tsx +102 -0
  58. package/pulse/components/overview/usage-over-time-chart.tsx +166 -0
  59. package/pulse/components/projects/project-card.tsx +175 -0
  60. package/pulse/components/sessions/replay/assistant-markdown.tsx +94 -0
  61. package/pulse/components/sessions/replay/compaction-card.tsx +25 -0
  62. package/pulse/components/sessions/replay/session-sidebar.tsx +231 -0
  63. package/pulse/components/sessions/replay/token-accumulation-chart.tsx +98 -0
  64. package/pulse/components/sessions/replay/tool-call-badge.tsx +127 -0
  65. package/pulse/components/sessions/replay/turn-cards.tsx +220 -0
  66. package/pulse/components/sessions/replay/user-tool-result.tsx +158 -0
  67. package/pulse/components/sessions/session-badges.tsx +49 -0
  68. package/pulse/components/sessions/session-table.tsx +299 -0
  69. package/pulse/components/theme-provider.tsx +44 -0
  70. package/pulse/components/tools/feature-adoption-table.tsx +58 -0
  71. package/pulse/components/tools/mcp-server-panel.tsx +45 -0
  72. package/pulse/components/tools/tool-ranking-chart.tsx +57 -0
  73. package/pulse/components/tools/version-history-table.tsx +32 -0
  74. package/pulse/components/ui/alert.tsx +66 -0
  75. package/pulse/components/ui/badge.tsx +48 -0
  76. package/pulse/components/ui/breadcrumb.tsx +109 -0
  77. package/pulse/components/ui/button.tsx +64 -0
  78. package/pulse/components/ui/calendar.tsx +220 -0
  79. package/pulse/components/ui/card.tsx +92 -0
  80. package/pulse/components/ui/command.tsx +158 -0
  81. package/pulse/components/ui/dialog.tsx +158 -0
  82. package/pulse/components/ui/input.tsx +21 -0
  83. package/pulse/components/ui/popover.tsx +89 -0
  84. package/pulse/components/ui/progress.tsx +31 -0
  85. package/pulse/components/ui/select.tsx +190 -0
  86. package/pulse/components/ui/separator.tsx +28 -0
  87. package/pulse/components/ui/sheet.tsx +143 -0
  88. package/pulse/components/ui/skeleton.tsx +13 -0
  89. package/pulse/components/ui/table.tsx +116 -0
  90. package/pulse/components/ui/tabs.tsx +91 -0
  91. package/pulse/components/ui/tooltip.tsx +57 -0
  92. package/pulse/components/use-global-keyboard-nav.ts +79 -0
  93. package/pulse/components.json +23 -0
  94. package/pulse/eslint.config.mjs +18 -0
  95. package/pulse/lib/claude-reader.ts +594 -0
  96. package/pulse/lib/decode.ts +129 -0
  97. package/pulse/lib/pricing.ts +102 -0
  98. package/pulse/lib/replay-parser.ts +165 -0
  99. package/pulse/lib/tool-categories.ts +127 -0
  100. package/pulse/lib/utils.ts +6 -0
  101. package/pulse/next-env.d.ts +6 -0
  102. package/pulse/next.config.ts +16 -0
  103. package/pulse/package.json +45 -0
  104. package/pulse/postcss.config.mjs +7 -0
  105. package/pulse/public/activity.png +0 -0
  106. package/pulse/public/cc-lens.png +0 -0
  107. package/pulse/public/command-k.png +0 -0
  108. package/pulse/public/costs.png +0 -0
  109. package/pulse/public/dashboard-dark.png +0 -0
  110. package/pulse/public/dashboard-white.png +0 -0
  111. package/pulse/public/export.png +0 -0
  112. package/pulse/public/file.svg +1 -0
  113. package/pulse/public/globe.svg +1 -0
  114. package/pulse/public/next.svg +1 -0
  115. package/pulse/public/projects.png +0 -0
  116. package/pulse/public/session-chat.png +0 -0
  117. package/pulse/public/todos.png +0 -0
  118. package/pulse/public/tools.png +0 -0
  119. package/pulse/public/vercel.svg +1 -0
  120. package/pulse/public/window.svg +1 -0
  121. package/pulse/tsconfig.json +34 -0
  122. package/pulse/types/claude.ts +294 -0
  123. package/src/agents/loader.mjs +89 -0
  124. package/src/agents/parser.mjs +98 -0
  125. package/src/agents/teams.mjs +123 -0
  126. package/src/auth/oauth.mjs +220 -0
  127. package/src/auth/tarang-auth.mjs +277 -0
  128. package/src/config/cli-args.mjs +173 -0
  129. package/src/config/env.mjs +263 -0
  130. package/src/config/settings.mjs +132 -0
  131. package/src/context/ast-parser.mjs +298 -0
  132. package/src/context/bm25.mjs +85 -0
  133. package/src/context/retriever.mjs +270 -0
  134. package/src/context/skeleton.mjs +134 -0
  135. package/src/core/agent-loop.mjs +480 -0
  136. package/src/core/approval.mjs +273 -0
  137. package/src/core/backend-url.mjs +57 -0
  138. package/src/core/cache.mjs +105 -0
  139. package/src/core/callback-client.mjs +149 -0
  140. package/src/core/checkpoints.mjs +142 -0
  141. package/src/core/context-manager.mjs +198 -0
  142. package/src/core/headless.mjs +168 -0
  143. package/src/core/hooks-manager.mjs +87 -0
  144. package/src/core/jsonl-writer.mjs +351 -0
  145. package/src/core/local-agent.mjs +429 -0
  146. package/src/core/local-store.mjs +325 -0
  147. package/src/core/mode-selector.mjs +51 -0
  148. package/src/core/output-filter.mjs +177 -0
  149. package/src/core/paths.mjs +98 -0
  150. package/src/core/pricing.mjs +314 -0
  151. package/src/core/providers.mjs +219 -0
  152. package/src/core/rate-limiter.mjs +119 -0
  153. package/src/core/safety.mjs +200 -0
  154. package/src/core/scheduler.mjs +173 -0
  155. package/src/core/session-manager.mjs +317 -0
  156. package/src/core/session.mjs +143 -0
  157. package/src/core/settings-sync.mjs +85 -0
  158. package/src/core/stagnation.mjs +57 -0
  159. package/src/core/stream-client.mjs +367 -0
  160. package/src/core/streaming.mjs +182 -0
  161. package/src/core/system-prompt.mjs +135 -0
  162. package/src/core/tool-executor.mjs +725 -0
  163. package/src/hooks/engine.mjs +162 -0
  164. package/src/index.mjs +370 -0
  165. package/src/mcp/client.mjs +253 -0
  166. package/src/mcp/transport-shttp.mjs +130 -0
  167. package/src/mcp/transport-sse.mjs +131 -0
  168. package/src/mcp/transport-ws.mjs +134 -0
  169. package/src/permissions/checker.mjs +57 -0
  170. package/src/permissions/command-classifier.mjs +573 -0
  171. package/src/permissions/injection-check.mjs +60 -0
  172. package/src/permissions/path-check.mjs +102 -0
  173. package/src/permissions/prompt.mjs +73 -0
  174. package/src/permissions/sandbox.mjs +112 -0
  175. package/src/plugins/loader.mjs +138 -0
  176. package/src/skills/loader.mjs +147 -0
  177. package/src/skills/runner.mjs +55 -0
  178. package/src/telemetry/index.mjs +96 -0
  179. package/src/terminal/agents.mjs +177 -0
  180. package/src/terminal/analytics.mjs +292 -0
  181. package/src/terminal/ansi.mjs +421 -0
  182. package/src/terminal/main.mjs +150 -0
  183. package/src/terminal/repl.mjs +1484 -0
  184. package/src/terminal/tool-display.mjs +58 -0
  185. package/src/tools/agent.mjs +137 -0
  186. package/src/tools/ask-user.mjs +61 -0
  187. package/src/tools/bash.mjs +148 -0
  188. package/src/tools/cron-create.mjs +120 -0
  189. package/src/tools/cron-delete.mjs +49 -0
  190. package/src/tools/cron-list.mjs +37 -0
  191. package/src/tools/edit.mjs +82 -0
  192. package/src/tools/enter-worktree.mjs +69 -0
  193. package/src/tools/exit-worktree.mjs +57 -0
  194. package/src/tools/glob.mjs +117 -0
  195. package/src/tools/grep.mjs +129 -0
  196. package/src/tools/lint.mjs +71 -0
  197. package/src/tools/ls.mjs +58 -0
  198. package/src/tools/lsp.mjs +115 -0
  199. package/src/tools/multi-edit.mjs +94 -0
  200. package/src/tools/notebook-edit.mjs +96 -0
  201. package/src/tools/read-mcp-resource.mjs +57 -0
  202. package/src/tools/read.mjs +138 -0
  203. package/src/tools/registry.mjs +132 -0
  204. package/src/tools/remote-trigger.mjs +84 -0
  205. package/src/tools/send-message.mjs +64 -0
  206. package/src/tools/skill.mjs +52 -0
  207. package/src/tools/test-runner.mjs +49 -0
  208. package/src/tools/todo-write.mjs +68 -0
  209. package/src/tools/tool-search.mjs +77 -0
  210. package/src/tools/web-fetch.mjs +65 -0
  211. package/src/tools/web-search.mjs +89 -0
  212. package/src/tools/write.mjs +55 -0
  213. package/src/ui/banner.mjs +237 -0
  214. package/src/ui/commands.mjs +499 -0
  215. package/src/ui/formatter.mjs +379 -0
  216. package/src/ui/markdown.mjs +278 -0
  217. package/src/ui/slash-commands.mjs +258 -0
  218. package/index.js +0 -1
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
31
+ "**/*.mts"
32
+ ],
33
+ "exclude": ["node_modules"]
34
+ }
@@ -0,0 +1,294 @@
1
+ // ─── Stats Cache ─────────────────────────────────────────────────────────────
2
+
3
+ export interface DailyActivity {
4
+ date: string
5
+ messageCount: number
6
+ sessionCount: number
7
+ toolCallCount: number
8
+ }
9
+
10
+ export interface DailyTokens {
11
+ date: string
12
+ tokensByModel: Record<string, number>
13
+ }
14
+
15
+ export interface ModelUsage {
16
+ inputTokens: number
17
+ outputTokens: number
18
+ cacheReadInputTokens: number
19
+ cacheCreationInputTokens: number
20
+ costUSD: number
21
+ webSearchRequests: number
22
+ }
23
+
24
+ export interface LongestSession {
25
+ sessionId: string
26
+ duration: number
27
+ messageCount: number
28
+ timestamp: string
29
+ }
30
+
31
+ export interface StatsCache {
32
+ version: number
33
+ lastComputedDate: string
34
+ dailyActivity: DailyActivity[]
35
+ tokensByDate: DailyTokens[]
36
+ dailyModelTokens?: DailyTokens[]
37
+ modelUsage: Record<string, ModelUsage>
38
+ totalSessions: number
39
+ totalMessages: number
40
+ longestSession: LongestSession
41
+ firstSessionDate: string
42
+ hourCounts: Record<string, number>
43
+ totalSpeculationTimeSavedMs: number
44
+ }
45
+
46
+ // ─── Session Meta ─────────────────────────────────────────────────────────────
47
+
48
+ export interface SessionMeta {
49
+ session_id: string
50
+ project_path: string
51
+ start_time: string
52
+ duration_minutes: number
53
+ user_message_count: number
54
+ assistant_message_count: number
55
+ tool_counts: Record<string, number>
56
+ languages: Record<string, number>
57
+ git_commits: number
58
+ git_pushes: number
59
+ input_tokens: number
60
+ output_tokens: number
61
+ cache_creation_input_tokens?: number
62
+ cache_read_input_tokens?: number
63
+ first_prompt: string
64
+ user_interruptions: number
65
+ user_response_times: number[]
66
+ tool_errors: number
67
+ tool_error_categories: Record<string, number>
68
+ uses_task_agent: boolean
69
+ uses_mcp: boolean
70
+ uses_web_search: boolean
71
+ uses_web_fetch: boolean
72
+ lines_added: number
73
+ lines_removed: number
74
+ files_modified: number
75
+ message_hours: number[]
76
+ user_message_timestamps: string[]
77
+ }
78
+
79
+ // ─── Facets ──────────────────────────────────────────────────────────────────
80
+
81
+ export interface Facet {
82
+ session_id: string
83
+ underlying_goal: string
84
+ goal_categories: Record<string, number>
85
+ outcome: string
86
+ user_satisfaction_counts: Record<string, number>
87
+ claude_helpfulness: string
88
+ session_type: string
89
+ friction_counts: Record<string, number>
90
+ friction_detail: string
91
+ primary_success: string
92
+ brief_summary: string
93
+ }
94
+
95
+ // ─── Session with Facet joined ───────────────────────────────────────────────
96
+
97
+ export interface SessionWithFacet extends SessionMeta {
98
+ facet?: Facet
99
+ estimated_cost: number
100
+ slug?: string
101
+ version?: string
102
+ git_branch?: string
103
+ has_compaction?: boolean
104
+ has_thinking?: boolean
105
+ }
106
+
107
+ // ─── Replay / JSONL ──────────────────────────────────────────────────────────
108
+
109
+ export interface TurnUsage {
110
+ input_tokens: number
111
+ output_tokens: number
112
+ cache_creation_input_tokens: number
113
+ cache_read_input_tokens: number
114
+ cache_creation?: {
115
+ ephemeral_5m_input_tokens: number
116
+ ephemeral_1h_input_tokens: number
117
+ }
118
+ service_tier?: string
119
+ inference_geo?: string
120
+ }
121
+
122
+ export interface ToolCall {
123
+ id: string
124
+ name: string
125
+ input: Record<string, unknown>
126
+ result?: string
127
+ is_error?: boolean
128
+ }
129
+
130
+ export interface ReplayTurn {
131
+ uuid: string
132
+ parentUuid: string | null
133
+ type: 'user' | 'assistant'
134
+ timestamp: string
135
+ model?: string
136
+ usage?: TurnUsage
137
+ text?: string
138
+ tool_calls?: ToolCall[]
139
+ tool_results?: Array<{ tool_use_id: string; content: string; is_error: boolean }>
140
+ has_thinking?: boolean
141
+ thinking_text?: string
142
+ estimated_cost?: number
143
+ turn_duration_ms?: number
144
+ response_time_s?: number
145
+ }
146
+
147
+ export interface CompactionEvent {
148
+ uuid: string
149
+ timestamp: string
150
+ trigger: 'auto' | 'manual'
151
+ pre_tokens: number
152
+ summary?: string
153
+ turn_index: number
154
+ }
155
+
156
+ export interface SummaryEvent {
157
+ uuid: string
158
+ summary: string
159
+ leaf_uuid: string
160
+ }
161
+
162
+ export interface ReplayData {
163
+ session_id: string
164
+ slug?: string
165
+ version?: string
166
+ git_branch?: string
167
+ turns: ReplayTurn[]
168
+ compactions: CompactionEvent[]
169
+ summaries: SummaryEvent[]
170
+ total_cost: number
171
+ }
172
+
173
+ // ─── Project Summary ──────────────────────────────────────────────────────────
174
+
175
+ export interface ProjectSummary {
176
+ slug: string
177
+ project_path: string
178
+ display_name: string
179
+ session_count: number
180
+ total_messages: number
181
+ total_duration_minutes: number
182
+ total_lines_added: number
183
+ total_lines_removed: number
184
+ total_files_modified: number
185
+ git_commits: number
186
+ git_pushes: number
187
+ estimated_cost: number
188
+ input_tokens: number
189
+ output_tokens: number
190
+ languages: Record<string, number>
191
+ tool_counts: Record<string, number>
192
+ last_active: string
193
+ first_active: string
194
+ uses_mcp: boolean
195
+ uses_task_agent: boolean
196
+ branches: string[]
197
+ }
198
+
199
+ // ─── Tool Analytics ───────────────────────────────────────────────────────────
200
+
201
+ export interface ToolSummary {
202
+ name: string
203
+ category: string
204
+ total_calls: number
205
+ session_count: number
206
+ error_count: number
207
+ }
208
+
209
+ export interface McpServerSummary {
210
+ server_name: string
211
+ tools: Array<{ name: string; calls: number }>
212
+ total_calls: number
213
+ session_count: number
214
+ }
215
+
216
+ export interface VersionRecord {
217
+ version: string
218
+ session_count: number
219
+ first_seen: string
220
+ last_seen: string
221
+ }
222
+
223
+ export interface ToolsAnalytics {
224
+ tools: ToolSummary[]
225
+ mcp_servers: McpServerSummary[]
226
+ feature_adoption: Record<string, { sessions: number; pct: number }>
227
+ versions: VersionRecord[]
228
+ branches: Array<{ branch: string; turns: number }>
229
+ error_categories: Record<string, number>
230
+ total_tool_calls: number
231
+ total_errors: number
232
+ }
233
+
234
+ // ─── Cost Analytics ───────────────────────────────────────────────────────────
235
+
236
+ export interface ModelCostBreakdown {
237
+ model: string
238
+ input_tokens: number
239
+ output_tokens: number
240
+ cache_write_tokens: number
241
+ cache_read_tokens: number
242
+ estimated_cost: number
243
+ cache_savings: number
244
+ cache_hit_rate: number
245
+ }
246
+
247
+ export interface DailyCost {
248
+ date: string
249
+ costs: Record<string, number>
250
+ total: number
251
+ }
252
+
253
+ export interface ProjectCost {
254
+ slug: string
255
+ display_name: string
256
+ estimated_cost: number
257
+ input_tokens: number
258
+ output_tokens: number
259
+ }
260
+
261
+ export interface CostAnalytics {
262
+ total_cost: number
263
+ total_savings: number
264
+ models: ModelCostBreakdown[]
265
+ daily: DailyCost[]
266
+ by_project: ProjectCost[]
267
+ }
268
+
269
+ // ─── History ──────────────────────────────────────────────────────────────────
270
+
271
+ export interface HistoryEntry {
272
+ display: string
273
+ timestamp: number
274
+ project: string
275
+ sessionId?: string
276
+ }
277
+
278
+ // ─── Export ──────────────────────────────────────────────────────────────────
279
+
280
+ export interface ExportPayload {
281
+ exportedAt: string
282
+ version: string
283
+ stats: StatsCache | null
284
+ sessions: SessionMeta[]
285
+ facets: Facet[]
286
+ history: HistoryEntry[]
287
+ }
288
+
289
+ export interface ImportDiff {
290
+ total_in_export: number
291
+ already_present: number
292
+ new_sessions: number
293
+ sessions_to_add: SessionMeta[]
294
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Agent Loader — loads custom agent definitions from .claude/agents/
3
+ *
4
+ * Supports two formats:
5
+ * - JSON: .claude/agents/*.json
6
+ * - Markdown with YAML frontmatter: .claude/agents/*.md
7
+ *
8
+ * Agent definitions specify: name, description, model, tools, hooks, prompt.
9
+ */
10
+
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import { parseAgentDefinition } from './parser.mjs';
14
+
15
+ export class AgentLoader {
16
+ constructor() {
17
+ this.agents = new Map();
18
+ this.searchPaths = [];
19
+ }
20
+
21
+ /**
22
+ * Load agents from standard directories.
23
+ * @param {string} [cwd] - project working directory
24
+ */
25
+ load(cwd = process.cwd()) {
26
+ this.searchPaths = [
27
+ path.join(cwd, '.claude', 'agents'),
28
+ path.join(process.env.HOME || '', '.claude', 'agents'),
29
+ ];
30
+
31
+ for (const dir of this.searchPaths) {
32
+ this._loadFromDir(dir);
33
+ }
34
+
35
+ return this;
36
+ }
37
+
38
+ _loadFromDir(dir) {
39
+ try {
40
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
41
+ for (const entry of entries) {
42
+ if (!entry.isFile()) continue;
43
+ const ext = path.extname(entry.name);
44
+ if (ext !== '.json' && ext !== '.md') continue;
45
+
46
+ const filePath = path.join(dir, entry.name);
47
+ try {
48
+ const content = fs.readFileSync(filePath, 'utf-8');
49
+ const agent = parseAgentDefinition(content, ext);
50
+ if (agent && agent.name) {
51
+ this.agents.set(agent.name, { ...agent, source: filePath });
52
+ }
53
+ } catch (err) {
54
+ if (process.env.DEBUG) {
55
+ console.error(`Failed to load agent ${filePath}: ${err.message}`);
56
+ }
57
+ }
58
+ }
59
+ } catch {
60
+ // Directory does not exist
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get an agent definition by name.
66
+ * @param {string} name
67
+ * @returns {object|null}
68
+ */
69
+ get(name) {
70
+ return this.agents.get(name) || null;
71
+ }
72
+
73
+ /**
74
+ * List all loaded agents.
75
+ * @returns {Array<object>}
76
+ */
77
+ list() {
78
+ return [...this.agents.values()];
79
+ }
80
+
81
+ /**
82
+ * Check if an agent exists.
83
+ * @param {string} name
84
+ * @returns {boolean}
85
+ */
86
+ has(name) {
87
+ return this.agents.has(name);
88
+ }
89
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Agent Parser — parses agent definitions from JSON and Markdown formats.
3
+ *
4
+ * JSON format:
5
+ * {
6
+ * "name": "my-agent",
7
+ * "description": "Does things",
8
+ * "model": "claude-sonnet-4-6",
9
+ * "tools": ["Bash", "Read", "Write"],
10
+ * "hooks": { ... },
11
+ * "prompt": "You are a specialized agent..."
12
+ * }
13
+ *
14
+ * Markdown format (YAML frontmatter):
15
+ * ---
16
+ * name: my-agent
17
+ * description: Does things
18
+ * model: claude-sonnet-4-6
19
+ * tools: [Bash, Read, Write]
20
+ * ---
21
+ * You are a specialized agent...
22
+ */
23
+
24
+ /**
25
+ * Parse an agent definition from file content.
26
+ * @param {string} content - file content
27
+ * @param {string} ext - file extension (.json or .md)
28
+ * @returns {object} agent definition
29
+ */
30
+ export function parseAgentDefinition(content, ext) {
31
+ if (ext === '.json') {
32
+ return parseJsonAgent(content);
33
+ }
34
+ if (ext === '.md') {
35
+ return parseMarkdownAgent(content);
36
+ }
37
+ throw new Error(`Unsupported agent format: ${ext}`);
38
+ }
39
+
40
+ function parseJsonAgent(content) {
41
+ const data = JSON.parse(content);
42
+ return normalizeAgent(data);
43
+ }
44
+
45
+ function parseMarkdownAgent(content) {
46
+ const { frontmatter, body } = parseFrontmatter(content);
47
+ return normalizeAgent({ ...frontmatter, prompt: body });
48
+ }
49
+
50
+ /**
51
+ * Parse YAML-like frontmatter from markdown.
52
+ * Simple key-value parser (no full YAML dependency).
53
+ */
54
+ function parseFrontmatter(content) {
55
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
56
+ if (!match) {
57
+ return { frontmatter: {}, body: content };
58
+ }
59
+
60
+ const yamlBlock = match[1];
61
+ const body = match[2].trim();
62
+ const frontmatter = {};
63
+
64
+ for (const line of yamlBlock.split('\n')) {
65
+ const colonIdx = line.indexOf(':');
66
+ if (colonIdx === -1) continue;
67
+
68
+ const key = line.slice(0, colonIdx).trim();
69
+ let value = line.slice(colonIdx + 1).trim();
70
+
71
+ // Parse arrays: [a, b, c]
72
+ if (value.startsWith('[') && value.endsWith(']')) {
73
+ value = value.slice(1, -1).split(',').map(s => s.trim()).filter(Boolean);
74
+ }
75
+ // Parse booleans
76
+ else if (value === 'true') value = true;
77
+ else if (value === 'false') value = false;
78
+ // Parse numbers
79
+ else if (/^\d+$/.test(value)) value = parseInt(value, 10);
80
+
81
+ frontmatter[key] = value;
82
+ }
83
+
84
+ return { frontmatter, body };
85
+ }
86
+
87
+ function normalizeAgent(data) {
88
+ return {
89
+ name: data.name || 'unnamed',
90
+ description: data.description || '',
91
+ model: data.model || null,
92
+ tools: Array.isArray(data.tools) ? data.tools : [],
93
+ hooks: data.hooks || {},
94
+ prompt: data.prompt || '',
95
+ maxTurns: data.maxTurns || data.max_turns || 10,
96
+ temperature: data.temperature || undefined,
97
+ };
98
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Agent Teams — multi-agent coordination.
3
+ *
4
+ * Allows registering named agents that can communicate via messages.
5
+ * Each teammate is an agent loop that can be invoked with a prompt.
6
+ */
7
+
8
+ export class AgentTeams {
9
+ constructor() {
10
+ /** @type {Map<string, { loop: object, role?: string, status: string }>} */
11
+ this.teammates = new Map();
12
+ this.messageLog = [];
13
+ }
14
+
15
+ /**
16
+ * Register a named agent.
17
+ * @param {string} name - unique agent name
18
+ * @param {object} agentLoop - agent loop with .run() async generator
19
+ * @param {object} [options]
20
+ * @param {string} [options.role] - agent role description
21
+ */
22
+ register(name, agentLoop, options = {}) {
23
+ if (this.teammates.has(name)) {
24
+ throw new Error(`Agent "${name}" is already registered`);
25
+ }
26
+ this.teammates.set(name, {
27
+ loop: agentLoop,
28
+ role: options.role || 'general',
29
+ status: 'idle',
30
+ });
31
+ }
32
+
33
+ /**
34
+ * Unregister an agent.
35
+ * @param {string} name
36
+ * @returns {boolean}
37
+ */
38
+ unregister(name) {
39
+ return this.teammates.delete(name);
40
+ }
41
+
42
+ /**
43
+ * Send a message to a teammate and collect all events.
44
+ * @param {string} to - target agent name
45
+ * @param {string} message - prompt to send
46
+ * @returns {Promise<Array<object>>} collected events
47
+ */
48
+ async sendMessage(to, message) {
49
+ const agent = this.teammates.get(to);
50
+ if (!agent) throw new Error(`Unknown teammate: ${to}`);
51
+
52
+ agent.status = 'running';
53
+ const results = [];
54
+
55
+ try {
56
+ for await (const event of agent.loop.run(message)) {
57
+ results.push(event);
58
+ }
59
+ } finally {
60
+ agent.status = 'idle';
61
+ }
62
+
63
+ this.messageLog.push({
64
+ to,
65
+ message: message.substring(0, 100),
66
+ resultCount: results.length,
67
+ timestamp: new Date().toISOString(),
68
+ });
69
+
70
+ return results;
71
+ }
72
+
73
+ /**
74
+ * Broadcast a message to all teammates.
75
+ * @param {string} message
76
+ * @returns {Promise<Map<string, Array<object>>>} results per agent
77
+ */
78
+ async broadcast(message) {
79
+ const results = new Map();
80
+ const promises = [];
81
+
82
+ for (const [name] of this.teammates) {
83
+ promises.push(
84
+ this.sendMessage(name, message)
85
+ .then(events => results.set(name, events))
86
+ .catch(err => results.set(name, [{ type: 'error', message: err.message }]))
87
+ );
88
+ }
89
+
90
+ await Promise.all(promises);
91
+ return results;
92
+ }
93
+
94
+ /**
95
+ * List all registered teammates.
96
+ * @returns {Array<{ name: string, role: string, status: string }>}
97
+ */
98
+ list() {
99
+ return [...this.teammates.entries()].map(([name, info]) => ({
100
+ name,
101
+ role: info.role,
102
+ status: info.status,
103
+ }));
104
+ }
105
+
106
+ /**
107
+ * Get the message log.
108
+ * @param {number} [limit] - max entries to return
109
+ * @returns {Array<object>}
110
+ */
111
+ getMessageLog(limit) {
112
+ if (limit) return this.messageLog.slice(-limit);
113
+ return [...this.messageLog];
114
+ }
115
+
116
+ /**
117
+ * Get count of registered teammates.
118
+ * @returns {number}
119
+ */
120
+ size() {
121
+ return this.teammates.size;
122
+ }
123
+ }