codeep 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 (103) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +576 -0
  3. package/dist/api/index.d.ts +8 -0
  4. package/dist/api/index.js +421 -0
  5. package/dist/app.d.ts +2 -0
  6. package/dist/app.js +1406 -0
  7. package/dist/components/AgentProgress.d.ts +33 -0
  8. package/dist/components/AgentProgress.js +97 -0
  9. package/dist/components/Export.d.ts +8 -0
  10. package/dist/components/Export.js +27 -0
  11. package/dist/components/Help.d.ts +2 -0
  12. package/dist/components/Help.js +3 -0
  13. package/dist/components/Input.d.ts +9 -0
  14. package/dist/components/Input.js +89 -0
  15. package/dist/components/Loading.d.ts +9 -0
  16. package/dist/components/Loading.js +31 -0
  17. package/dist/components/Login.d.ts +7 -0
  18. package/dist/components/Login.js +77 -0
  19. package/dist/components/Logo.d.ts +8 -0
  20. package/dist/components/Logo.js +89 -0
  21. package/dist/components/LogoutPicker.d.ts +8 -0
  22. package/dist/components/LogoutPicker.js +61 -0
  23. package/dist/components/Message.d.ts +10 -0
  24. package/dist/components/Message.js +234 -0
  25. package/dist/components/MessageList.d.ts +10 -0
  26. package/dist/components/MessageList.js +8 -0
  27. package/dist/components/ProjectPermission.d.ts +7 -0
  28. package/dist/components/ProjectPermission.js +52 -0
  29. package/dist/components/Search.d.ts +10 -0
  30. package/dist/components/Search.js +30 -0
  31. package/dist/components/SessionPicker.d.ts +9 -0
  32. package/dist/components/SessionPicker.js +88 -0
  33. package/dist/components/Sessions.d.ts +12 -0
  34. package/dist/components/Sessions.js +102 -0
  35. package/dist/components/Settings.d.ts +7 -0
  36. package/dist/components/Settings.js +162 -0
  37. package/dist/components/Status.d.ts +2 -0
  38. package/dist/components/Status.js +12 -0
  39. package/dist/config/config.test.d.ts +1 -0
  40. package/dist/config/config.test.js +157 -0
  41. package/dist/config/index.d.ts +121 -0
  42. package/dist/config/index.js +555 -0
  43. package/dist/config/providers.d.ts +43 -0
  44. package/dist/config/providers.js +82 -0
  45. package/dist/config/providers.test.d.ts +1 -0
  46. package/dist/config/providers.test.js +132 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +38 -0
  49. package/dist/utils/agent.d.ts +37 -0
  50. package/dist/utils/agent.js +627 -0
  51. package/dist/utils/codeReview.d.ts +36 -0
  52. package/dist/utils/codeReview.js +390 -0
  53. package/dist/utils/context.d.ts +49 -0
  54. package/dist/utils/context.js +216 -0
  55. package/dist/utils/diffPreview.d.ts +57 -0
  56. package/dist/utils/diffPreview.js +335 -0
  57. package/dist/utils/export.d.ts +19 -0
  58. package/dist/utils/export.js +94 -0
  59. package/dist/utils/git.d.ts +85 -0
  60. package/dist/utils/git.js +399 -0
  61. package/dist/utils/git.test.d.ts +1 -0
  62. package/dist/utils/git.test.js +193 -0
  63. package/dist/utils/history.d.ts +93 -0
  64. package/dist/utils/history.js +348 -0
  65. package/dist/utils/interactive.d.ts +34 -0
  66. package/dist/utils/interactive.js +206 -0
  67. package/dist/utils/keychain.d.ts +17 -0
  68. package/dist/utils/keychain.js +160 -0
  69. package/dist/utils/learning.d.ts +89 -0
  70. package/dist/utils/learning.js +330 -0
  71. package/dist/utils/logger.d.ts +33 -0
  72. package/dist/utils/logger.js +130 -0
  73. package/dist/utils/project.d.ts +86 -0
  74. package/dist/utils/project.js +415 -0
  75. package/dist/utils/project.test.d.ts +1 -0
  76. package/dist/utils/project.test.js +212 -0
  77. package/dist/utils/ratelimit.d.ts +26 -0
  78. package/dist/utils/ratelimit.js +132 -0
  79. package/dist/utils/ratelimit.test.d.ts +1 -0
  80. package/dist/utils/ratelimit.test.js +131 -0
  81. package/dist/utils/retry.d.ts +28 -0
  82. package/dist/utils/retry.js +109 -0
  83. package/dist/utils/retry.test.d.ts +1 -0
  84. package/dist/utils/retry.test.js +163 -0
  85. package/dist/utils/search.d.ts +11 -0
  86. package/dist/utils/search.js +29 -0
  87. package/dist/utils/shell.d.ts +45 -0
  88. package/dist/utils/shell.js +242 -0
  89. package/dist/utils/skills.d.ts +144 -0
  90. package/dist/utils/skills.js +1137 -0
  91. package/dist/utils/smartContext.d.ts +29 -0
  92. package/dist/utils/smartContext.js +441 -0
  93. package/dist/utils/tools.d.ts +224 -0
  94. package/dist/utils/tools.js +731 -0
  95. package/dist/utils/update.d.ts +22 -0
  96. package/dist/utils/update.js +128 -0
  97. package/dist/utils/validation.d.ts +28 -0
  98. package/dist/utils/validation.js +141 -0
  99. package/dist/utils/validation.test.d.ts +1 -0
  100. package/dist/utils/validation.test.js +164 -0
  101. package/dist/utils/verify.d.ts +78 -0
  102. package/dist/utils/verify.js +464 -0
  103. package/package.json +68 -0
@@ -0,0 +1,234 @@
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
+ return (_jsx(Box, { marginY: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", bold: true, children: '> ' }), _jsx(Text, { children: content })] }) }));
23
+ }
24
+ if (role === 'system') {
25
+ return (_jsx(Box, { marginY: 1, justifyContent: "center", children: _jsx(Text, { italic: true, children: content }) }));
26
+ }
27
+ // Assistant message - parse code blocks and markdown
28
+ // Clear and rebuild code blocks on each render
29
+ codeBlocks = [];
30
+ return (_jsx(Box, { flexDirection: "column", marginY: 1, paddingX: 1, children: _jsx(FormattedResponse, { content: content }) }));
31
+ });
32
+ const FormattedResponse = memo(({ content }) => {
33
+ const segments = parseContent(content);
34
+ let codeBlockIndex = 0;
35
+ return (_jsx(_Fragment, { children: segments.map((segment, i) => {
36
+ if (segment.type === 'code') {
37
+ const idx = codeBlockIndex++;
38
+ // Store code block for copy functionality
39
+ codeBlocks.push(segment.content);
40
+ return _jsx(CodeBlock, { code: segment.content, language: segment.language || 'code', index: idx }, i);
41
+ }
42
+ // Render text with inline markdown
43
+ return _jsx(MarkdownText, { text: segment.content }, i);
44
+ }) }));
45
+ });
46
+ // Render inline markdown: **bold**, *italic*, `code`, __underline__
47
+ const MarkdownText = memo(({ text }) => {
48
+ const parts = [];
49
+ let remaining = text;
50
+ let key = 0;
51
+ while (remaining.length > 0) {
52
+ // Bold: **text** or __text__
53
+ const boldMatch = remaining.match(/^(\*\*|__)(.+?)\1/);
54
+ if (boldMatch) {
55
+ parts.push(_jsx(Text, { bold: true, children: boldMatch[2] }, key++));
56
+ remaining = remaining.slice(boldMatch[0].length);
57
+ continue;
58
+ }
59
+ // Italic: *text* or _text_
60
+ const italicMatch = remaining.match(/^(\*|_)([^*_]+)\1/);
61
+ if (italicMatch) {
62
+ parts.push(_jsx(Text, { italic: true, children: italicMatch[2] }, key++));
63
+ remaining = remaining.slice(italicMatch[0].length);
64
+ continue;
65
+ }
66
+ // Inline code: `code`
67
+ const codeMatch = remaining.match(/^`([^`]+)`/);
68
+ if (codeMatch) {
69
+ parts.push(_jsx(Text, { color: "cyan", backgroundColor: "gray", children: codeMatch[1] }, key++));
70
+ remaining = remaining.slice(codeMatch[0].length);
71
+ continue;
72
+ }
73
+ // Headers: # ## ### at start of line
74
+ const headerMatch = remaining.match(/^(#{1,3})\s+(.+?)(?:\n|$)/);
75
+ if (headerMatch) {
76
+ const level = headerMatch[1].length;
77
+ const color = level === 1 ? '#f02a30' : level === 2 ? 'cyan' : 'green';
78
+ parts.push(_jsx(Text, { color: color, bold: true, children: headerMatch[2] }, key++));
79
+ parts.push(_jsx(Text, { children: '\n' }, key++));
80
+ remaining = remaining.slice(headerMatch[0].length);
81
+ continue;
82
+ }
83
+ // List items: - item or * item or number. item
84
+ const listMatch = remaining.match(/^(\s*)([-*]|\d+\.)\s+(.+?)(?:\n|$)/);
85
+ if (listMatch) {
86
+ const indent = listMatch[1];
87
+ const bullet = listMatch[2].match(/^\d/) ? listMatch[2] : '•';
88
+ parts.push(_jsxs(Text, { children: [indent, _jsx(Text, { color: "#f02a30", children: bullet }), " ", listMatch[3], '\n'] }, key++));
89
+ remaining = remaining.slice(listMatch[0].length);
90
+ continue;
91
+ }
92
+ // Links: [text](url)
93
+ const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)]+)\)/);
94
+ if (linkMatch) {
95
+ parts.push(_jsx(Text, { color: "blue", underline: true, children: linkMatch[1] }, key++));
96
+ remaining = remaining.slice(linkMatch[0].length);
97
+ continue;
98
+ }
99
+ // Default: consume until next special character or end
100
+ const nextSpecial = remaining.search(/[\*_`#\[\n-]/);
101
+ if (nextSpecial === -1) {
102
+ parts.push(_jsx(Text, { children: remaining }, key++));
103
+ break;
104
+ }
105
+ else if (nextSpecial === 0) {
106
+ // Not a valid markdown char, consume single char
107
+ parts.push(_jsx(Text, { children: remaining[0] }, key++));
108
+ remaining = remaining.slice(1);
109
+ }
110
+ else {
111
+ parts.push(_jsx(Text, { children: remaining.slice(0, nextSpecial) }, key++));
112
+ remaining = remaining.slice(nextSpecial);
113
+ }
114
+ }
115
+ return _jsx(Text, { children: parts });
116
+ });
117
+ const CodeBlock = memo(({ code, language, index }) => {
118
+ const lines = code.split('\n');
119
+ 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)))] }));
120
+ });
121
+ // Keywords by language - defined outside component to avoid recreation
122
+ const SYNTAX_KEYWORDS = {
123
+ 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'],
124
+ 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'],
125
+ 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'],
126
+ 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'],
127
+ go: ['func', 'var', 'const', 'if', 'else', 'for', 'range', 'switch', 'case', 'return', 'struct', 'interface', 'package', 'import', 'type', 'map', 'chan', 'go', 'defer', 'true', 'false', 'nil'],
128
+ 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'],
129
+ bash: ['if', 'then', 'else', 'fi', 'for', 'do', 'done', 'while', 'case', 'esac', 'function', 'return', 'echo', 'exit', 'export', 'local'],
130
+ sh: ['if', 'then', 'else', 'fi', 'for', 'do', 'done', 'while', 'case', 'esac', 'function', 'return', 'echo', 'exit', 'export', 'local'],
131
+ php: ['function', 'class', 'public', 'private', 'protected', 'return', 'if', 'else', 'elseif', 'for', 'foreach', 'while', 'echo', 'print', 'new', 'this', 'true', 'false', 'null', 'use', 'namespace', 'extends', 'implements'],
132
+ 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'],
133
+ css: ['color', 'background', 'margin', 'padding', 'border', 'width', 'height', 'display', 'flex', 'grid', 'position', 'top', 'left', 'right', 'bottom', 'font', 'text'],
134
+ 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'],
135
+ };
136
+ // Syntax highlighting component - memoized to prevent re-renders
137
+ const SyntaxLine = memo(({ line, language }) => {
138
+ const lang = language.toLowerCase();
139
+ const langKeywords = SYNTAX_KEYWORDS[lang] || [];
140
+ const parts = [];
141
+ let remaining = line;
142
+ let key = 0;
143
+ while (remaining.length > 0) {
144
+ // Comments
145
+ if (remaining.startsWith('//') || remaining.startsWith('#')) {
146
+ parts.push(_jsx(Text, { color: "gray", children: remaining }, key++));
147
+ break;
148
+ }
149
+ // Multi-line string/docstring
150
+ if (remaining.startsWith('"""') || remaining.startsWith("'''")) {
151
+ const quote = remaining.slice(0, 3);
152
+ parts.push(_jsx(Text, { color: "green", children: remaining }, key++));
153
+ break;
154
+ }
155
+ // Strings
156
+ const stringMatch = remaining.match(/^(["'`])(?:[^\\]|\\.)*?\1/);
157
+ if (stringMatch) {
158
+ parts.push(_jsx(Text, { color: "green", children: stringMatch[0] }, key++));
159
+ remaining = remaining.slice(stringMatch[0].length);
160
+ continue;
161
+ }
162
+ // Keywords
163
+ const wordMatch = remaining.match(/^[a-zA-Z_]\w*/);
164
+ if (wordMatch) {
165
+ const word = wordMatch[0];
166
+ if (langKeywords.includes(word)) {
167
+ parts.push(_jsx(Text, { color: "magenta", bold: true, children: word }, key++));
168
+ }
169
+ else if (word.match(/^[A-Z]/)) {
170
+ // Class names / constants
171
+ parts.push(_jsx(Text, { color: "cyan", children: word }, key++));
172
+ }
173
+ else {
174
+ parts.push(_jsx(Text, { children: word }, key++));
175
+ }
176
+ remaining = remaining.slice(word.length);
177
+ continue;
178
+ }
179
+ // Numbers
180
+ const numMatch = remaining.match(/^\d+\.?\d*/);
181
+ if (numMatch) {
182
+ parts.push(_jsx(Text, { color: "#f02a30", children: numMatch[0] }, key++));
183
+ remaining = remaining.slice(numMatch[0].length);
184
+ continue;
185
+ }
186
+ // Operators and brackets
187
+ const opMatch = remaining.match(/^[+\-*/%=<>!&|^~?:;,.()\[\]{}]+/);
188
+ if (opMatch) {
189
+ parts.push(_jsx(Text, { color: "white", children: opMatch[0] }, key++));
190
+ remaining = remaining.slice(opMatch[0].length);
191
+ continue;
192
+ }
193
+ // Default
194
+ parts.push(_jsx(Text, { children: remaining[0] }, key++));
195
+ remaining = remaining.slice(1);
196
+ }
197
+ return _jsx(Text, { children: parts });
198
+ });
199
+ function parseContent(text) {
200
+ const segments = [];
201
+ // Match code blocks: ```lang or ``` followed by code and closing ```
202
+ const codeBlockRegex = /```(\w*)\n?([\s\S]*?)```/g;
203
+ let lastIndex = 0;
204
+ let match;
205
+ while ((match = codeBlockRegex.exec(text)) !== null) {
206
+ // Text before code block
207
+ if (match.index > lastIndex) {
208
+ const textBefore = text.slice(lastIndex, match.index).trim();
209
+ if (textBefore) {
210
+ segments.push({ type: 'text', content: textBefore });
211
+ }
212
+ }
213
+ // Code block
214
+ const lang = match[1] || 'code';
215
+ const code = match[2] || '';
216
+ segments.push({
217
+ type: 'code',
218
+ content: code.trim(),
219
+ language: lang,
220
+ });
221
+ lastIndex = match.index + match[0].length;
222
+ }
223
+ // Remaining text
224
+ if (lastIndex < text.length) {
225
+ const remaining = text.slice(lastIndex).trim();
226
+ if (remaining) {
227
+ segments.push({ type: 'text', content: remaining });
228
+ }
229
+ }
230
+ if (segments.length === 0) {
231
+ segments.push({ type: 'text', content: text });
232
+ }
233
+ return segments;
234
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Message } from '../config';
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 {};
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Static } from 'ink';
3
+ import { MessageView } from './Message';
4
+ export const MessageList = ({ messages, }) => {
5
+ // Use Static component to prevent messages from re-rendering on every keystroke
6
+ // This keeps the scroll position stable when typing in input field
7
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(Static, { items: messages, children: (msg, index) => (_jsx(MessageView, { role: msg.role, content: msg.content }, index)) }) }));
8
+ };
@@ -0,0 +1,7 @@
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 {};
@@ -0,0 +1,52 @@
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 } from '../config/index';
5
+ import { getProjectSummary } from '../utils/project';
6
+ export const ProjectPermission = ({ projectPath, onComplete }) => {
7
+ const [step, setStep] = useState('read');
8
+ const [readGranted, setReadGranted] = useState(false);
9
+ const summary = getProjectSummary(projectPath);
10
+ const projectName = summary?.name || projectPath.split('/').pop() || 'Unknown';
11
+ useInput((input, key) => {
12
+ const char = input.toLowerCase();
13
+ if (step === 'read') {
14
+ if (char === 'y') {
15
+ // Grant for this session only
16
+ setReadGranted(true);
17
+ setStep('write');
18
+ }
19
+ else if (char === 'a') {
20
+ // Grant permanently (always)
21
+ setProjectPermission(projectPath, true, false);
22
+ setReadGranted(true);
23
+ setStep('write');
24
+ }
25
+ else if (char === 'n' || key.escape) {
26
+ // Deny
27
+ onComplete(false, false);
28
+ }
29
+ }
30
+ else if (step === 'write') {
31
+ if (char === 'y') {
32
+ // Grant write for session
33
+ onComplete(true, false, true);
34
+ }
35
+ else if (char === 'a') {
36
+ // Grant write permanently
37
+ setProjectPermission(projectPath, true, true);
38
+ onComplete(true, true, true);
39
+ }
40
+ else if (char === 'n' || key.escape) {
41
+ // Read only
42
+ if (readGranted) {
43
+ onComplete(true, false, false);
44
+ }
45
+ else {
46
+ onComplete(false, false, false);
47
+ }
48
+ }
49
+ }
50
+ });
51
+ 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 === '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" })] })] }))] }));
52
+ };
@@ -0,0 +1,10 @@
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 {};
@@ -0,0 +1,30 @@
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
+ };
@@ -0,0 +1,9 @@
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 {};
@@ -0,0 +1,88 @@
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';
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: "gray", 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: "gray", children: "\u2191\u2193 Navigate " }), _jsx(Text, { color: "gray", children: "Enter Select " }), _jsx(Text, { color: "gray", children: "N New " }), _jsx(Text, { color: "gray", children: "Esc New" })] })] }));
88
+ };
@@ -0,0 +1,12 @@
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 {};
@@ -0,0 +1,102 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Text, Box, useInput } from 'ink';
4
+ import TextInput from 'ink-text-input';
5
+ import { listSessions, saveSession, loadSession, deleteSession } from '../config/index';
6
+ export const Sessions = ({ history, onLoad, onClose, onDelete, deleteMode = false, projectPath }) => {
7
+ const [name, setName] = useState('');
8
+ const [message, setMessage] = useState(deleteMode ? 'Select a session to delete (D or Enter)' : '');
9
+ const [sessions, setSessions] = useState(listSessions(projectPath));
10
+ const [selectedIndex, setSelectedIndex] = useState(0);
11
+ const [confirmDelete, setConfirmDelete] = useState(null);
12
+ useInput((input, key) => {
13
+ // Handle delete confirmation
14
+ if (confirmDelete) {
15
+ if (input === 'y' || input === 'Y') {
16
+ if (deleteSession(confirmDelete, projectPath)) {
17
+ setMessage(`Deleted: ${confirmDelete}`);
18
+ setSessions(listSessions(projectPath));
19
+ if (onDelete)
20
+ onDelete(confirmDelete);
21
+ }
22
+ else {
23
+ setMessage('Failed to delete');
24
+ }
25
+ setConfirmDelete(null);
26
+ }
27
+ else if (input === 'n' || input === 'N' || key.escape) {
28
+ setConfirmDelete(null);
29
+ setMessage('Delete cancelled');
30
+ }
31
+ return;
32
+ }
33
+ if (key.escape) {
34
+ onClose();
35
+ return;
36
+ }
37
+ if (key.upArrow && sessions.length > 0) {
38
+ setSelectedIndex(i => Math.max(0, i - 1));
39
+ setName(sessions[Math.max(0, selectedIndex - 1)] || '');
40
+ }
41
+ if (key.downArrow && sessions.length > 0) {
42
+ setSelectedIndex(i => Math.min(sessions.length - 1, i + 1));
43
+ setName(sessions[Math.min(sessions.length - 1, selectedIndex + 1)] || '');
44
+ }
45
+ // In delete mode, Enter also triggers delete
46
+ if (deleteMode && key.return) {
47
+ handleDelete();
48
+ return;
49
+ }
50
+ // S = Save, L = Load, D = Delete
51
+ if (!deleteMode && (input === 's' || input === 'S')) {
52
+ handleSave();
53
+ }
54
+ if (!deleteMode && (input === 'l' || input === 'L')) {
55
+ handleLoad();
56
+ }
57
+ if (input === 'd' || input === 'D') {
58
+ handleDelete();
59
+ }
60
+ });
61
+ const handleSave = () => {
62
+ if (!name.trim()) {
63
+ setMessage('Enter session name');
64
+ return;
65
+ }
66
+ if (history.length === 0) {
67
+ setMessage('Nothing to save');
68
+ return;
69
+ }
70
+ if (saveSession(name.trim(), history, projectPath)) {
71
+ setMessage(`Saved: ${name}`);
72
+ setTimeout(onClose, 1000);
73
+ }
74
+ else {
75
+ setMessage('Failed to save');
76
+ }
77
+ };
78
+ const handleLoad = () => {
79
+ if (!name.trim()) {
80
+ setMessage('Enter session name');
81
+ return;
82
+ }
83
+ const loaded = loadSession(name.trim(), projectPath);
84
+ if (loaded) {
85
+ onLoad(loaded, name.trim());
86
+ }
87
+ else {
88
+ setMessage('Session not found');
89
+ }
90
+ };
91
+ const handleDelete = () => {
92
+ const sessionName = name.trim() || (sessions.length > 0 ? sessions[selectedIndex] : '');
93
+ if (!sessionName) {
94
+ setMessage('Select a session to delete');
95
+ return;
96
+ }
97
+ // Ask for confirmation
98
+ setConfirmDelete(sessionName);
99
+ setMessage(`Delete "${sessionName}"? (Y/N)`);
100
+ };
101
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, children: [_jsx(Text, { color: "#f02a30", bold: true, children: deleteMode ? 'Delete Session' : 'Sessions' }), _jsx(Text, { children: " " }), !deleteMode && (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsx(Text, { color: "#f02a30", children: "Name: " }), _jsx(TextInput, { value: name, onChange: setName, placeholder: "session name..." })] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Actions: ", _jsx(Text, { color: "#f02a30", children: "S" }), "=Save ", _jsx(Text, { color: "#f02a30", children: "L" }), "=Load ", _jsx(Text, { color: "#f02a30", children: "D" }), "=Delete ", _jsx(Text, { color: "#f02a30", children: "Esc" }), "=Close"] })] })), deleteMode && (_jsx(_Fragment, { children: _jsxs(Text, { children: ["Actions: ", _jsx(Text, { color: "#f02a30", children: "Enter/D" }), "=Delete ", _jsx(Text, { color: "#f02a30", children: "Esc" }), "=Cancel"] }) })), sessions.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { children: "Saved sessions (\u2191/\u2193 to select):" }), sessions.map((s, i) => (_jsxs(Text, { children: [i === selectedIndex ? _jsx(Text, { color: "#f02a30", children: "\u25B8 " }) : ' ', _jsx(Text, { color: i === selectedIndex ? '#f02a30' : undefined, children: s })] }, s)))] })), message && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", children: message })] }))] }));
102
+ };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface SettingsProps {
3
+ onClose: () => void;
4
+ notify: (msg: string) => void;
5
+ }
6
+ export declare const Settings: React.FC<SettingsProps>;
7
+ export {};