codeep 1.2.10 → 1.2.12

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 (88) hide show
  1. package/dist/api/index.js +7 -7
  2. package/dist/config/index.js +3 -3
  3. package/dist/config/providers.d.ts +6 -0
  4. package/dist/config/providers.js +41 -2
  5. package/dist/config/providers.test.js +31 -2
  6. package/dist/hooks/index.js +1 -1
  7. package/dist/hooks/useAgent.js +3 -3
  8. package/dist/renderer/App.js +8 -8
  9. package/dist/renderer/ChatUI.js +3 -3
  10. package/dist/renderer/Screen.js +1 -1
  11. package/dist/renderer/components/Export.js +1 -1
  12. package/dist/renderer/components/Help.js +1 -1
  13. package/dist/renderer/components/Intro.js +1 -1
  14. package/dist/renderer/components/Login.js +3 -3
  15. package/dist/renderer/components/Logout.js +1 -1
  16. package/dist/renderer/components/Modal.js +2 -2
  17. package/dist/renderer/components/Permission.js +2 -2
  18. package/dist/renderer/components/Search.js +1 -1
  19. package/dist/renderer/components/SelectScreen.js +1 -1
  20. package/dist/renderer/components/Settings.js +3 -3
  21. package/dist/renderer/components/Status.js +1 -1
  22. package/dist/renderer/demo-app.js +1 -1
  23. package/dist/renderer/demo.js +1 -1
  24. package/dist/renderer/index.js +9 -9
  25. package/dist/renderer/main.js +31 -31
  26. package/dist/utils/agent.js +10 -10
  27. package/dist/utils/agent.test.js +1 -1
  28. package/dist/utils/codeReview.js +1 -1
  29. package/dist/utils/context.js +1 -1
  30. package/dist/utils/git.test.js +1 -1
  31. package/dist/utils/gitignore.test.js +1 -1
  32. package/dist/utils/keychain.js +1 -1
  33. package/dist/utils/project.test.js +1 -1
  34. package/dist/utils/ratelimit.js +1 -1
  35. package/dist/utils/ratelimit.test.js +1 -1
  36. package/dist/utils/retry.test.js +1 -1
  37. package/dist/utils/smartContext.js +1 -1
  38. package/dist/utils/smartContext.test.js +1 -1
  39. package/dist/utils/taskPlanner.js +2 -2
  40. package/dist/utils/tools.d.ts +64 -4
  41. package/dist/utils/tools.js +212 -7
  42. package/dist/utils/tools.test.js +1 -1
  43. package/dist/utils/validation.test.js +1 -1
  44. package/dist/utils/verify.js +1 -1
  45. package/package.json +1 -1
  46. package/bin/codeep.js +0 -2
  47. package/dist/app.d.ts +0 -2
  48. package/dist/app.js +0 -1501
  49. package/dist/components/AgentActions.d.ts +0 -18
  50. package/dist/components/AgentActions.js +0 -122
  51. package/dist/components/AgentProgress.d.ts +0 -59
  52. package/dist/components/AgentProgress.js +0 -368
  53. package/dist/components/Export.d.ts +0 -8
  54. package/dist/components/Export.js +0 -27
  55. package/dist/components/Help.d.ts +0 -6
  56. package/dist/components/Help.js +0 -7
  57. package/dist/components/Input.d.ts +0 -9
  58. package/dist/components/Input.js +0 -334
  59. package/dist/components/Loading.d.ts +0 -17
  60. package/dist/components/Loading.js +0 -52
  61. package/dist/components/Login.d.ts +0 -7
  62. package/dist/components/Login.js +0 -77
  63. package/dist/components/Logo.d.ts +0 -8
  64. package/dist/components/Logo.js +0 -89
  65. package/dist/components/LogoutPicker.d.ts +0 -8
  66. package/dist/components/LogoutPicker.js +0 -61
  67. package/dist/components/Message.d.ts +0 -10
  68. package/dist/components/Message.js +0 -242
  69. package/dist/components/MessageList.d.ts +0 -10
  70. package/dist/components/MessageList.js +0 -42
  71. package/dist/components/ProjectPermission.d.ts +0 -7
  72. package/dist/components/ProjectPermission.js +0 -65
  73. package/dist/components/Search.d.ts +0 -10
  74. package/dist/components/Search.js +0 -30
  75. package/dist/components/SessionPicker.d.ts +0 -9
  76. package/dist/components/SessionPicker.js +0 -88
  77. package/dist/components/Sessions.d.ts +0 -12
  78. package/dist/components/Sessions.js +0 -119
  79. package/dist/components/Settings.d.ts +0 -9
  80. package/dist/components/Settings.js +0 -198
  81. package/dist/components/Spinner.d.ts +0 -34
  82. package/dist/components/Spinner.js +0 -38
  83. package/dist/components/Status.d.ts +0 -2
  84. package/dist/components/Status.js +0 -13
  85. package/dist/components/StreamingMessage.d.ts +0 -14
  86. package/dist/components/StreamingMessage.js +0 -19
  87. package/dist/index.d.ts +0 -2
  88. package/dist/index.js +0 -42
@@ -1,61 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Text, Box, useInput } from 'ink';
4
- import { getConfiguredProviders, clearApiKey, getCurrentProvider } from '../config/index.js';
5
- export const LogoutPicker = ({ onLogout, onLogoutAll, onCancel }) => {
6
- const providers = getConfiguredProviders();
7
- const currentProvider = getCurrentProvider();
8
- const [selectedIndex, setSelectedIndex] = useState(0);
9
- // Options: individual providers + "All" + "Cancel"
10
- const options = [
11
- ...providers.map(p => ({
12
- type: 'provider',
13
- id: p.id,
14
- label: p.name,
15
- isCurrent: p.id === currentProvider.id
16
- })),
17
- { type: 'all', id: 'all', label: 'Logout from all providers', isCurrent: false },
18
- { type: 'cancel', id: 'cancel', label: 'Cancel', isCurrent: false },
19
- ];
20
- useInput((input, key) => {
21
- if (key.escape) {
22
- onCancel();
23
- return;
24
- }
25
- if (key.upArrow) {
26
- setSelectedIndex(i => Math.max(0, i - 1));
27
- }
28
- if (key.downArrow) {
29
- setSelectedIndex(i => Math.min(options.length - 1, i + 1));
30
- }
31
- if (key.return) {
32
- const selected = options[selectedIndex];
33
- if (selected.type === 'provider') {
34
- clearApiKey(selected.id);
35
- onLogout(selected.id);
36
- }
37
- else if (selected.type === 'all') {
38
- for (const p of providers) {
39
- clearApiKey(p.id);
40
- }
41
- onLogoutAll();
42
- }
43
- else {
44
- onCancel();
45
- }
46
- }
47
- });
48
- if (providers.length === 0) {
49
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { color: "yellow", children: "No providers configured." }), _jsx(Text, { color: "cyan", children: "Press Escape to go back." })] }));
50
- }
51
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Select provider to logout:" }) }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: options.map((option, index) => {
52
- const isSelected = index === selectedIndex;
53
- const prefix = isSelected ? '→ ' : ' ';
54
- let color = isSelected ? 'green' : 'white';
55
- if (option.type === 'all')
56
- color = isSelected ? 'red' : 'yellow';
57
- if (option.type === 'cancel')
58
- color = isSelected ? 'blue' : 'gray';
59
- return (_jsxs(Box, { children: [_jsxs(Text, { color: color, bold: isSelected, children: [prefix, option.label] }), option.isCurrent && (_jsx(Text, { color: "cyan", children: " (current)" }))] }, option.id));
60
- }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "cyan", children: "\u2191\u2193 Navigate Enter Select Esc Cancel" }) })] }));
61
- };
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- export declare function getCodeBlocks(): string[];
3
- export declare function clearCodeBlocks(): void;
4
- export declare function getCodeBlock(index: number): string | null;
5
- interface MessageProps {
6
- role: 'user' | 'assistant' | 'system';
7
- content: string;
8
- }
9
- export declare const MessageView: React.FC<MessageProps>;
10
- export {};
@@ -1,242 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { memo } from 'react';
3
- import { Text, Box } from 'ink';
4
- // Global code block storage for copy functionality
5
- let codeBlocks = [];
6
- export function getCodeBlocks() {
7
- return codeBlocks;
8
- }
9
- export function clearCodeBlocks() {
10
- codeBlocks = [];
11
- }
12
- export function getCodeBlock(index) {
13
- if (index < 0) {
14
- // Negative index = from end (-1 = last block)
15
- const actualIndex = codeBlocks.length + index;
16
- return codeBlocks[actualIndex] || null;
17
- }
18
- return codeBlocks[index] || null;
19
- }
20
- export const MessageView = memo(({ role, content }) => {
21
- if (role === 'user') {
22
- // For long user messages, truncate display but keep full content for processing
23
- const maxDisplayLength = 500;
24
- const isLong = content.length > maxDisplayLength;
25
- const displayContent = isLong
26
- ? content.substring(0, maxDisplayLength) + '...'
27
- : content;
28
- // Replace multiple newlines with single space for cleaner display
29
- const cleanContent = displayContent.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim();
30
- return (_jsxs(Box, { marginY: 1, flexDirection: "column", children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: "#f02a30", bold: true, children: '> ' }), _jsx(Text, { children: cleanContent })] }), isLong && (_jsxs(Text, { color: "cyan", dimColor: true, children: [" (", content.length, " characters total)"] }))] }));
31
- }
32
- if (role === 'system') {
33
- return (_jsx(Box, { marginY: 1, justifyContent: "center", children: _jsx(Text, { italic: true, children: content }) }));
34
- }
35
- // Assistant message - parse code blocks and markdown
36
- // Clear and rebuild code blocks on each render
37
- codeBlocks = [];
38
- return (_jsx(Box, { flexDirection: "column", marginY: 1, paddingX: 1, children: _jsx(FormattedResponse, { content: content }) }));
39
- });
40
- const FormattedResponse = memo(({ content }) => {
41
- const segments = parseContent(content);
42
- let codeBlockIndex = 0;
43
- return (_jsx(_Fragment, { children: segments.map((segment, i) => {
44
- if (segment.type === 'code') {
45
- const idx = codeBlockIndex++;
46
- // Store code block for copy functionality
47
- codeBlocks.push(segment.content);
48
- return _jsx(CodeBlock, { code: segment.content, language: segment.language || 'code', index: idx }, i);
49
- }
50
- // Render text with inline markdown
51
- return _jsx(MarkdownText, { text: segment.content }, i);
52
- }) }));
53
- });
54
- // Render inline markdown: **bold**, *italic*, `code`, __underline__
55
- const MarkdownText = memo(({ text }) => {
56
- const parts = [];
57
- let remaining = text;
58
- let key = 0;
59
- while (remaining.length > 0) {
60
- // Bold: **text** or __text__
61
- const boldMatch = remaining.match(/^(\*\*|__)(.+?)\1/);
62
- if (boldMatch) {
63
- parts.push(_jsx(Text, { bold: true, children: boldMatch[2] }, key++));
64
- remaining = remaining.slice(boldMatch[0].length);
65
- continue;
66
- }
67
- // Italic: *text* or _text_
68
- const italicMatch = remaining.match(/^(\*|_)([^*_]+)\1/);
69
- if (italicMatch) {
70
- parts.push(_jsx(Text, { italic: true, children: italicMatch[2] }, key++));
71
- remaining = remaining.slice(italicMatch[0].length);
72
- continue;
73
- }
74
- // Inline code: `code`
75
- const codeMatch = remaining.match(/^`([^`]+)`/);
76
- if (codeMatch) {
77
- parts.push(_jsx(Text, { color: "cyan", backgroundColor: "gray", children: codeMatch[1] }, key++));
78
- remaining = remaining.slice(codeMatch[0].length);
79
- continue;
80
- }
81
- // Headers: # ## ### at start of line
82
- const headerMatch = remaining.match(/^(#{1,3})\s+(.+?)(?:\n|$)/);
83
- if (headerMatch) {
84
- const level = headerMatch[1].length;
85
- const color = level === 1 ? '#f02a30' : level === 2 ? 'cyan' : 'green';
86
- parts.push(_jsx(Text, { color: color, bold: true, children: headerMatch[2] }, key++));
87
- parts.push(_jsx(Text, { children: '\n' }, key++));
88
- remaining = remaining.slice(headerMatch[0].length);
89
- continue;
90
- }
91
- // List items: - item or * item or number. item
92
- const listMatch = remaining.match(/^(\s*)([-*]|\d+\.)\s+(.+?)(?:\n|$)/);
93
- if (listMatch) {
94
- const indent = listMatch[1];
95
- const bullet = listMatch[2].match(/^\d/) ? listMatch[2] : '•';
96
- parts.push(_jsxs(Text, { children: [indent, _jsx(Text, { color: "#f02a30", children: bullet }), " ", listMatch[3], '\n'] }, key++));
97
- remaining = remaining.slice(listMatch[0].length);
98
- continue;
99
- }
100
- // Links: [text](url)
101
- const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)]+)\)/);
102
- if (linkMatch) {
103
- parts.push(_jsx(Text, { color: "blue", underline: true, children: linkMatch[1] }, key++));
104
- remaining = remaining.slice(linkMatch[0].length);
105
- continue;
106
- }
107
- // Default: consume until next special character or end
108
- const nextSpecial = remaining.search(/[\*_`#\[\n-]/);
109
- if (nextSpecial === -1) {
110
- parts.push(_jsx(Text, { children: remaining }, key++));
111
- break;
112
- }
113
- else if (nextSpecial === 0) {
114
- // Not a valid markdown char, consume single char
115
- parts.push(_jsx(Text, { children: remaining[0] }, key++));
116
- remaining = remaining.slice(1);
117
- }
118
- else {
119
- parts.push(_jsx(Text, { children: remaining.slice(0, nextSpecial) }, key++));
120
- remaining = remaining.slice(nextSpecial);
121
- }
122
- }
123
- return _jsx(Text, { children: parts });
124
- });
125
- const CodeBlock = memo(({ code, language, index }) => {
126
- const lines = code.split('\n');
127
- return (_jsxs(Box, { flexDirection: "column", marginY: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: "cyan", bold: true, children: language }), _jsxs(Text, { children: ["[", index, "]"] })] }), _jsx(Text, { children: " " }), lines.map((line, i) => (_jsx(SyntaxLine, { line: line, language: language }, i)))] }));
128
- });
129
- // Keywords by language - defined outside component to avoid recreation
130
- const SYNTAX_KEYWORDS = {
131
- python: ['def', 'class', 'return', 'if', 'else', 'elif', 'for', 'while', 'import', 'from', 'as', 'try', 'except', 'raise', 'with', 'True', 'False', 'None', 'and', 'or', 'not', 'in', 'is', 'lambda', 'pass', 'break', 'continue', 'global', 'async', 'await'],
132
- javascript: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'class', 'import', 'export', 'from', 'async', 'await', 'try', 'catch', 'throw', 'new', 'this', 'true', 'false', 'null', 'undefined', 'typeof', 'instanceof'],
133
- typescript: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'class', 'import', 'export', 'from', 'async', 'await', 'try', 'catch', 'throw', 'new', 'this', 'true', 'false', 'null', 'undefined', 'interface', 'type', 'enum', 'private', 'public', 'protected', 'readonly'],
134
- java: ['public', 'private', 'protected', 'class', 'interface', 'extends', 'implements', 'return', 'if', 'else', 'for', 'while', 'new', 'this', 'static', 'final', 'void', 'int', 'String', 'boolean', 'true', 'false', 'null', 'try', 'catch', 'throw', 'throws'],
135
- go: ['func', 'var', 'const', 'if', 'else', 'for', 'range', 'switch', 'case', 'return', 'struct', 'interface', 'package', 'import', 'type', 'map', 'chan', 'go', 'defer', 'true', 'false', 'nil'],
136
- rust: ['fn', 'let', 'mut', 'const', 'if', 'else', 'for', 'while', 'loop', 'match', 'struct', 'enum', 'impl', 'trait', 'pub', 'use', 'mod', 'self', 'super', 'true', 'false', 'return', 'async', 'await'],
137
- bash: ['if', 'then', 'else', 'fi', 'for', 'do', 'done', 'while', 'case', 'esac', 'function', 'return', 'echo', 'exit', 'export', 'local'],
138
- sh: ['if', 'then', 'else', 'fi', 'for', 'do', 'done', 'while', 'case', 'esac', 'function', 'return', 'echo', 'exit', 'export', 'local'],
139
- php: ['function', 'class', 'public', 'private', 'protected', 'return', 'if', 'else', 'elseif', 'for', 'foreach', 'while', 'echo', 'print', 'new', 'this', 'true', 'false', 'null', 'use', 'namespace', 'extends', 'implements'],
140
- html: ['html', 'head', 'body', 'div', 'span', 'p', 'a', 'img', 'script', 'style', 'link', 'meta', 'title', 'h1', 'h2', 'h3', 'ul', 'li', 'table', 'tr', 'td', 'form', 'input', 'button'],
141
- css: ['color', 'background', 'margin', 'padding', 'border', 'width', 'height', 'display', 'flex', 'grid', 'position', 'top', 'left', 'right', 'bottom', 'font', 'text'],
142
- sql: ['SELECT', 'FROM', 'WHERE', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'TABLE', 'INDEX', 'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'ON', 'AND', 'OR', 'NOT', 'NULL', 'ORDER', 'BY', 'GROUP', 'HAVING', 'LIMIT'],
143
- };
144
- // Syntax highlighting component - memoized to prevent re-renders
145
- const SyntaxLine = memo(({ line, language }) => {
146
- const lang = language.toLowerCase();
147
- const langKeywords = SYNTAX_KEYWORDS[lang] || [];
148
- const parts = [];
149
- let remaining = line;
150
- let key = 0;
151
- while (remaining.length > 0) {
152
- // Comments
153
- if (remaining.startsWith('//') || remaining.startsWith('#')) {
154
- parts.push(_jsx(Text, { color: "cyan", children: remaining }, key++));
155
- break;
156
- }
157
- // Multi-line string/docstring
158
- if (remaining.startsWith('"""') || remaining.startsWith("'''")) {
159
- const quote = remaining.slice(0, 3);
160
- parts.push(_jsx(Text, { color: "green", children: remaining }, key++));
161
- break;
162
- }
163
- // Strings
164
- const stringMatch = remaining.match(/^(["'`])(?:[^\\]|\\.)*?\1/);
165
- if (stringMatch) {
166
- parts.push(_jsx(Text, { color: "green", children: stringMatch[0] }, key++));
167
- remaining = remaining.slice(stringMatch[0].length);
168
- continue;
169
- }
170
- // Keywords
171
- const wordMatch = remaining.match(/^[a-zA-Z_]\w*/);
172
- if (wordMatch) {
173
- const word = wordMatch[0];
174
- if (langKeywords.includes(word)) {
175
- parts.push(_jsx(Text, { color: "magenta", bold: true, children: word }, key++));
176
- }
177
- else if (word.match(/^[A-Z]/)) {
178
- // Class names / constants
179
- parts.push(_jsx(Text, { color: "cyan", children: word }, key++));
180
- }
181
- else {
182
- parts.push(_jsx(Text, { children: word }, key++));
183
- }
184
- remaining = remaining.slice(word.length);
185
- continue;
186
- }
187
- // Numbers
188
- const numMatch = remaining.match(/^\d+\.?\d*/);
189
- if (numMatch) {
190
- parts.push(_jsx(Text, { color: "#f02a30", children: numMatch[0] }, key++));
191
- remaining = remaining.slice(numMatch[0].length);
192
- continue;
193
- }
194
- // Operators and brackets
195
- const opMatch = remaining.match(/^[+\-*/%=<>!&|^~?:;,.()\[\]{}]+/);
196
- if (opMatch) {
197
- parts.push(_jsx(Text, { color: "white", children: opMatch[0] }, key++));
198
- remaining = remaining.slice(opMatch[0].length);
199
- continue;
200
- }
201
- // Default
202
- parts.push(_jsx(Text, { children: remaining[0] }, key++));
203
- remaining = remaining.slice(1);
204
- }
205
- return _jsx(Text, { children: parts });
206
- });
207
- function parseContent(text) {
208
- const segments = [];
209
- // Match code blocks: ```lang or ``` followed by code and closing ```
210
- const codeBlockRegex = /```(\w*)\n?([\s\S]*?)```/g;
211
- let lastIndex = 0;
212
- let match;
213
- while ((match = codeBlockRegex.exec(text)) !== null) {
214
- // Text before code block
215
- if (match.index > lastIndex) {
216
- const textBefore = text.slice(lastIndex, match.index).trim();
217
- if (textBefore) {
218
- segments.push({ type: 'text', content: textBefore });
219
- }
220
- }
221
- // Code block
222
- const lang = match[1] || 'code';
223
- const code = match[2] || '';
224
- segments.push({
225
- type: 'code',
226
- content: code.trim(),
227
- language: lang,
228
- });
229
- lastIndex = match.index + match[0].length;
230
- }
231
- // Remaining text
232
- if (lastIndex < text.length) {
233
- const remaining = text.slice(lastIndex).trim();
234
- if (remaining) {
235
- segments.push({ type: 'text', content: remaining });
236
- }
237
- }
238
- if (segments.length === 0) {
239
- segments.push({ type: 'text', content: text });
240
- }
241
- return segments;
242
- }
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import { Message } from '../config/index';
3
- interface MessageListProps {
4
- messages: Message[];
5
- streamingContent?: string;
6
- scrollOffset?: number;
7
- terminalHeight?: number;
8
- }
9
- export declare const MessageList: React.FC<MessageListProps>;
10
- export {};
@@ -1,42 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo, useMemo } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { MessageView } from './Message.js';
5
- import { StreamingMessage } from './StreamingMessage.js';
6
- /**
7
- * Memoized individual message component
8
- * Only re-renders when its specific content changes
9
- */
10
- const MemoizedMessage = memo(({ msg }) => _jsx(MessageView, { role: msg.role, content: msg.content }), (prev, next) => prev.msg.content === next.msg.content && prev.msg.role === next.msg.role);
11
- MemoizedMessage.displayName = 'MemoizedMessage';
12
- /**
13
- * Message list WITHOUT Static component
14
- *
15
- * We removed the Static component because:
16
- * - Static preserves content in terminal scroll history even after unmount
17
- * - This causes ghost/duplicate content when switching screens
18
- * - The trade-off is that messages will re-render on each update
19
- * - We mitigate this with memoization at the individual message level
20
- *
21
- * NOTE: This is a temporary solution until we implement a custom renderer
22
- * like Claude CLI uses (DEC Mode 2026 / synchronized output).
23
- */
24
- // Maximum number of messages to render at once
25
- // This prevents performance issues and flickering with large chat histories
26
- const MAX_VISIBLE_MESSAGES = 20;
27
- export const MessageList = memo(({ messages, streamingContent, }) => {
28
- // Virtualization: only render the last N messages to prevent flickering
29
- // Older messages are still in history but not rendered
30
- const visibleMessages = useMemo(() => {
31
- if (messages.length <= MAX_VISIBLE_MESSAGES) {
32
- return messages;
33
- }
34
- return messages.slice(-MAX_VISIBLE_MESSAGES);
35
- }, [messages]);
36
- // Calculate how many messages are hidden
37
- const hiddenCount = messages.length - visibleMessages.length;
38
- // Memoize the messages array rendering
39
- const renderedMessages = useMemo(() => (visibleMessages.map((msg, index) => (_jsx(MemoizedMessage, { msg: msg, index: hiddenCount + index }, `msg-${hiddenCount + index}-${msg.role}`)))), [visibleMessages, hiddenCount]);
40
- return (_jsxs(Box, { flexDirection: "column", children: [hiddenCount > 0 && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "gray", children: ["... ", hiddenCount, " earlier message(s) hidden ..."] }) })), renderedMessages, streamingContent && (_jsx(StreamingMessage, { content: streamingContent }))] }));
41
- });
42
- MessageList.displayName = 'MessageList';
@@ -1,7 +0,0 @@
1
- import React from 'react';
2
- interface ProjectPermissionProps {
3
- projectPath: string;
4
- onComplete: (granted: boolean, permanent: boolean, writeGranted?: boolean) => void;
5
- }
6
- export declare const ProjectPermission: React.FC<ProjectPermissionProps>;
7
- export {};
@@ -1,65 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- import { setProjectPermission, hasStandardProjectMarkers, initializeAsProject } from '../config/index.js';
5
- import { getProjectSummary } from '../utils/project.js';
6
- export const ProjectPermission = ({ projectPath, onComplete }) => {
7
- // If folder doesn't have standard project markers, ask if user wants to initialize it
8
- const isStandardProject = hasStandardProjectMarkers(projectPath);
9
- const [step, setStep] = useState(isStandardProject ? 'read' : 'init');
10
- const [readGranted, setReadGranted] = useState(false);
11
- const summary = getProjectSummary(projectPath);
12
- const projectName = summary?.name || projectPath.split('/').pop() || 'Unknown';
13
- useInput((input, key) => {
14
- const char = input.toLowerCase();
15
- if (step === 'init') {
16
- if (char === 'y') {
17
- // Initialize as project and continue to permissions
18
- initializeAsProject(projectPath);
19
- setStep('read');
20
- }
21
- else if (char === 'n' || key.escape) {
22
- // Don't initialize - use global config, skip to read permission
23
- setStep('read');
24
- }
25
- }
26
- else if (step === 'read') {
27
- if (char === 'y') {
28
- // Grant for this session only
29
- setReadGranted(true);
30
- setStep('write');
31
- }
32
- else if (char === 'a') {
33
- // Grant permanently (always)
34
- setProjectPermission(projectPath, true, false);
35
- setReadGranted(true);
36
- setStep('write');
37
- }
38
- else if (char === 'n' || key.escape) {
39
- // Deny
40
- onComplete(false, false);
41
- }
42
- }
43
- else if (step === 'write') {
44
- if (char === 'y') {
45
- // Grant write for session
46
- onComplete(true, false, true);
47
- }
48
- else if (char === 'a') {
49
- // Grant write permanently
50
- setProjectPermission(projectPath, true, true);
51
- onComplete(true, true, true);
52
- }
53
- else if (char === 'n' || key.escape) {
54
- // Read only
55
- if (readGranted) {
56
- onComplete(true, false, false);
57
- }
58
- else {
59
- onComplete(false, false, false);
60
- }
61
- }
62
- }
63
- });
64
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Project Access Request" }), _jsx(Text, { children: " " }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { children: "Project: " }), _jsx(Text, { color: "cyan", bold: true, children: projectName })] }), _jsxs(Text, { children: [_jsx(Text, { children: "Path: " }), _jsx(Text, { children: projectPath })] }), summary && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { children: "Type: " }), _jsx(Text, { children: summary.type })] }), _jsxs(Text, { children: [_jsx(Text, { children: "Files: " }), _jsxs(Text, { children: [summary.fileCount, " code files"] })] })] }))] }), _jsx(Text, { children: " " }), step === 'init' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "yellow", children: "This folder is not recognized as a project." }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Initialize as a Codeep project?" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: "This will create a .codeep/ folder to store:" }), _jsx(Text, { color: "gray", children: " \u2022 Session history" }), _jsx(Text, { color: "gray", children: " \u2022 Project-specific settings" }), _jsx(Text, { color: "gray", children: " \u2022 Permission preferences" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: "If you choose No, settings will be stored globally." }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "[Y]" }), _jsx(Text, { children: " Yes, initialize " }), _jsx(Text, { color: "cyan", children: "[N]" }), _jsx(Text, { children: " No, use global config" })] })] })), step === 'read' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "#f02a30", children: "Allow Codeep to read project files?" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "This enables:" }), _jsx(Text, { children: " \u2022 Auto-detect file paths in your messages" }), _jsx(Text, { children: " \u2022 Send file contents to AI for analysis" }), _jsx(Text, { children: " \u2022 Project structure awareness" }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "[Y]" }), _jsx(Text, { children: " Yes (this session) " }), _jsx(Text, { color: "cyan", children: "[A]" }), _jsx(Text, { children: " Always " }), _jsx(Text, { color: "#f02a30", children: "[N]" }), _jsx(Text, { children: " No" })] })] })), step === 'write' && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "green", children: "\u2713 Read permission granted" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "#f02a30", children: "Allow Codeep to suggest file changes?" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "This enables:" }), _jsx(Text, { children: " \u2022 AI can suggest code modifications" }), _jsx(Text, { children: " \u2022 You'll always see changes before applying" }), _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { color: "green", children: "[Y]" }), _jsx(Text, { children: " Yes (this session) " }), _jsx(Text, { color: "cyan", children: "[A]" }), _jsx(Text, { children: " Always " }), _jsx(Text, { color: "#f02a30", children: "[N]" }), _jsx(Text, { children: " Read-only" })] })] }))] }));
65
- };
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import { SearchResult } from '../utils/search';
3
- interface SearchProps {
4
- results: SearchResult[];
5
- searchTerm: string;
6
- onClose: () => void;
7
- onSelectMessage: (index: number) => void;
8
- }
9
- export declare const Search: React.FC<SearchProps>;
10
- export {};
@@ -1,30 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- export const Search = ({ results, searchTerm, onClose, onSelectMessage }) => {
5
- const [selectedIndex, setSelectedIndex] = useState(0);
6
- useInput((input, key) => {
7
- if (key.escape) {
8
- onClose();
9
- return;
10
- }
11
- if (key.upArrow) {
12
- setSelectedIndex(i => Math.max(0, i - 1));
13
- }
14
- if (key.downArrow) {
15
- setSelectedIndex(i => Math.min(results.length - 1, i + 1));
16
- }
17
- if (key.return && results.length > 0) {
18
- onSelectMessage(results[selectedIndex].messageIndex);
19
- onClose();
20
- }
21
- });
22
- if (results.length === 0) {
23
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Search Results" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [_jsx(Text, { children: "Query: " }), _jsxs(Text, { color: "cyan", children: ["\"", searchTerm, "\""] })] }), _jsx(Text, { children: " " }), _jsx(Text, { children: "No results found." }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Press Esc to close" })] }));
24
- }
25
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Search Results" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [_jsx(Text, { children: "Query: " }), _jsxs(Text, { color: "cyan", children: ["\"", searchTerm, "\""] }), _jsxs(Text, { children: [" (", results.length, " ", results.length === 1 ? 'result' : 'results', ")"] })] }), _jsx(Text, { children: " " }), results.map((result, i) => {
26
- const isSelected = i === selectedIndex;
27
- const roleColor = result.role === 'user' ? 'green' : 'blue';
28
- return (_jsxs(Box, { flexDirection: "column", marginBottom: i < results.length - 1 ? 1 : 0, children: [_jsxs(Text, { children: [isSelected ? _jsx(Text, { color: "#f02a30", children: "\u25B8 " }) : ' ', _jsxs(Text, { color: roleColor, bold: true, children: ["[", result.role.toUpperCase(), "]"] }), _jsxs(Text, { children: [" Message #", result.messageIndex + 1] })] }), _jsxs(Text, { children: [' ', _jsx(Text, { children: result.matchedText })] })] }, i));
29
- }), _jsx(Text, { children: " " }), _jsx(Text, { children: "\u2191/\u2193 Navigate, Enter = Jump to message, Esc = Close" })] }));
30
- };
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import { Message } from '../config/index';
3
- interface SessionPickerProps {
4
- onSelect: (messages: Message[], sessionName: string) => void;
5
- onNewSession: () => void;
6
- projectPath?: string;
7
- }
8
- export declare const SessionPicker: React.FC<SessionPickerProps>;
9
- export {};
@@ -1,88 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useMemo } from 'react';
3
- import { Text, Box, useInput } from 'ink';
4
- import { listSessionsWithInfo, loadSession, startNewSession } from '../config/index.js';
5
- /**
6
- * Format relative time (e.g., "today", "yesterday", "3 days ago")
7
- */
8
- function formatRelativeTime(dateStr) {
9
- const date = new Date(dateStr);
10
- const now = new Date();
11
- const diffMs = now.getTime() - date.getTime();
12
- const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
13
- if (diffDays === 0)
14
- return 'today';
15
- if (diffDays === 1)
16
- return 'yesterday';
17
- if (diffDays < 7)
18
- return `${diffDays}d ago`;
19
- if (diffDays < 30)
20
- return `${Math.floor(diffDays / 7)}w ago`;
21
- return `${Math.floor(diffDays / 30)}mo ago`;
22
- }
23
- /**
24
- * Format file size (e.g., "1.2 KB")
25
- */
26
- function formatFileSize(bytes) {
27
- if (bytes < 1024)
28
- return `${bytes} B`;
29
- if (bytes < 1024 * 1024)
30
- return `${(bytes / 1024).toFixed(1)} KB`;
31
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
32
- }
33
- /**
34
- * Truncate session name for display
35
- */
36
- function truncateName(name, maxLength = 25) {
37
- if (name.length <= maxLength)
38
- return name;
39
- return name.substring(0, maxLength - 3) + '...';
40
- }
41
- export const SessionPicker = ({ onSelect, onNewSession, projectPath }) => {
42
- const sessions = useMemo(() => listSessionsWithInfo(projectPath), [projectPath]);
43
- const [selectedIndex, setSelectedIndex] = useState(0);
44
- useInput((input, key) => {
45
- // N = New session
46
- if (input === 'n' || input === 'N') {
47
- startNewSession();
48
- onNewSession();
49
- return;
50
- }
51
- // Arrow navigation
52
- if (key.upArrow) {
53
- setSelectedIndex(i => Math.max(0, i - 1));
54
- }
55
- if (key.downArrow) {
56
- setSelectedIndex(i => Math.min(sessions.length - 1, i + 1));
57
- }
58
- // Enter = Load selected session
59
- if (key.return && sessions.length > 0) {
60
- const selected = sessions[selectedIndex];
61
- const messages = loadSession(selected.name, projectPath);
62
- if (messages) {
63
- onSelect(messages, selected.name);
64
- }
65
- }
66
- // Escape = Start new session (same as N)
67
- if (key.escape) {
68
- startNewSession();
69
- onNewSession();
70
- }
71
- });
72
- // If no sessions exist, auto-start new session
73
- if (sessions.length === 0) {
74
- // Will trigger on next render cycle
75
- setTimeout(() => {
76
- startNewSession();
77
- onNewSession();
78
- }, 0);
79
- return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(Text, { color: "cyan", children: "Starting new session..." }) }));
80
- }
81
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Select a session:" }) }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: sessions.map((session, index) => {
82
- const isSelected = index === selectedIndex;
83
- const prefix = isSelected ? '→ ' : ' ';
84
- const name = truncateName(session.name);
85
- const meta = `${session.messageCount} msg, ${formatRelativeTime(session.createdAt)}`;
86
- return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? 'green' : 'white', bold: isSelected, children: [prefix, name] }), _jsxs(Text, { color: "cyan", children: [" (", meta, ")"] })] }, session.name));
87
- }) }), _jsxs(Box, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { color: "yellow", children: "[N]" }), _jsx(Text, { children: " New session" })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "\u2191\u2193 Navigate " }), _jsx(Text, { color: "cyan", children: "Enter Select " }), _jsx(Text, { color: "cyan", children: "N New " }), _jsx(Text, { color: "cyan", children: "Esc New" })] })] }));
88
- };
@@ -1,12 +0,0 @@
1
- import React from 'react';
2
- import { Message } from '../config/index';
3
- interface SessionsProps {
4
- history: Message[];
5
- onLoad: (history: Message[], name: string) => void;
6
- onClose: () => void;
7
- onDelete?: (name: string) => void;
8
- deleteMode?: boolean;
9
- projectPath?: string;
10
- }
11
- export declare const Sessions: React.FC<SessionsProps>;
12
- export {};