@iamharshil/cortex 5.0.0 → 5.0.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.
package/bin/tui.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "child_process";
3
+ import { fileURLToPath } from "url";
4
+ import { dirname, join } from "path";
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const tuiPath = join(__dirname, "dist", "tui", "index.js");
8
+
9
+ const args = process.argv.slice(2).filter((a) => a !== "--tui");
10
+
11
+ const proc = spawn("node", [tuiPath, ...args], {
12
+ stdio: "inherit",
13
+ detached: false,
14
+ shell: false,
15
+ });
16
+
17
+ proc.on("exit", (code) => {
18
+ process.exit(code || 0);
19
+ });
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/tui/index.tsx
4
+ import React3 from "react";
5
+ import { render } from "ink";
6
+
7
+ // src/tui/App.tsx
8
+ import { useState as useState2 } from "react";
9
+ import { Box as Box5 } from "ink";
10
+
11
+ // src/tui/theme.ts
12
+ var colors = {
13
+ primary: "#00D9FF",
14
+ secondary: "#FF79C6",
15
+ accent: "#50FA7B",
16
+ error: "#FF5555",
17
+ warning: "#FFB86C",
18
+ success: "#50FA7B",
19
+ dim: "#6272A4",
20
+ bg: "#282A36",
21
+ bgDim: "#44475A",
22
+ fg: "#F8F8F2",
23
+ fgDim: "#BDBDBD"
24
+ };
25
+ var theme = {
26
+ colors,
27
+ border: {
28
+ color: colors.dim,
29
+ accent: colors.primary
30
+ },
31
+ spacing: {
32
+ xs: 1,
33
+ sm: 2,
34
+ md: 4,
35
+ lg: 8
36
+ },
37
+ font: {
38
+ mono: "monospace"
39
+ }
40
+ };
41
+
42
+ // src/tui/components/Chat.tsx
43
+ import { Box, Text } from "ink";
44
+ import { jsx, jsxs } from "react/jsx-runtime";
45
+ var Chat = ({
46
+ messages,
47
+ isStreaming,
48
+ currentToolCall
49
+ }) => {
50
+ const renderMessage = (msg, idx) => {
51
+ const roleColors = {
52
+ user: theme.colors.primary,
53
+ assistant: theme.colors.secondary,
54
+ system: theme.colors.warning,
55
+ tool: theme.colors.accent
56
+ };
57
+ const roleLabels = {
58
+ user: "You",
59
+ assistant: "Cortex",
60
+ system: "System",
61
+ tool: msg.name || "Tool"
62
+ };
63
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
64
+ /* @__PURE__ */ jsx(Text, { bold: true, color: roleColors[msg.role], children: roleLabels[msg.role] }),
65
+ /* @__PURE__ */ jsx(Text, { children: msg.content }),
66
+ msg.tool_calls && msg.tool_calls.length > 0 && /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: msg.tool_calls.map((tc) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [
67
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
68
+ "\u2192 ",
69
+ tc.name,
70
+ "(",
71
+ JSON.stringify(tc.arguments),
72
+ ")"
73
+ ] }),
74
+ tc.status === "completed" && tc.result && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
75
+ "Result: ",
76
+ tc.result.slice(0, 200)
77
+ ] })
78
+ ] }, tc.id)) })
79
+ ] }, msg.id);
80
+ };
81
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", overflow: "hidden", children: messages.length === 0 ? /* @__PURE__ */ jsxs(
82
+ Box,
83
+ {
84
+ flexDirection: "column",
85
+ justifyContent: "center",
86
+ alignItems: "center",
87
+ flex: 1,
88
+ children: [
89
+ /* @__PURE__ */ jsx(Text, { bold: true, color: theme.colors.primary, children: "Cortex AI" }),
90
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type your message below to start coding" }),
91
+ isStreaming && /* @__PURE__ */ jsx(Text, { color: theme.colors.accent, italic: true, children: "Thinking..." })
92
+ ]
93
+ }
94
+ ) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
95
+ messages.map((msg, idx) => renderMessage(msg, idx)),
96
+ isStreaming && /* @__PURE__ */ jsx(Text, { color: theme.colors.accent, italic: true, children: "\u258A" }),
97
+ currentToolCall && /* @__PURE__ */ jsx(
98
+ Box,
99
+ {
100
+ marginY: 1,
101
+ paddingX: 2,
102
+ borderStyle: "round",
103
+ borderColor: theme.colors.warning,
104
+ children: /* @__PURE__ */ jsxs(Text, { color: theme.colors.warning, children: [
105
+ "\u26A1 Running: ",
106
+ currentToolCall.name
107
+ ] })
108
+ }
109
+ )
110
+ ] }) });
111
+ };
112
+
113
+ // src/tui/components/Input.tsx
114
+ import { useState } from "react";
115
+ import { Box as Box2, Text as Text2, useInput } from "ink";
116
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
117
+ var Input = ({
118
+ value,
119
+ onChange,
120
+ onSubmit,
121
+ disabled
122
+ }) => {
123
+ const [history, setHistory] = useState([]);
124
+ const [historyIndex, setHistoryIndex] = useState(-1);
125
+ useInput((input, key) => {
126
+ if (disabled) return;
127
+ if (key.return) {
128
+ if (value.trim()) {
129
+ setHistory((prev) => [value, ...prev.slice(0, 49)]);
130
+ setHistoryIndex(-1);
131
+ onSubmit(value);
132
+ }
133
+ } else if (key.upArrow) {
134
+ if (historyIndex < history.length - 1) {
135
+ const newIndex = historyIndex + 1;
136
+ setHistoryIndex(newIndex);
137
+ onChange(history[newIndex]);
138
+ }
139
+ } else if (key.downArrow) {
140
+ if (historyIndex > 0) {
141
+ const newIndex = historyIndex - 1;
142
+ setHistoryIndex(newIndex);
143
+ onChange(history[newIndex]);
144
+ } else if (historyIndex === 0) {
145
+ setHistoryIndex(-1);
146
+ onChange("");
147
+ }
148
+ } else if (key.delete || key.backspace) {
149
+ onChange(value.slice(0, -1));
150
+ } else if (input) {
151
+ onChange(value + input);
152
+ }
153
+ });
154
+ return /* @__PURE__ */ jsxs2(
155
+ Box2,
156
+ {
157
+ flexDirection: "row",
158
+ alignItems: "center",
159
+ paddingX: 2,
160
+ paddingY: 1,
161
+ borderStyle: "single",
162
+ borderColor: theme.colors.dim,
163
+ children: [
164
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.colors.primary, children: "\u276F" }),
165
+ /* @__PURE__ */ jsx2(Box2, { flex: 1, marginLeft: 1, children: /* @__PURE__ */ jsx2(Text2, { color: disabled ? theme.colors.dim : theme.colors.fg, children: disabled ? "Processing..." : value || "Type a message..." }) }),
166
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " Enter | \u2191\u2193 | Ctrl+C " })
167
+ ]
168
+ }
169
+ );
170
+ };
171
+
172
+ // src/tui/components/StatusBar.tsx
173
+ import { Box as Box3, Text as Text3 } from "ink";
174
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
175
+ var StatusBar = ({
176
+ view,
177
+ onViewChange,
178
+ provider: provider2,
179
+ model: model2,
180
+ messages
181
+ }) => {
182
+ return /* @__PURE__ */ jsxs3(
183
+ Box3,
184
+ {
185
+ flexDirection: "row",
186
+ justifyContent: "space-between",
187
+ paddingX: 2,
188
+ paddingY: 1,
189
+ borderStyle: "single",
190
+ borderColor: theme.colors.dim,
191
+ children: [
192
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", children: [
193
+ /* @__PURE__ */ jsx3(
194
+ Text3,
195
+ {
196
+ bold: true,
197
+ color: view === "chat" ? theme.colors.primary : theme.colors.dim,
198
+ onPress: () => onViewChange("chat"),
199
+ children: "[Chat]"
200
+ }
201
+ ),
202
+ /* @__PURE__ */ jsx3(Text3, { color: theme.colors.dim, children: " | " }),
203
+ /* @__PURE__ */ jsx3(
204
+ Text3,
205
+ {
206
+ bold: true,
207
+ color: view === "sessions" ? theme.colors.primary : theme.colors.dim,
208
+ onPress: () => onViewChange("sessions"),
209
+ children: "[Sessions]"
210
+ }
211
+ ),
212
+ /* @__PURE__ */ jsx3(Text3, { color: theme.colors.dim, children: " | " }),
213
+ /* @__PURE__ */ jsx3(
214
+ Text3,
215
+ {
216
+ bold: true,
217
+ color: view === "settings" ? theme.colors.primary : theme.colors.dim,
218
+ onPress: () => onViewChange("settings"),
219
+ children: "[Settings]"
220
+ }
221
+ )
222
+ ] }),
223
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", children: [
224
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
225
+ provider2,
226
+ "/",
227
+ model2
228
+ ] }),
229
+ /* @__PURE__ */ jsx3(Text3, { color: theme.colors.dim, children: " | " }),
230
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
231
+ "Messages: ",
232
+ messages
233
+ ] })
234
+ ] })
235
+ ]
236
+ }
237
+ );
238
+ };
239
+
240
+ // src/tui/components/Sidebar.tsx
241
+ import { Box as Box4, Text as Text4 } from "ink";
242
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
243
+ var Sidebar = ({
244
+ sessions,
245
+ activeSessionId,
246
+ onSelectSession
247
+ }) => {
248
+ const formatDate = (timestamp) => {
249
+ const date = new Date(timestamp);
250
+ return date.toLocaleDateString() + " " + date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
251
+ };
252
+ return /* @__PURE__ */ jsxs4(
253
+ Box4,
254
+ {
255
+ width: 30,
256
+ flexDirection: "column",
257
+ borderStyle: "single",
258
+ borderColor: theme.colors.dim,
259
+ marginRight: 1,
260
+ children: [
261
+ /* @__PURE__ */ jsx4(
262
+ Box4,
263
+ {
264
+ paddingX: 2,
265
+ paddingY: 1,
266
+ borderStyle: "bold",
267
+ borderColor: theme.colors.primary,
268
+ children: /* @__PURE__ */ jsx4(Text4, { bold: true, color: theme.colors.primary, children: "Sessions" })
269
+ }
270
+ ),
271
+ /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", flex: 1, children: sessions.length === 0 ? /* @__PURE__ */ jsx4(Box4, { justifyContent: "center", alignItems: "center", flex: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No sessions yet" }) }) : sessions.map((session) => /* @__PURE__ */ jsxs4(
272
+ Box4,
273
+ {
274
+ flexDirection: "column",
275
+ paddingX: 2,
276
+ paddingY: 1,
277
+ borderStyle: "none",
278
+ onPress: () => onSelectSession(session.id),
279
+ children: [
280
+ /* @__PURE__ */ jsx4(
281
+ Text4,
282
+ {
283
+ bold: true,
284
+ color: session.id === activeSessionId ? theme.colors.primary : theme.colors.fg,
285
+ children: session.name
286
+ }
287
+ ),
288
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, font: "monospace", children: formatDate(session.updatedAt) })
289
+ ]
290
+ },
291
+ session.id
292
+ )) })
293
+ ]
294
+ }
295
+ );
296
+ };
297
+
298
+ // src/tui/App.tsx
299
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
300
+ var App = ({ cwd: cwd2, provider: provider2, model: model2 }) => {
301
+ const [messages, setMessages] = useState2([]);
302
+ const [input, setInput] = useState2("");
303
+ const [isStreaming, setIsStreaming] = useState2(false);
304
+ const [currentToolCall, setCurrentToolCall] = useState2(null);
305
+ const [sessions, setSessions] = useState2([]);
306
+ const [activeView, setActiveView] = useState2("chat");
307
+ const [sessionId, setSessionId] = useState2(null);
308
+ const handleSubmit = async (value) => {
309
+ if (!value.trim() || isStreaming) return;
310
+ const userMessage = {
311
+ id: Date.now().toString(),
312
+ role: "user",
313
+ content: value,
314
+ timestamp: Date.now()
315
+ };
316
+ setMessages((prev) => [...prev, userMessage]);
317
+ setInput("");
318
+ setIsStreaming(true);
319
+ setTimeout(() => {
320
+ const assistantMessage = {
321
+ id: (Date.now() + 1).toString(),
322
+ role: "assistant",
323
+ content: "Thinking...",
324
+ timestamp: Date.now()
325
+ };
326
+ setMessages((prev) => [...prev, assistantMessage]);
327
+ setIsStreaming(false);
328
+ }, 500);
329
+ };
330
+ const handleToolCall = (toolCall) => {
331
+ setCurrentToolCall(toolCall);
332
+ };
333
+ const handleToolComplete = (toolCallId, result) => {
334
+ setCurrentToolCall(null);
335
+ setMessages(
336
+ (prev) => prev.map(
337
+ (msg) => msg.tool_calls?.map(
338
+ (tc) => tc.id === toolCallId ? { ...tc, status: "completed", result } : tc
339
+ )
340
+ )
341
+ );
342
+ };
343
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", height: 100, children: [
344
+ /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", flex: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", flex: 1, children: [
345
+ activeView === "sessions" && /* @__PURE__ */ jsx5(
346
+ Sidebar,
347
+ {
348
+ sessions,
349
+ activeSessionId: sessionId,
350
+ onSelectSession: (id) => setSessionId(id)
351
+ }
352
+ ),
353
+ /* @__PURE__ */ jsxs5(
354
+ Box5,
355
+ {
356
+ flexDirection: "column",
357
+ flex: 1,
358
+ borderStyle: "round",
359
+ borderColor: theme.colors.dim,
360
+ children: [
361
+ /* @__PURE__ */ jsx5(
362
+ Box5,
363
+ {
364
+ paddingX: 1,
365
+ borderStyle: "bold",
366
+ borderColor: theme.colors.primary,
367
+ flex: 1,
368
+ overflow: "hidden",
369
+ children: /* @__PURE__ */ jsx5(
370
+ Chat,
371
+ {
372
+ messages,
373
+ isStreaming,
374
+ currentToolCall
375
+ }
376
+ )
377
+ }
378
+ ),
379
+ /* @__PURE__ */ jsx5(
380
+ Input,
381
+ {
382
+ value: input,
383
+ onChange: setInput,
384
+ onSubmit: handleSubmit,
385
+ disabled: isStreaming
386
+ }
387
+ )
388
+ ]
389
+ }
390
+ )
391
+ ] }) }),
392
+ /* @__PURE__ */ jsx5(
393
+ StatusBar,
394
+ {
395
+ view: activeView,
396
+ onViewChange: setActiveView,
397
+ provider: provider2,
398
+ model: model2 || "default",
399
+ messages: messages.length
400
+ }
401
+ )
402
+ ] });
403
+ };
404
+
405
+ // src/tui/index.tsx
406
+ var args = process.argv.slice(2);
407
+ var cwd = process.cwd();
408
+ var isTui = args.length === 0 || args.includes("--tui") || args.some((a) => !a.startsWith("--"));
409
+ var provider = "ollama";
410
+ var model = "llama3.2";
411
+ for (let i = 0; i < args.length; i++) {
412
+ if (args[i] === "--provider" && args[i + 1]) {
413
+ provider = args[i + 1];
414
+ i++;
415
+ } else if (args[i] === "--model" && args[i + 1]) {
416
+ model = args[i + 1];
417
+ i++;
418
+ }
419
+ }
420
+ if (isTui) {
421
+ const { waitUntilExit } = render(
422
+ React3.createElement(App, { cwd, provider, model })
423
+ );
424
+ waitUntilExit().then(() => {
425
+ console.log("\nGoodbye!");
426
+ process.exit(0);
427
+ });
428
+ } else {
429
+ console.log("Use 'cortex' without arguments to start TUI mode");
430
+ process.exit(1);
431
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iamharshil/cortex",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "The ultimate local AI coding agent - context-aware, memory-powered, MCP-enabled",
5
5
  "keywords": [
6
6
  "cli",
@@ -30,13 +30,14 @@
30
30
  },
31
31
  "files": [
32
32
  "dist",
33
+ "bin",
33
34
  "README.md",
34
35
  "LICENSE"
35
36
  ],
36
37
  "scripts": {
37
38
  "build": "node scripts/build.js",
38
39
  "build:tui": "node scripts/build-tui.js",
39
- "prepublishOnly": "npm run build",
40
+ "prepublishOnly": "npm run build && npm run build:tui",
40
41
  "dev": "tsx src/index.ts",
41
42
  "dev:tui": "tsx src/tui/index.tsx",
42
43
  "start": "node bin/index.js",