sessioncast-cli 2.0.2 → 2.0.3
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/agent/runner.js +23 -0
- package/dist/agent/session-handler.d.ts +1 -2
- package/dist/agent/session-handler.js +34 -79
- package/dist/agent/tmux-executor.d.ts +3 -33
- package/dist/agent/tmux-executor.js +3 -50
- package/dist/agent/tmux.d.ts +2 -6
- package/dist/agent/tmux.js +2 -9
- package/dist/agent/types.d.ts +0 -10
- package/dist/agent/websocket.d.ts +2 -21
- package/dist/agent/websocket.js +10 -46
- package/dist/commands/agent.js +3 -0
- package/dist/index.js +14 -0
- package/dist/sentry.d.ts +4 -0
- package/dist/sentry.js +87 -0
- package/package.json +2 -1
- package/dist/autopilot/index.d.ts +0 -94
- package/dist/autopilot/index.js +0 -322
- package/dist/autopilot/mission-analyzer.d.ts +0 -27
- package/dist/autopilot/mission-analyzer.js +0 -232
- package/dist/autopilot/project-detector.d.ts +0 -12
- package/dist/autopilot/project-detector.js +0 -326
- package/dist/autopilot/source-scanner.d.ts +0 -26
- package/dist/autopilot/source-scanner.js +0 -285
- package/dist/autopilot/speckit-generator.d.ts +0 -60
- package/dist/autopilot/speckit-generator.js +0 -511
- package/dist/autopilot/types.d.ts +0 -110
- package/dist/autopilot/types.js +0 -6
- package/dist/autopilot/workflow-generator.d.ts +0 -33
- package/dist/autopilot/workflow-generator.js +0 -278
- package/dist/commands/autopilot.d.ts +0 -30
- package/dist/commands/autopilot.js +0 -262
- package/dist/commands/project.d.ts +0 -33
- package/dist/commands/project.js +0 -350
- package/dist/project/executor.d.ts +0 -73
- package/dist/project/executor.js +0 -437
- package/dist/project/index.d.ts +0 -4
- package/dist/project/index.js +0 -20
- package/dist/project/manager.d.ts +0 -66
- package/dist/project/manager.js +0 -290
- package/dist/project/relay-client.d.ts +0 -37
- package/dist/project/relay-client.js +0 -204
- package/dist/project/types.d.ts +0 -48
- package/dist/project/types.js +0 -3
- package/dist/utils/fileUtils.d.ts +0 -28
- package/dist/utils/fileUtils.js +0 -159
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* MissionAnalyzer - LLM-based analysis of user prompts
|
|
4
|
-
* Breaks down a single prompt into structured workflow steps
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.analyzeMission = analyzeMission;
|
|
8
|
-
exports.analyzeQuick = analyzeQuick;
|
|
9
|
-
/**
|
|
10
|
-
* Analyze user prompt and generate mission breakdown
|
|
11
|
-
*/
|
|
12
|
-
async function analyzeMission(prompt, context, llmClient) {
|
|
13
|
-
const systemPrompt = buildSystemPrompt(context);
|
|
14
|
-
const userPrompt = buildUserPrompt(prompt, context);
|
|
15
|
-
try {
|
|
16
|
-
const response = await llmClient.chat([
|
|
17
|
-
{ role: 'system', content: systemPrompt },
|
|
18
|
-
{ role: 'user', content: userPrompt }
|
|
19
|
-
]);
|
|
20
|
-
return parseAnalysisResponse(response, prompt);
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
// Fallback to simple analysis if LLM fails
|
|
24
|
-
return createFallbackAnalysis(prompt, context);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Build system prompt for mission analysis
|
|
29
|
-
*/
|
|
30
|
-
function buildSystemPrompt(context) {
|
|
31
|
-
return `You are a software development task analyzer for SessionCast.
|
|
32
|
-
Your job is to analyze user requests and break them down into executable steps.
|
|
33
|
-
|
|
34
|
-
Project Information:
|
|
35
|
-
- Type: ${context.projectType}
|
|
36
|
-
- Name: ${context.projectName}
|
|
37
|
-
- Main Language: ${getMainLanguage(context.projectType)}
|
|
38
|
-
|
|
39
|
-
Rules:
|
|
40
|
-
1. Break down the task into clear, independent steps
|
|
41
|
-
2. Each step should be achievable by a single AI agent
|
|
42
|
-
3. Consider dependencies between steps
|
|
43
|
-
4. Estimate complexity based on the scope of changes
|
|
44
|
-
5. Identify required agent types: frontend, backend, fullstack, mobile, infra, test
|
|
45
|
-
|
|
46
|
-
Response Format (JSON):
|
|
47
|
-
{
|
|
48
|
-
"mission": "Brief summary of the mission",
|
|
49
|
-
"complexity": "simple|medium|complex",
|
|
50
|
-
"steps": [
|
|
51
|
-
{
|
|
52
|
-
"name": "Step name",
|
|
53
|
-
"description": "What this step accomplishes",
|
|
54
|
-
"type": "frontend|backend|fullstack|mobile|infra|test",
|
|
55
|
-
"prompt": "Detailed prompt for the AI agent",
|
|
56
|
-
"dependsOn": ["previous step names if any"]
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
|
-
"requiredAgents": ["agent types needed"],
|
|
60
|
-
"estimatedFiles": ["likely files to be modified"]
|
|
61
|
-
}`;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Build user prompt with context
|
|
65
|
-
*/
|
|
66
|
-
function buildUserPrompt(prompt, context) {
|
|
67
|
-
let userContent = `Analyze this task and break it down into executable steps:
|
|
68
|
-
|
|
69
|
-
USER REQUEST: ${prompt}
|
|
70
|
-
|
|
71
|
-
PROJECT STRUCTURE:
|
|
72
|
-
${context.projectStructure}
|
|
73
|
-
|
|
74
|
-
SOURCE FILES:
|
|
75
|
-
${context.sources.map(s => `- ${s.relativePath} (${s.type})`).join('\n')}`;
|
|
76
|
-
if (context.keyFilesContent) {
|
|
77
|
-
userContent += `\n\nKEY FILES CONTENT:\n${context.keyFilesContent}`;
|
|
78
|
-
}
|
|
79
|
-
return userContent;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Parse LLM response into MissionAnalysis
|
|
83
|
-
*/
|
|
84
|
-
function parseAnalysisResponse(response, originalPrompt) {
|
|
85
|
-
try {
|
|
86
|
-
// Extract JSON from response
|
|
87
|
-
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
88
|
-
if (!jsonMatch) {
|
|
89
|
-
throw new Error('No JSON found in response');
|
|
90
|
-
}
|
|
91
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
92
|
-
// Validate and normalize
|
|
93
|
-
const analysis = {
|
|
94
|
-
mission: parsed.mission || originalPrompt,
|
|
95
|
-
complexity: validateComplexity(parsed.complexity),
|
|
96
|
-
steps: normalizeSteps(parsed.steps || []),
|
|
97
|
-
requiredAgents: parsed.requiredAgents || [],
|
|
98
|
-
estimatedFiles: parsed.estimatedFiles || []
|
|
99
|
-
};
|
|
100
|
-
// Ensure at least one step
|
|
101
|
-
if (analysis.steps.length === 0) {
|
|
102
|
-
analysis.steps = [{
|
|
103
|
-
name: 'Execute task',
|
|
104
|
-
description: originalPrompt,
|
|
105
|
-
type: 'fullstack',
|
|
106
|
-
prompt: originalPrompt
|
|
107
|
-
}];
|
|
108
|
-
}
|
|
109
|
-
return analysis;
|
|
110
|
-
}
|
|
111
|
-
catch (error) {
|
|
112
|
-
// Return basic analysis if parsing fails
|
|
113
|
-
return {
|
|
114
|
-
mission: originalPrompt,
|
|
115
|
-
complexity: 'simple',
|
|
116
|
-
steps: [{
|
|
117
|
-
name: 'Execute task',
|
|
118
|
-
description: originalPrompt,
|
|
119
|
-
type: 'fullstack',
|
|
120
|
-
prompt: originalPrompt
|
|
121
|
-
}],
|
|
122
|
-
requiredAgents: ['fullstack'],
|
|
123
|
-
estimatedFiles: []
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Validate complexity value
|
|
129
|
-
*/
|
|
130
|
-
function validateComplexity(value) {
|
|
131
|
-
const valid = ['simple', 'medium', 'complex'];
|
|
132
|
-
return valid.includes(value) ? value : 'simple';
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Normalize steps array
|
|
136
|
-
*/
|
|
137
|
-
function normalizeSteps(steps) {
|
|
138
|
-
return steps.map((step, index) => ({
|
|
139
|
-
name: step.name || `Step ${index + 1}`,
|
|
140
|
-
description: step.description || '',
|
|
141
|
-
type: validateStepType(step.type),
|
|
142
|
-
prompt: step.prompt || step.description || '',
|
|
143
|
-
dependsOn: Array.isArray(step.dependsOn) ? step.dependsOn : undefined
|
|
144
|
-
}));
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Validate step type
|
|
148
|
-
*/
|
|
149
|
-
function validateStepType(value) {
|
|
150
|
-
const valid = ['frontend', 'backend', 'fullstack', 'mobile', 'infra', 'test'];
|
|
151
|
-
return valid.includes(value) ? value : 'fullstack';
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Get main language for project type
|
|
155
|
-
*/
|
|
156
|
-
function getMainLanguage(projectType) {
|
|
157
|
-
const map = {
|
|
158
|
-
android: 'Kotlin/Java',
|
|
159
|
-
ios: 'Swift',
|
|
160
|
-
react: 'TypeScript/React',
|
|
161
|
-
next: 'TypeScript/Next.js',
|
|
162
|
-
vue: 'TypeScript/Vue',
|
|
163
|
-
node: 'JavaScript/Node.js',
|
|
164
|
-
python: 'Python',
|
|
165
|
-
spring: 'Java/Spring',
|
|
166
|
-
go: 'Go',
|
|
167
|
-
rust: 'Rust',
|
|
168
|
-
unknown: 'Unknown'
|
|
169
|
-
};
|
|
170
|
-
return map[projectType];
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Create fallback analysis without LLM
|
|
174
|
-
*/
|
|
175
|
-
function createFallbackAnalysis(prompt, context) {
|
|
176
|
-
// Simple heuristic-based analysis
|
|
177
|
-
const promptLower = prompt.toLowerCase();
|
|
178
|
-
let type = 'fullstack';
|
|
179
|
-
let complexity = 'simple';
|
|
180
|
-
// Detect type from keywords
|
|
181
|
-
if (promptLower.includes('ui') || promptLower.includes('화면') || promptLower.includes('button') || promptLower.includes('버튼')) {
|
|
182
|
-
type = 'frontend';
|
|
183
|
-
}
|
|
184
|
-
else if (promptLower.includes('api') || promptLower.includes('database') || promptLower.includes('db') || promptLower.includes('서버')) {
|
|
185
|
-
type = 'backend';
|
|
186
|
-
}
|
|
187
|
-
else if (promptLower.includes('test') || promptLower.includes('테스트')) {
|
|
188
|
-
type = 'test';
|
|
189
|
-
}
|
|
190
|
-
else if (promptLower.includes('deploy') || promptLower.includes('docker') || promptLower.includes('배포')) {
|
|
191
|
-
type = 'infra';
|
|
192
|
-
}
|
|
193
|
-
// Mobile detection based on project type
|
|
194
|
-
if (context.projectType === 'android' || context.projectType === 'ios') {
|
|
195
|
-
type = 'mobile';
|
|
196
|
-
}
|
|
197
|
-
// Detect complexity from keywords
|
|
198
|
-
if (promptLower.includes('refactor') ||
|
|
199
|
-
promptLower.includes('리팩토링') ||
|
|
200
|
-
promptLower.includes('architecture') ||
|
|
201
|
-
promptLower.includes('아키텍처') ||
|
|
202
|
-
promptLower.includes('전체') ||
|
|
203
|
-
promptLower.includes('모든')) {
|
|
204
|
-
complexity = 'complex';
|
|
205
|
-
}
|
|
206
|
-
else if (promptLower.includes('add') ||
|
|
207
|
-
promptLower.includes('추가') ||
|
|
208
|
-
promptLower.includes('create') ||
|
|
209
|
-
promptLower.includes('생성') ||
|
|
210
|
-
promptLower.includes('implement') ||
|
|
211
|
-
promptLower.includes('구현')) {
|
|
212
|
-
complexity = 'medium';
|
|
213
|
-
}
|
|
214
|
-
return {
|
|
215
|
-
mission: prompt,
|
|
216
|
-
complexity,
|
|
217
|
-
steps: [{
|
|
218
|
-
name: 'Execute task',
|
|
219
|
-
description: prompt,
|
|
220
|
-
type,
|
|
221
|
-
prompt
|
|
222
|
-
}],
|
|
223
|
-
requiredAgents: [type === 'mobile' ? 'mobile' : type],
|
|
224
|
-
estimatedFiles: []
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Analyze mission without LLM (for quick mode or when LLM is unavailable)
|
|
229
|
-
*/
|
|
230
|
-
function analyzeQuick(prompt, context) {
|
|
231
|
-
return createFallbackAnalysis(prompt, context);
|
|
232
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ProjectDetector - Auto-detect project type from directory
|
|
3
|
-
*/
|
|
4
|
-
import { ProjectDetectionResult } from './types';
|
|
5
|
-
/**
|
|
6
|
-
* Detect project type from directory
|
|
7
|
-
*/
|
|
8
|
-
export declare function detectProjectType(dir: string): Promise<ProjectDetectionResult>;
|
|
9
|
-
/**
|
|
10
|
-
* Get a summary of project structure for LLM context
|
|
11
|
-
*/
|
|
12
|
-
export declare function getProjectStructure(dir: string, maxDepth?: number): string;
|
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* ProjectDetector - Auto-detect project type from directory
|
|
4
|
-
*/
|
|
5
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
-
}
|
|
11
|
-
Object.defineProperty(o, k2, desc);
|
|
12
|
-
}) : (function(o, m, k, k2) {
|
|
13
|
-
if (k2 === undefined) k2 = k;
|
|
14
|
-
o[k2] = m[k];
|
|
15
|
-
}));
|
|
16
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
-
}) : function(o, v) {
|
|
19
|
-
o["default"] = v;
|
|
20
|
-
});
|
|
21
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
-
var ownKeys = function(o) {
|
|
23
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
-
var ar = [];
|
|
25
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
-
return ar;
|
|
27
|
-
};
|
|
28
|
-
return ownKeys(o);
|
|
29
|
-
};
|
|
30
|
-
return function (mod) {
|
|
31
|
-
if (mod && mod.__esModule) return mod;
|
|
32
|
-
var result = {};
|
|
33
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
-
__setModuleDefault(result, mod);
|
|
35
|
-
return result;
|
|
36
|
-
};
|
|
37
|
-
})();
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.detectProjectType = detectProjectType;
|
|
40
|
-
exports.getProjectStructure = getProjectStructure;
|
|
41
|
-
const fs = __importStar(require("fs"));
|
|
42
|
-
const path = __importStar(require("path"));
|
|
43
|
-
const PROJECT_SIGNATURES = {
|
|
44
|
-
android: {
|
|
45
|
-
files: ['build.gradle', 'app/build.gradle', 'settings.gradle'],
|
|
46
|
-
contentPatterns: [
|
|
47
|
-
{ file: 'build.gradle', pattern: /android\s*\{/ },
|
|
48
|
-
{ file: 'app/build.gradle', pattern: /android\s*\{/ }
|
|
49
|
-
],
|
|
50
|
-
weight: 10
|
|
51
|
-
},
|
|
52
|
-
ios: {
|
|
53
|
-
files: ['*.xcodeproj', '*.xcworkspace', 'Podfile', 'Package.swift'],
|
|
54
|
-
weight: 10
|
|
55
|
-
},
|
|
56
|
-
react: {
|
|
57
|
-
files: ['package.json'],
|
|
58
|
-
contentPatterns: [
|
|
59
|
-
{ file: 'package.json', pattern: /"react":\s*"/ }
|
|
60
|
-
],
|
|
61
|
-
weight: 8
|
|
62
|
-
},
|
|
63
|
-
next: {
|
|
64
|
-
files: ['next.config.js', 'next.config.mjs', 'next.config.ts'],
|
|
65
|
-
weight: 9
|
|
66
|
-
},
|
|
67
|
-
vue: {
|
|
68
|
-
files: ['vue.config.js', 'vite.config.ts', 'vite.config.js'],
|
|
69
|
-
contentPatterns: [
|
|
70
|
-
{ file: 'package.json', pattern: /"vue":\s*"/ }
|
|
71
|
-
],
|
|
72
|
-
weight: 8
|
|
73
|
-
},
|
|
74
|
-
node: {
|
|
75
|
-
files: ['package.json'],
|
|
76
|
-
weight: 5
|
|
77
|
-
},
|
|
78
|
-
python: {
|
|
79
|
-
files: ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile'],
|
|
80
|
-
weight: 7
|
|
81
|
-
},
|
|
82
|
-
spring: {
|
|
83
|
-
files: ['pom.xml', 'build.gradle'],
|
|
84
|
-
contentPatterns: [
|
|
85
|
-
{ file: 'pom.xml', pattern: /spring-boot/ },
|
|
86
|
-
{ file: 'build.gradle', pattern: /spring-boot/ }
|
|
87
|
-
],
|
|
88
|
-
weight: 9
|
|
89
|
-
},
|
|
90
|
-
go: {
|
|
91
|
-
files: ['go.mod', 'go.sum'],
|
|
92
|
-
weight: 8
|
|
93
|
-
},
|
|
94
|
-
rust: {
|
|
95
|
-
files: ['Cargo.toml', 'Cargo.lock'],
|
|
96
|
-
weight: 8
|
|
97
|
-
},
|
|
98
|
-
unknown: {
|
|
99
|
-
files: [],
|
|
100
|
-
weight: 0
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const LANGUAGE_MAP = {
|
|
104
|
-
android: 'kotlin',
|
|
105
|
-
ios: 'swift',
|
|
106
|
-
react: 'typescript',
|
|
107
|
-
next: 'typescript',
|
|
108
|
-
vue: 'typescript',
|
|
109
|
-
node: 'javascript',
|
|
110
|
-
python: 'python',
|
|
111
|
-
spring: 'java',
|
|
112
|
-
go: 'go',
|
|
113
|
-
rust: 'rust'
|
|
114
|
-
};
|
|
115
|
-
/**
|
|
116
|
-
* Detect project type from directory
|
|
117
|
-
*/
|
|
118
|
-
async function detectProjectType(dir) {
|
|
119
|
-
const scores = new Map();
|
|
120
|
-
const foundConfigFiles = [];
|
|
121
|
-
// Initialize scores
|
|
122
|
-
for (const type of Object.keys(PROJECT_SIGNATURES)) {
|
|
123
|
-
scores.set(type, 0);
|
|
124
|
-
}
|
|
125
|
-
// Check file signatures
|
|
126
|
-
for (const [type, signature] of Object.entries(PROJECT_SIGNATURES)) {
|
|
127
|
-
for (const filePattern of signature.files) {
|
|
128
|
-
const found = await findFiles(dir, filePattern);
|
|
129
|
-
if (found.length > 0) {
|
|
130
|
-
scores.set(type, (scores.get(type) || 0) + signature.weight);
|
|
131
|
-
foundConfigFiles.push(...found);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Check content patterns
|
|
135
|
-
if (signature.contentPatterns) {
|
|
136
|
-
for (const { file, pattern } of signature.contentPatterns) {
|
|
137
|
-
const filePath = path.join(dir, file);
|
|
138
|
-
if (fs.existsSync(filePath)) {
|
|
139
|
-
try {
|
|
140
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
141
|
-
if (pattern.test(content)) {
|
|
142
|
-
scores.set(type, (scores.get(type) || 0) + 5);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// Ignore read errors
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// Find the best match
|
|
153
|
-
let bestType = 'unknown';
|
|
154
|
-
let bestScore = 0;
|
|
155
|
-
let totalScore = 0;
|
|
156
|
-
for (const [type, score] of scores.entries()) {
|
|
157
|
-
totalScore += score;
|
|
158
|
-
if (score > bestScore) {
|
|
159
|
-
bestScore = score;
|
|
160
|
-
bestType = type;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Calculate confidence
|
|
164
|
-
const confidence = totalScore > 0 ? Math.min(bestScore / (totalScore * 0.5), 1) : 0;
|
|
165
|
-
// Get project name
|
|
166
|
-
const projectName = await getProjectName(dir, bestType);
|
|
167
|
-
return {
|
|
168
|
-
type: bestType,
|
|
169
|
-
name: projectName,
|
|
170
|
-
confidence,
|
|
171
|
-
configFiles: [...new Set(foundConfigFiles)],
|
|
172
|
-
mainLanguage: LANGUAGE_MAP[bestType]
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Find files matching pattern in directory
|
|
177
|
-
*/
|
|
178
|
-
async function findFiles(dir, pattern) {
|
|
179
|
-
const results = [];
|
|
180
|
-
try {
|
|
181
|
-
if (pattern.includes('*')) {
|
|
182
|
-
// Glob pattern
|
|
183
|
-
const ext = pattern.replace('*', '');
|
|
184
|
-
const entries = fs.readdirSync(dir);
|
|
185
|
-
for (const entry of entries) {
|
|
186
|
-
if (entry.endsWith(ext)) {
|
|
187
|
-
results.push(entry);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
else if (pattern.includes('/')) {
|
|
192
|
-
// Path pattern
|
|
193
|
-
const fullPath = path.join(dir, pattern);
|
|
194
|
-
if (fs.existsSync(fullPath)) {
|
|
195
|
-
results.push(pattern);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
// Simple filename
|
|
200
|
-
const fullPath = path.join(dir, pattern);
|
|
201
|
-
if (fs.existsSync(fullPath)) {
|
|
202
|
-
results.push(pattern);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
// Ignore errors
|
|
208
|
-
}
|
|
209
|
-
return results;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Get project name from config files
|
|
213
|
-
*/
|
|
214
|
-
async function getProjectName(dir, type) {
|
|
215
|
-
// Try package.json
|
|
216
|
-
const packageJsonPath = path.join(dir, 'package.json');
|
|
217
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
218
|
-
try {
|
|
219
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
220
|
-
if (pkg.name)
|
|
221
|
-
return pkg.name;
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
// Ignore
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Try settings.gradle (Android)
|
|
228
|
-
const settingsGradlePath = path.join(dir, 'settings.gradle');
|
|
229
|
-
if (fs.existsSync(settingsGradlePath)) {
|
|
230
|
-
try {
|
|
231
|
-
const content = fs.readFileSync(settingsGradlePath, 'utf-8');
|
|
232
|
-
const match = content.match(/rootProject\.name\s*=\s*['"](.+)['"]/);
|
|
233
|
-
if (match)
|
|
234
|
-
return match[1];
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
// Ignore
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
// Try pom.xml (Maven)
|
|
241
|
-
const pomPath = path.join(dir, 'pom.xml');
|
|
242
|
-
if (fs.existsSync(pomPath)) {
|
|
243
|
-
try {
|
|
244
|
-
const content = fs.readFileSync(pomPath, 'utf-8');
|
|
245
|
-
const match = content.match(/<artifactId>(.+?)<\/artifactId>/);
|
|
246
|
-
if (match)
|
|
247
|
-
return match[1];
|
|
248
|
-
}
|
|
249
|
-
catch {
|
|
250
|
-
// Ignore
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
// Try Cargo.toml (Rust)
|
|
254
|
-
const cargoPath = path.join(dir, 'Cargo.toml');
|
|
255
|
-
if (fs.existsSync(cargoPath)) {
|
|
256
|
-
try {
|
|
257
|
-
const content = fs.readFileSync(cargoPath, 'utf-8');
|
|
258
|
-
const match = content.match(/name\s*=\s*"(.+?)"/);
|
|
259
|
-
if (match)
|
|
260
|
-
return match[1];
|
|
261
|
-
}
|
|
262
|
-
catch {
|
|
263
|
-
// Ignore
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
// Try go.mod (Go)
|
|
267
|
-
const goModPath = path.join(dir, 'go.mod');
|
|
268
|
-
if (fs.existsSync(goModPath)) {
|
|
269
|
-
try {
|
|
270
|
-
const content = fs.readFileSync(goModPath, 'utf-8');
|
|
271
|
-
const match = content.match(/module\s+(.+)/);
|
|
272
|
-
if (match) {
|
|
273
|
-
const parts = match[1].split('/');
|
|
274
|
-
return parts[parts.length - 1];
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
catch {
|
|
278
|
-
// Ignore
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// Fallback to directory name
|
|
282
|
-
return path.basename(dir);
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Get a summary of project structure for LLM context
|
|
286
|
-
*/
|
|
287
|
-
function getProjectStructure(dir, maxDepth = 3) {
|
|
288
|
-
const lines = [];
|
|
289
|
-
function walk(currentDir, prefix, depth) {
|
|
290
|
-
if (depth > maxDepth)
|
|
291
|
-
return;
|
|
292
|
-
try {
|
|
293
|
-
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
294
|
-
// Sort: directories first, then files
|
|
295
|
-
entries.sort((a, b) => {
|
|
296
|
-
if (a.isDirectory() && !b.isDirectory())
|
|
297
|
-
return -1;
|
|
298
|
-
if (!a.isDirectory() && b.isDirectory())
|
|
299
|
-
return 1;
|
|
300
|
-
return a.name.localeCompare(b.name);
|
|
301
|
-
});
|
|
302
|
-
// Filter out common non-essential directories
|
|
303
|
-
const filtered = entries.filter(e => !['node_modules', '.git', '.gradle', 'build', 'dist', '__pycache__',
|
|
304
|
-
'.idea', '.vscode', 'target', '.next', 'venv', 'env'].includes(e.name));
|
|
305
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
306
|
-
const entry = filtered[i];
|
|
307
|
-
const isLast = i === filtered.length - 1;
|
|
308
|
-
const connector = isLast ? '└── ' : '├── ';
|
|
309
|
-
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
310
|
-
if (entry.isDirectory()) {
|
|
311
|
-
lines.push(`${prefix}${connector}${entry.name}/`);
|
|
312
|
-
walk(path.join(currentDir, entry.name), newPrefix, depth + 1);
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
lines.push(`${prefix}${connector}${entry.name}`);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
catch {
|
|
320
|
-
// Ignore permission errors
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
lines.push(path.basename(dir) + '/');
|
|
324
|
-
walk(dir, '', 1);
|
|
325
|
-
return lines.slice(0, 100).join('\n'); // Limit to 100 lines
|
|
326
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SourceScanner - Scan and collect relevant source files for context
|
|
3
|
-
*/
|
|
4
|
-
import { SourceInfo, ProjectType } from './types';
|
|
5
|
-
interface ScanOptions {
|
|
6
|
-
maxFiles?: number;
|
|
7
|
-
maxFileSize?: number;
|
|
8
|
-
includeTests?: boolean;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Scan directory for source files
|
|
12
|
-
*/
|
|
13
|
-
export declare function scanSources(dir: string, projectType: ProjectType, options?: ScanOptions): Promise<SourceInfo[]>;
|
|
14
|
-
/**
|
|
15
|
-
* Read source file content (for LLM context)
|
|
16
|
-
*/
|
|
17
|
-
export declare function readSourceContent(source: SourceInfo): string | null;
|
|
18
|
-
/**
|
|
19
|
-
* Get condensed context for LLM (file list with sizes)
|
|
20
|
-
*/
|
|
21
|
-
export declare function getSourcesSummary(sources: SourceInfo[]): string;
|
|
22
|
-
/**
|
|
23
|
-
* Get key files content for deep analysis
|
|
24
|
-
*/
|
|
25
|
-
export declare function getKeyFilesContent(sources: SourceInfo[], maxTotalSize?: number): string;
|
|
26
|
-
export {};
|