codeep 1.0.126 → 1.0.128

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/dist/api/index.js CHANGED
@@ -2,6 +2,7 @@ import { config, getApiKey } from '../config/index.js';
2
2
  import { withRetry, isNetworkError, isTimeoutError } from '../utils/retry.js';
3
3
  import { getProvider, getProviderBaseUrl, getProviderAuthHeader } from '../config/providers.js';
4
4
  import { logApiRequest, logApiResponse } from '../utils/logger.js';
5
+ import { loadProjectIntelligence, generateContextFromIntelligence } from '../utils/projectIntelligence.js';
5
6
  // Error messages by language
6
7
  const ERROR_MESSAGES = {
7
8
  en: {
@@ -24,8 +25,16 @@ function getErrorMessage(key) {
24
25
  }
25
26
  // Store project context for use in system prompt
26
27
  let currentProjectContext = null;
28
+ let cachedIntelligence = null;
27
29
  export function setProjectContext(ctx) {
28
30
  currentProjectContext = ctx;
31
+ // Try to load cached intelligence when project context is set
32
+ if (ctx) {
33
+ cachedIntelligence = loadProjectIntelligence(ctx.root);
34
+ }
35
+ else {
36
+ cachedIntelligence = null;
37
+ }
29
38
  }
30
39
  export async function chat(message, history = [], onChunk, onRetry, projectContext, abortSignal) {
31
40
  // Update project context if provided
@@ -132,6 +141,21 @@ The user will review and approve changes before they are applied.`
132
141
  : `
133
142
 
134
143
  **Write Access:** READ-ONLY - You can analyze code but cannot suggest file modifications.`;
144
+ // Use cached intelligence if available (from /scan command)
145
+ // This provides richer context than basic project structure
146
+ if (cachedIntelligence) {
147
+ const intelligenceContext = generateContextFromIntelligence(cachedIntelligence);
148
+ const projectInfo = `
149
+
150
+ ## Project Intelligence (cached)
151
+ ${intelligenceContext}
152
+ ${writeInfo}
153
+
154
+ When the user mentions a file path, the file content will be automatically attached to their message.
155
+ You can analyze, explain, or suggest improvements to the code.`;
156
+ return basePrompt + projectInfo;
157
+ }
158
+ // Fallback to basic project context
135
159
  const projectInfo = `
136
160
 
137
161
  ## Project Context
package/dist/app.js CHANGED
@@ -35,8 +35,8 @@ import { saveContext, loadContext, clearContext, mergeContext } from './utils/co
35
35
  import { performCodeReview, formatReviewResult } from './utils/codeReview.js';
36
36
  import { learnFromProject, addCustomRule, getLearningStatus } from './utils/learning.js';
37
37
  import { getAllSkills, findSkill, formatSkillsList, formatSkillHelp, generateSkillPrompt, saveCustomSkill, deleteCustomSkill, parseSkillChain, parseSkillArgs, searchSkills, trackSkillUsage, getSkillStats } from './utils/skills.js';
38
- import { AgentProgress, LiveCodeStream } from './components/AgentProgress.js';
39
38
  import { createActionLog } from './utils/tools.js';
39
+ import { scanProject, saveProjectIntelligence, loadProjectIntelligence, generateContextFromIntelligence } from './utils/projectIntelligence.js';
40
40
  export const App = () => {
41
41
  const { exit } = useApp();
42
42
  const { stdout } = useStdout();
@@ -82,6 +82,7 @@ export const App = () => {
82
82
  const [agentThinking, setAgentThinking] = useState('');
83
83
  const [agentResult, setAgentResult] = useState(null);
84
84
  const [agentDryRun, setAgentDryRun] = useState(false);
85
+ const [agentStreamingContent, setAgentStreamingContent] = useState(''); // Live action log in chat
85
86
  // Load API keys for ALL providers on startup and check if current provider is configured
86
87
  useEffect(() => {
87
88
  loadAllApiKeys().then(() => {
@@ -266,6 +267,7 @@ export const App = () => {
266
267
  setAgentThinking('');
267
268
  setAgentResult(null);
268
269
  setAgentDryRun(dryRun);
270
+ setAgentStreamingContent(''); // Reset streaming content
269
271
  // Add user message
270
272
  const userMessage = {
271
273
  role: 'user',
@@ -322,6 +324,78 @@ export const App = () => {
322
324
  }
323
325
  return updated;
324
326
  });
327
+ // Add formatted action to streaming content (stays in chat)
328
+ const actionType = actionLog.type;
329
+ const target = actionLog.target.split('/').pop() || actionLog.target; // Just filename
330
+ const fullPath = actionLog.target;
331
+ const status = actionLog.result === 'success' ? '✓' : '✗';
332
+ const statusColor = actionLog.result === 'success' ? '' : ' (failed)';
333
+ // Get file extension for syntax highlighting
334
+ const getLanguage = (filename) => {
335
+ const ext = filename.split('.').pop()?.toLowerCase() || '';
336
+ const langMap = {
337
+ 'ts': 'typescript', 'tsx': 'typescript', 'js': 'javascript', 'jsx': 'javascript',
338
+ 'py': 'python', 'rb': 'ruby', 'go': 'go', 'rs': 'rust', 'java': 'java',
339
+ 'php': 'php', 'css': 'css', 'scss': 'css', 'html': 'html', 'vue': 'html',
340
+ 'json': 'json', 'yml': 'yaml', 'yaml': 'yaml', 'md': 'markdown',
341
+ 'sh': 'bash', 'bash': 'bash', 'sql': 'sql',
342
+ };
343
+ return langMap[ext] || ext || 'code';
344
+ };
345
+ let actionLine = '';
346
+ if (actionType === 'write' && actionLog.details) {
347
+ // Show full code with syntax highlighting
348
+ const lang = getLanguage(target);
349
+ actionLine = `${status} **Created ${fullPath}**\n\`\`\`${lang}\n${actionLog.details}\n\`\`\``;
350
+ }
351
+ else if (actionType === 'write') {
352
+ actionLine = `${status} Created **${target}**${statusColor}`;
353
+ }
354
+ else if (actionType === 'edit' && actionLog.details) {
355
+ // Show edited code with syntax highlighting
356
+ const lang = getLanguage(target);
357
+ actionLine = `${status} **Edited ${fullPath}**\n\`\`\`${lang}\n${actionLog.details}\n\`\`\``;
358
+ }
359
+ else if (actionType === 'edit') {
360
+ actionLine = `${status} Edited **${target}**${statusColor}`;
361
+ }
362
+ else if (actionType === 'read') {
363
+ // Don't show read actions in stream - too noisy
364
+ return;
365
+ }
366
+ else if (actionType === 'delete') {
367
+ actionLine = `${status} Deleted **${fullPath}**${statusColor}`;
368
+ }
369
+ else if (actionType === 'command') {
370
+ const cmd = actionLog.target;
371
+ // Show command output if available
372
+ if (actionLog.details && actionLog.details.trim()) {
373
+ actionLine = `${status} Ran \`${cmd}\`\n\`\`\`\n${actionLog.details.slice(0, 500)}${actionLog.details.length > 500 ? '\n...(truncated)' : ''}\n\`\`\``;
374
+ }
375
+ else {
376
+ actionLine = `${status} Ran \`${cmd}\`${statusColor}`;
377
+ }
378
+ }
379
+ else if (actionType === 'search') {
380
+ // Don't show search actions - too noisy
381
+ return;
382
+ }
383
+ else if (actionType === 'mkdir') {
384
+ actionLine = `${status} Created directory **${fullPath}**${statusColor}`;
385
+ }
386
+ else if (actionType === 'fetch') {
387
+ actionLine = `${status} Fetched ${target}${statusColor}`;
388
+ }
389
+ else if (actionType === 'list') {
390
+ // Don't show list actions
391
+ return;
392
+ }
393
+ else {
394
+ actionLine = `◦ ${actionType}: ${target}`;
395
+ }
396
+ if (actionLine) {
397
+ setAgentStreamingContent(prev => prev + (prev ? '\n\n' : '') + actionLine);
398
+ }
325
399
  },
326
400
  onThinking: (text) => {
327
401
  // Strip <think> and <tool_call> tags from thinking text
@@ -983,6 +1057,57 @@ export const App = () => {
983
1057
  }]);
984
1058
  break;
985
1059
  }
1060
+ case '/scan': {
1061
+ if (!projectContext) {
1062
+ notify('No project context');
1063
+ break;
1064
+ }
1065
+ // Check for subcommands
1066
+ if (args[0] === 'status') {
1067
+ const intel = loadProjectIntelligence(projectContext.root);
1068
+ if (intel) {
1069
+ const age = Math.round((Date.now() - new Date(intel.scannedAt).getTime()) / (1000 * 60 * 60));
1070
+ notify(`Last scan: ${age}h ago | ${intel.structure.totalFiles} files | ${intel.type}`);
1071
+ }
1072
+ else {
1073
+ notify('No scan data. Run /scan to analyze project.');
1074
+ }
1075
+ break;
1076
+ }
1077
+ if (args[0] === 'clear') {
1078
+ // Clear cached intelligence
1079
+ const intelPath = `${projectContext.root}/.codeep/intelligence.json`;
1080
+ try {
1081
+ const fs = require('fs');
1082
+ if (fs.existsSync(intelPath)) {
1083
+ fs.unlinkSync(intelPath);
1084
+ notify('Project intelligence cleared');
1085
+ }
1086
+ else {
1087
+ notify('No cached intelligence to clear');
1088
+ }
1089
+ }
1090
+ catch {
1091
+ notify('Failed to clear intelligence');
1092
+ }
1093
+ break;
1094
+ }
1095
+ // Run full scan
1096
+ notify('Scanning project...');
1097
+ scanProject(projectContext.root).then(intelligence => {
1098
+ saveProjectIntelligence(projectContext.root, intelligence);
1099
+ // Generate and display summary
1100
+ const context = generateContextFromIntelligence(intelligence);
1101
+ setMessages(prev => [...prev, {
1102
+ role: 'assistant',
1103
+ content: `# Project Scan Complete\n\n${context}\n\n---\n*Saved to .codeep/intelligence.json*`,
1104
+ }]);
1105
+ notify(`Scanned: ${intelligence.structure.totalFiles} files, ${intelligence.structure.totalDirectories} dirs`);
1106
+ }).catch(error => {
1107
+ notify('Scan failed: ' + error.message);
1108
+ });
1109
+ break;
1110
+ }
986
1111
  case '/learn': {
987
1112
  if (!projectContext) {
988
1113
  notify('No project context');
@@ -1319,7 +1444,7 @@ export const App = () => {
1319
1444
  // Helper to check if we're showing an inline menu
1320
1445
  const isInlineMenu = ['help', 'status', 'settings', 'sessions', 'sessions-delete',
1321
1446
  'logout', 'search', 'export', 'model', 'provider', 'protocol', 'language'].includes(screen);
1322
- return (_jsxs(Box, { flexDirection: "column", children: [messages.length === 0 && !isLoading && _jsx(Logo, {}), messages.length === 0 && !isLoading && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Box, { justifyContent: "center", children: _jsxs(Text, { children: ["Connected to ", _jsx(Text, { color: "#f02a30", children: config.get('model') }), ". Type ", _jsx(Text, { color: "#f02a30", children: "/help" }), " for commands."] }) }), _jsx(Text, { children: " " }), _jsx(Box, { justifyContent: "center", children: _jsx(Text, { color: "cyan", bold: true, children: "Welcome to Codeep - Your AI Coding Assistant" }) }), _jsx(Text, { children: " " }), _jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Ask questions about your code or request implementations"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Use ", _jsxs(Text, { color: "cyan", children: ["/agent ", '<task>'] }), " for autonomous task execution"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Type ", _jsx(Text, { color: "cyan", children: "/diff" }), " to review changes, ", _jsx(Text, { color: "cyan", children: "/commit" }), " to generate commit messages"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Configure settings with ", _jsx(Text, { color: "cyan", children: "/settings" }), " - enable Agent Mode for auto-execution"] })] }), _jsx(Text, { children: " " }), _jsx(Box, { justifyContent: "center", children: _jsx(Text, { color: "gray", children: "Start typing your message or use a command to begin..." }) }), _jsx(Text, { children: " " })] })), _jsx(MessageList, { messages: messages, streamingContent: streamingContent }, sessionId), isLoading && !isAgentRunning && _jsx(Loading, { isStreaming: !!streamingContent }), (isAgentRunning || agentResult) && (_jsxs(Box, { flexDirection: "column", children: [_jsx(LiveCodeStream, { actions: agentActions, isRunning: isAgentRunning, terminalWidth: stdout?.columns || 80 }), isAgentRunning && (_jsx(AgentProgress, { isRunning: true, iteration: agentIteration, maxIterations: 50, actions: agentActions, currentThinking: agentThinking, dryRun: agentDryRun }))] }, "agent-ui")), pendingFileChanges.length > 0 && !isLoading && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, marginY: 1, children: [_jsxs(Text, { color: "#f02a30", bold: true, children: ["\u2713 Detected ", pendingFileChanges.length, " file change(s):"] }), pendingFileChanges.map((change, i) => {
1447
+ return (_jsxs(Box, { flexDirection: "column", children: [messages.length === 0 && !isLoading && _jsx(Logo, {}), messages.length === 0 && !isLoading && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Box, { justifyContent: "center", children: _jsxs(Text, { children: ["Connected to ", _jsx(Text, { color: "#f02a30", children: config.get('model') }), ". Type ", _jsx(Text, { color: "#f02a30", children: "/help" }), " for commands."] }) }), _jsx(Text, { children: " " }), _jsx(Box, { justifyContent: "center", children: _jsx(Text, { color: "cyan", bold: true, children: "Welcome to Codeep - Your AI Coding Assistant" }) }), _jsx(Text, { children: " " }), _jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Ask questions about your code or request implementations"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Use ", _jsxs(Text, { color: "cyan", children: ["/agent ", '<task>'] }), " for autonomous task execution"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Type ", _jsx(Text, { color: "cyan", children: "/diff" }), " to review changes, ", _jsx(Text, { color: "cyan", children: "/commit" }), " to generate commit messages"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "\u2022" }), " Configure settings with ", _jsx(Text, { color: "cyan", children: "/settings" }), " - enable Agent Mode for auto-execution"] })] }), _jsx(Text, { children: " " }), _jsx(Box, { justifyContent: "center", children: _jsx(Text, { color: "gray", children: "Start typing your message or use a command to begin..." }) }), _jsx(Text, { children: " " })] })), _jsx(MessageList, { messages: messages, streamingContent: streamingContent, agentStreamingContent: isAgentRunning ? agentStreamingContent : undefined }, sessionId), isLoading && !isAgentRunning && _jsx(Loading, { isStreaming: !!streamingContent }), isAgentRunning && (_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { color: agentDryRun ? 'yellow' : '#f02a30', children: [agentDryRun ? '[DRY RUN]' : '[AGENT]', " Step ", agentIteration, " | ", agentActions.length, " actions"] }), _jsx(Text, { color: "gray", children: " | Press Esc to stop" })] }, "agent-status")), pendingFileChanges.length > 0 && !isLoading && (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "#f02a30", padding: 1, marginY: 1, children: [_jsxs(Text, { color: "#f02a30", bold: true, children: ["\u2713 Detected ", pendingFileChanges.length, " file change(s):"] }), pendingFileChanges.map((change, i) => {
1323
1448
  const actionColor = change.action === 'delete' ? 'red' : change.action === 'edit' ? 'yellow' : 'green';
1324
1449
  const actionLabel = change.action === 'delete' ? 'DELETE' : change.action === 'edit' ? 'EDIT' : 'CREATE';
1325
1450
  return (_jsxs(Text, { children: ["\u2022 ", _jsxs(Text, { color: actionColor, children: ["[", actionLabel, "]"] }), " ", change.path, change.action !== 'delete' && change.content.includes('\n') && ` (${change.content.split('\n').length} lines)`] }, i));
@@ -14,12 +14,12 @@ interface AgentProgressProps {
14
14
  }
15
15
  export declare const AgentProgress: React.FC<AgentProgressProps>;
16
16
  /**
17
- * Live Code Stream component - shows current file operation with live preview
17
+ * Live Statistics component - shows real-time agent progress
18
18
  *
19
- * KEY DESIGN: This component always renders exactly MAX_LINES lines
20
- * - While running: shows live code preview
21
- * - When finished: shows summary statistics
22
- * - This prevents ghost content and terminal jumping because height is constant
19
+ * KEY DESIGN: Always renders exactly FIXED_HEIGHT lines to prevent terminal jumping
20
+ * - While running: shows current action + live statistics
21
+ * - When finished: shows final summary (same height)
22
+ * - NEVER returns null - always renders the same box structure
23
23
  */
24
24
  interface LiveCodeStreamProps {
25
25
  actions: ActionLog[];
@@ -145,21 +145,17 @@ const isSectionBreak = (line, prevLine) => {
145
145
  }
146
146
  return false;
147
147
  };
148
- const MAX_PREVIEW_LINES = 12; // Code preview lines for live streaming
148
+ const FIXED_HEIGHT = 6; // Fixed number of content lines (excluding border)
149
149
  export const LiveCodeStream = memo(({ actions, isRunning, terminalWidth = 80 }) => {
150
- // Find the current write/edit action for live preview (skip read actions)
151
150
  const currentAction = actions.length > 0 ? actions[actions.length - 1] : null;
152
- const isCodeAction = currentAction && (currentAction.type === 'write' || currentAction.type === 'edit');
153
- // Skip rendering for read actions - no preview needed
154
- const isReadAction = currentAction && currentAction.type === 'read';
155
- // ALL HOOKS MUST BE CALLED BEFORE ANY EARLY RETURNS (Rules of Hooks)
156
151
  // Calculate statistics from all actions
157
152
  const stats = useMemo(() => {
158
153
  const filesCreated = actions.filter(a => a.type === 'write' && a.result === 'success');
159
154
  const filesEdited = actions.filter(a => a.type === 'edit' && a.result === 'success');
160
155
  const filesDeleted = actions.filter(a => a.type === 'delete' && a.result === 'success');
161
- const filesRead = actions.filter(a => a.type === 'read' && a.result === 'success');
162
- const commands = actions.filter(a => a.type === 'command' && a.result === 'success');
156
+ const filesRead = actions.filter(a => a.type === 'read');
157
+ const commands = actions.filter(a => a.type === 'command');
158
+ const searches = actions.filter(a => a.type === 'search');
163
159
  const errors = actions.filter(a => a.result === 'error');
164
160
  // Calculate total lines written
165
161
  let totalLinesWritten = 0;
@@ -174,55 +170,114 @@ export const LiveCodeStream = memo(({ actions, isRunning, terminalWidth = 80 })
174
170
  filesDeleted: filesDeleted.length,
175
171
  filesRead: filesRead.length,
176
172
  commands: commands.length,
173
+ searches: searches.length,
177
174
  errors: errors.length,
178
175
  totalLinesWritten,
179
- createdFiles: filesCreated.map(a => a.target.split('/').pop() || a.target),
180
- editedFiles: filesEdited.map(a => a.target.split('/').pop() || a.target),
176
+ totalActions: actions.length,
181
177
  };
182
178
  }, [actions]);
183
- // Get code lines for preview
184
- const codeLines = useMemo(() => {
185
- if (!isCodeAction || !currentAction?.details)
186
- return [];
187
- return currentAction.details.split('\n');
188
- }, [isCodeAction, currentAction?.details]);
189
- // Don't render anything if no actions yet (agent just started)
190
- if (actions.length === 0) {
191
- return null;
192
- }
193
- const filename = currentAction?.target.split('/').pop() || '';
194
- const ext = getFileExtension(filename);
195
- const langLabel = getLanguageLabel(ext);
196
- // RUNNING STATE: Show live code preview
197
- if (isRunning && isCodeAction && codeLines.length > 0) {
198
- // Show last N lines of code being written
199
- const visibleLines = codeLines.slice(-MAX_PREVIEW_LINES);
200
- const hiddenCount = Math.max(0, codeLines.length - MAX_PREVIEW_LINES);
201
- const actionIcon = currentAction.type === 'write' ? '+' : '~';
202
- const actionColor = currentAction.type === 'write' ? 'green' : 'yellow';
203
- const boxWidth = Math.min(terminalWidth - 4, 76);
204
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: actionColor, marginBottom: 1, width: boxWidth, children: [_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { color: actionColor, bold: true, children: [actionIcon, " "] }), _jsx(Text, { color: "white", bold: true, children: filename }), _jsxs(Text, { color: "gray", children: [" \u2022 ", langLabel, " \u2022 ", codeLines.length, " lines"] })] }), _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [hiddenCount > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ... ", hiddenCount, " more lines above"] })), visibleLines.map((line, i) => {
205
- const lineNum = hiddenCount + i + 1;
206
- const displayLine = line.length > boxWidth - 10
207
- ? line.slice(0, boxWidth - 13) + '...'
208
- : line;
209
- return (_jsxs(Text, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: [String(lineNum).padStart(3), " "] }), _jsx(Text, { color: getCodeColor(line, ext), children: displayLine || ' ' })] }, i));
210
- })] })] }));
179
+ const boxWidth = Math.min(terminalWidth - 4, 60);
180
+ // Build content lines array - always exactly FIXED_HEIGHT lines
181
+ const contentLines = [];
182
+ if (isRunning) {
183
+ // Line 1: Current action
184
+ if (currentAction) {
185
+ contentLines.push(_jsxs(Box, { children: [_jsxs(Text, { color: getActionColor(currentAction.type), bold: true, children: [getActionLabel(currentAction.type), " "] }), _jsx(Text, { color: "white", children: formatTarget(currentAction.target, boxWidth - 15) })] }, "current"));
186
+ }
187
+ else {
188
+ contentLines.push(_jsx(Text, { color: "gray", children: "Starting..." }, "current"));
189
+ }
190
+ // Line 2: Separator
191
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: '─'.repeat(boxWidth - 4) }, "sep"));
192
+ // Line 3: File changes stats
193
+ const changesParts = [];
194
+ if (stats.filesCreated > 0)
195
+ changesParts.push(`+${stats.filesCreated} created`);
196
+ if (stats.filesEdited > 0)
197
+ changesParts.push(`~${stats.filesEdited} edited`);
198
+ if (stats.filesDeleted > 0)
199
+ changesParts.push(`-${stats.filesDeleted} deleted`);
200
+ if (changesParts.length > 0) {
201
+ contentLines.push(_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "Files: " }), _jsx(Text, { color: "green", children: changesParts.join(' ') })] }, "changes"));
202
+ }
203
+ else {
204
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: "Files: no changes yet" }, "changes"));
205
+ }
206
+ // Line 4: Other stats
207
+ const otherParts = [];
208
+ if (stats.filesRead > 0)
209
+ otherParts.push(`${stats.filesRead}R`);
210
+ if (stats.commands > 0)
211
+ otherParts.push(`${stats.commands}C`);
212
+ if (stats.searches > 0)
213
+ otherParts.push(`${stats.searches}S`);
214
+ if (stats.totalLinesWritten > 0)
215
+ otherParts.push(`${stats.totalLinesWritten}L`);
216
+ contentLines.push(_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "Stats: " }), _jsx(Text, { color: "gray", children: otherParts.length > 0 ? otherParts.join(' | ') : 'gathering...' })] }, "other"));
217
+ // Line 5: Errors if any
218
+ if (stats.errors > 0) {
219
+ contentLines.push(_jsxs(Text, { color: "red", children: [stats.errors, " error(s)"] }, "errors"));
220
+ }
221
+ else {
222
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: " " }, "errors"));
223
+ }
224
+ // Line 6: Help text
225
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: "Press Esc to stop" }, "help"));
211
226
  }
212
- // RUNNING STATE but not a code action (and not read): show simple status
213
- // Skip showing box for read actions - they don't need preview
214
- if (isRunning && currentAction && !isReadAction) {
215
- const boxWidth = Math.min(terminalWidth - 4, 76);
216
- return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", marginBottom: 1, width: boxWidth, children: _jsxs(Box, { paddingX: 1, children: [_jsx(Text, { color: "cyan", bold: true, children: "\u25E6 " }), _jsxs(Text, { color: getActionColor(currentAction.type), children: [getActionLabel(currentAction.type), " "] }), _jsx(Text, { color: "white", children: formatTarget(currentAction.target) })] }) }));
227
+ else {
228
+ // FINISHED STATE
229
+ // Line 1: Complete header
230
+ contentLines.push(_jsxs(Text, { color: stats.errors > 0 ? 'red' : 'green', bold: true, children: [stats.errors > 0 ? 'Completed with errors' : 'Completed', " (", stats.totalActions, " actions)"] }, "header"));
231
+ // Line 2: Separator
232
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: '─'.repeat(boxWidth - 4) }, "sep"));
233
+ // Line 3: File changes
234
+ const changesParts = [];
235
+ if (stats.filesCreated > 0)
236
+ changesParts.push(`+${stats.filesCreated} created`);
237
+ if (stats.filesEdited > 0)
238
+ changesParts.push(`~${stats.filesEdited} edited`);
239
+ if (stats.filesDeleted > 0)
240
+ changesParts.push(`-${stats.filesDeleted} deleted`);
241
+ if (changesParts.length > 0) {
242
+ contentLines.push(_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "Files: " }), _jsx(Text, { color: "green", children: changesParts.join(' ') })] }, "changes"));
243
+ }
244
+ else {
245
+ contentLines.push(_jsx(Text, { color: "gray", children: "Files: no changes made" }, "changes"));
246
+ }
247
+ // Line 4: Lines written
248
+ if (stats.totalLinesWritten > 0) {
249
+ contentLines.push(_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "Lines: " }), _jsxs(Text, { color: "white", children: [stats.totalLinesWritten, " written"] })] }, "lines"));
250
+ }
251
+ else {
252
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: " " }, "lines"));
253
+ }
254
+ // Line 5: Other stats
255
+ const otherParts = [];
256
+ if (stats.filesRead > 0)
257
+ otherParts.push(`${stats.filesRead} read`);
258
+ if (stats.commands > 0)
259
+ otherParts.push(`${stats.commands} commands`);
260
+ if (stats.searches > 0)
261
+ otherParts.push(`${stats.searches} searches`);
262
+ if (otherParts.length > 0) {
263
+ contentLines.push(_jsxs(Text, { color: "gray", dimColor: true, children: ["Also: ", otherParts.join(', ')] }, "other"));
264
+ }
265
+ else {
266
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: " " }, "other"));
267
+ }
268
+ // Line 6: Errors
269
+ if (stats.errors > 0) {
270
+ contentLines.push(_jsxs(Text, { color: "red", children: [stats.errors, " error(s) occurred"] }, "errors"));
271
+ }
272
+ else {
273
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: " " }, "errors"));
274
+ }
217
275
  }
218
- // For read actions while running, don't show anything
219
- if (isRunning && isReadAction) {
220
- return null;
276
+ // Pad to fixed height if needed
277
+ while (contentLines.length < FIXED_HEIGHT) {
278
+ contentLines.push(_jsx(Text, { color: "gray", dimColor: true, children: " " }, `pad-${contentLines.length}`));
221
279
  }
222
- // FINISHED STATE: Show summary statistics (same height as live preview)
223
- const boxWidth = Math.min(terminalWidth - 4, 76);
224
- const hasChanges = stats.filesCreated > 0 || stats.filesEdited > 0 || stats.filesDeleted > 0;
225
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: stats.errors > 0 ? 'red' : 'green', marginBottom: 1, width: boxWidth, children: [_jsxs(Box, { paddingX: 1, children: [_jsxs(Text, { color: stats.errors > 0 ? 'red' : 'green', bold: true, children: [stats.errors > 0 ? '!' : '✓', " Session Complete"] }), _jsxs(Text, { color: "gray", children: [" \u2022 ", actions.length, " actions"] })] }), _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [hasChanges ? (_jsxs(_Fragment, { children: [stats.filesCreated > 0 && (_jsxs(Text, { children: [_jsxs(Text, { color: "green", bold: true, children: [" + ", stats.filesCreated, " "] }), _jsx(Text, { color: "gray", children: "file(s) created" }), stats.createdFiles.length <= 3 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" (", stats.createdFiles.join(', '), ")"] }))] })), stats.filesEdited > 0 && (_jsxs(Text, { children: [_jsxs(Text, { color: "yellow", bold: true, children: [" ~ ", stats.filesEdited, " "] }), _jsx(Text, { color: "gray", children: "file(s) modified" }), stats.editedFiles.length <= 3 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" (", stats.editedFiles.join(', '), ")"] }))] })), stats.filesDeleted > 0 && (_jsxs(Text, { children: [_jsxs(Text, { color: "red", bold: true, children: [" - ", stats.filesDeleted, " "] }), _jsx(Text, { color: "gray", children: "file(s) deleted" })] })), stats.totalLinesWritten > 0 && (_jsxs(Text, { children: [_jsxs(Text, { color: "cyan", bold: true, children: [" \u2261 ", stats.totalLinesWritten, " "] }), _jsx(Text, { color: "gray", children: "total lines written" })] })), stats.filesRead > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", stats.filesRead, " file(s) read"] })), stats.commands > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", stats.commands, " command(s) run"] }))] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: " No file changes made" }), stats.filesRead > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", stats.filesRead, " file(s) read"] })), stats.commands > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", stats.commands, " command(s) run"] }))] })), stats.errors > 0 && (_jsxs(Text, { color: "red", children: [" \u2717 ", stats.errors, " error(s) occurred"] })), Array.from({ length: Math.max(0, MAX_PREVIEW_LINES - 6) }).map((_, i) => (_jsx(Text, { color: "gray", dimColor: true, children: " " }, `pad-${i}`)))] })] }));
280
+ return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: isRunning ? 'cyan' : (stats.errors > 0 ? 'red' : 'green'), paddingX: 1, marginBottom: 1, width: boxWidth, children: contentLines }));
226
281
  });
227
282
  LiveCodeStream.displayName = 'LiveCodeStream';
228
283
  // Helper functions for action display
@@ -254,17 +309,18 @@ const getActionLabel = (type) => {
254
309
  default: return type.toUpperCase();
255
310
  }
256
311
  };
257
- const formatTarget = (target) => {
312
+ const formatTarget = (target, maxLen = 50) => {
258
313
  // For file paths, show just the filename or last part
259
314
  if (target.includes('/')) {
260
315
  const parts = target.split('/');
261
316
  const filename = parts[parts.length - 1];
262
317
  if (parts.length > 2) {
263
- return `.../${parts[parts.length - 2]}/${filename}`;
318
+ const short = `.../${parts[parts.length - 2]}/${filename}`;
319
+ return short.length > maxLen ? '...' + short.slice(-(maxLen - 3)) : short;
264
320
  }
265
- return target.length > 50 ? '...' + target.slice(-47) : target;
321
+ return target.length > maxLen ? '...' + target.slice(-(maxLen - 3)) : target;
266
322
  }
267
- return target.length > 50 ? target.slice(0, 47) + '...' : target;
323
+ return target.length > maxLen ? target.slice(0, maxLen - 3) + '...' : target;
268
324
  };
269
325
  /**
270
326
  * Single action item display
@@ -3,5 +3,5 @@ import { Text, Box } from 'ink';
3
3
  import { detectProjectFeatures } from '../utils/project.js';
4
4
  export const Help = ({ projectPath }) => {
5
5
  const features = projectPath ? detectProjectFeatures(projectPath) : null;
6
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Available Commands" }), _jsx(Text, { children: " " }), _jsxs(Box, { flexDirection: "row", gap: 2, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/help" }), " - Show this help"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/status" }), " - Current status"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/settings" }), " - Adjust settings"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/sessions" }), " - Manage sessions"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/grant" }), " - Grant permissions"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/agent" }), " ", '<task>', " - Run agent"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/clear" }), " - Clear chat"] })] }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/model" }), " - Switch model"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/provider" }), " - Switch provider"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/diff" }), " - Review git changes"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/commit" }), " - Generate commit msg"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/export" }), " - Export chat"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/copy" }), " [n] - Copy code block"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/exit" }), " - Quit"] })] })] }), features && (features.hasGit || features.hasPackageJson || features.hasPython || features.hasCargo || features.hasGoMod) && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: "green", bold: true, children: "Suggested for this project:" }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [features.hasGit && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/diff" }), " - Review your uncommitted changes"] })), features.hasGit && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/commit" }), " - AI-generated commit message"] })), features.hasPackageJson && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent npm run build" }), " - Build Node.js project"] })), features.hasPython && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent pytest" }), " - Run Python tests"] })), features.hasCargo && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent cargo build" }), " - Build Rust project"] })), features.hasGoMod && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent go build" }), " - Build Go project"] }))] })] })), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", children: "Type / to see autocomplete. Docs: github.com/VladoIvankovic/Codeep" })] }));
6
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "#f02a30", bold: true, children: "Codeep Commands" }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "General" }), _jsxs(Box, { flexDirection: "row", gap: 4, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/help" }), " - Show this help"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/status" }), " - Current status"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/version" }), " - Show version"] })] }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/update" }), " - Check for updates"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/clear" }), " - Clear chat"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/exit" }), " - Quit"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "Agent & AI" }), _jsxs(Box, { flexDirection: "row", gap: 4, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/agent" }), " ", '<task>', " - Run AI agent"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/agent-dry" }), " - Preview agent actions"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/agent-stop" }), " - Stop running agent"] })] }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/grant" }), " - Grant write permission"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/scan" }), " - Scan project for AI"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/skills" }), " - List available skills"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "Git" }), _jsxs(Box, { flexDirection: "row", gap: 4, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/diff" }), " - Review git changes"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/diff --staged" }), " - Review staged"] })] }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/commit" }), " - Generate commit msg"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/apply" }), " - Apply file changes"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "Configuration" }), _jsxs(Box, { flexDirection: "row", gap: 4, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/model" }), " - Switch AI model"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/provider" }), " - Switch provider"] })] }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/settings" }), " - Adjust settings"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/lang" }), " - Set language"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "Sessions" }), _jsxs(Box, { flexDirection: "row", gap: 4, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/sessions" }), " - Manage sessions"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/rename" }), " - Rename session"] })] }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/search" }), " - Search history"] }), _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/export" }), " - Export chat"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "Clipboard" }), _jsxs(Box, { flexDirection: "row", gap: 4, children: [_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/copy" }), " [n] - Copy code block"] }) }), _jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/paste" }), " - Paste from clipboard"] }) })] }), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", bold: true, children: "Account" }), _jsxs(Box, { flexDirection: "row", gap: 4, children: [_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/login" }), " - Change API key"] }) }), _jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: [_jsx(Text, { color: "#f02a30", children: "/logout" }), " - Logout"] }) })] }), features && (features.hasGit || features.hasPackageJson || features.hasPython || features.hasCargo || features.hasGoMod) && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: "green", bold: true, children: "Suggested for this project:" }), _jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [features.hasGit && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/diff" }), " - Review your uncommitted changes"] })), features.hasGit && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/commit" }), " - AI-generated commit message"] })), features.hasPackageJson && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent npm run build" }), " - Build Node.js project"] })), features.hasPython && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent pytest" }), " - Run Python tests"] })), features.hasCargo && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent cargo build" }), " - Build Rust project"] })), features.hasGoMod && (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "/agent go build" }), " - Build Go project"] }))] })] })), _jsx(Text, { children: " " }), _jsx(Text, { color: "cyan", children: "Type / for autocomplete | Docs: github.com/VladoIvankovic/Codeep" })] }));
7
7
  };
@@ -27,6 +27,9 @@ const COMMANDS = [
27
27
  { cmd: '/agent', desc: 'Run agent for a task' },
28
28
  { cmd: '/agent-dry', desc: 'Preview agent actions' },
29
29
  { cmd: '/agent-stop', desc: 'Stop running agent' },
30
+ { cmd: '/scan', desc: 'Scan project for AI context' },
31
+ { cmd: '/scan status', desc: 'Show scan status' },
32
+ { cmd: '/scan clear', desc: 'Clear cached scan' },
30
33
  { cmd: '/clear', desc: 'Clear chat' },
31
34
  { cmd: '/login', desc: 'Change API key' },
32
35
  { cmd: '/logout', desc: 'Logout' },
@@ -3,6 +3,7 @@ import { Message } from '../config/index';
3
3
  interface MessageListProps {
4
4
  messages: Message[];
5
5
  streamingContent?: string;
6
+ agentStreamingContent?: string;
6
7
  scrollOffset?: number;
7
8
  terminalHeight?: number;
8
9
  }
@@ -21,9 +21,9 @@ MemoizedMessage.displayName = 'MemoizedMessage';
21
21
  * NOTE: This is a temporary solution until we implement a custom renderer
22
22
  * like Claude CLI uses (DEC Mode 2026 / synchronized output).
23
23
  */
24
- export const MessageList = memo(({ messages, streamingContent, }) => {
24
+ export const MessageList = memo(({ messages, streamingContent, agentStreamingContent, }) => {
25
25
  // Memoize the messages array rendering
26
26
  const renderedMessages = useMemo(() => (messages.map((msg, index) => (_jsx(MemoizedMessage, { msg: msg, index: index }, `msg-${index}-${msg.role}`)))), [messages]);
27
- return (_jsxs(Box, { flexDirection: "column", children: [renderedMessages, streamingContent && (_jsx(StreamingMessage, { content: streamingContent }))] }));
27
+ return (_jsxs(Box, { flexDirection: "column", children: [renderedMessages, streamingContent && (_jsx(StreamingMessage, { content: streamingContent })), agentStreamingContent && (_jsx(StreamingMessage, { content: `**Agent Actions:**\n${agentStreamingContent}` }))] }));
28
28
  });
29
29
  MessageList.displayName = 'MessageList';
@@ -1,16 +1,29 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Box, Text, useInput } from 'ink';
4
- import { setProjectPermission } from '../config/index.js';
4
+ import { setProjectPermission, hasStandardProjectMarkers, initializeAsProject } from '../config/index.js';
5
5
  import { getProjectSummary } from '../utils/project.js';
6
6
  export const ProjectPermission = ({ projectPath, onComplete }) => {
7
- const [step, setStep] = useState('read');
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');
8
10
  const [readGranted, setReadGranted] = useState(false);
9
11
  const summary = getProjectSummary(projectPath);
10
12
  const projectName = summary?.name || projectPath.split('/').pop() || 'Unknown';
11
13
  useInput((input, key) => {
12
14
  const char = input.toLowerCase();
13
- if (step === 'read') {
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') {
14
27
  if (char === 'y') {
15
28
  // Grant for this session only
16
29
  setReadGranted(true);
@@ -48,5 +61,5 @@ export const ProjectPermission = ({ projectPath, onComplete }) => {
48
61
  }
49
62
  }
50
63
  });
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" })] })] }))] }));
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" })] })] }))] }));
52
65
  };
@@ -43,6 +43,19 @@ interface ConfigSchema {
43
43
  }
44
44
  export type { AgentMode };
45
45
  export type { LanguageCode };
46
+ /**
47
+ * Check if directory has standard project markers (not manually initialized)
48
+ */
49
+ export declare function hasStandardProjectMarkers(path: string): boolean;
50
+ /**
51
+ * Initialize a folder as a Codeep project
52
+ * Creates .codeep/project.json marker file
53
+ */
54
+ export declare function initializeAsProject(path: string): boolean;
55
+ /**
56
+ * Check if folder was manually initialized as project
57
+ */
58
+ export declare function isManuallyInitializedProject(path: string): boolean;
46
59
  export declare const config: Conf<ConfigSchema>;
47
60
  export declare const LANGUAGES: Record<string, string>;
48
61
  export declare const PROTOCOLS: Record<string, string>;