sessioncast-cli 2.0.0 → 2.0.2

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 (34) hide show
  1. package/dist/agent/session-handler.d.ts +2 -1
  2. package/dist/agent/session-handler.js +79 -32
  3. package/dist/agent/tmux-executor.d.ts +33 -3
  4. package/dist/agent/tmux-executor.js +50 -3
  5. package/dist/agent/tmux.d.ts +6 -2
  6. package/dist/agent/tmux.js +9 -2
  7. package/dist/agent/types.d.ts +10 -0
  8. package/dist/agent/websocket.d.ts +21 -2
  9. package/dist/agent/websocket.js +46 -10
  10. package/dist/autopilot/index.d.ts +94 -0
  11. package/dist/autopilot/index.js +322 -0
  12. package/dist/autopilot/mission-analyzer.d.ts +27 -0
  13. package/dist/autopilot/mission-analyzer.js +232 -0
  14. package/dist/autopilot/project-detector.d.ts +12 -0
  15. package/dist/autopilot/project-detector.js +326 -0
  16. package/dist/autopilot/source-scanner.d.ts +26 -0
  17. package/dist/autopilot/source-scanner.js +285 -0
  18. package/dist/autopilot/speckit-generator.d.ts +60 -0
  19. package/dist/autopilot/speckit-generator.js +511 -0
  20. package/dist/autopilot/types.d.ts +110 -0
  21. package/dist/autopilot/types.js +6 -0
  22. package/dist/autopilot/workflow-generator.d.ts +33 -0
  23. package/dist/autopilot/workflow-generator.js +278 -0
  24. package/dist/project/executor.d.ts +73 -0
  25. package/dist/project/executor.js +437 -0
  26. package/dist/project/index.d.ts +4 -0
  27. package/dist/project/index.js +20 -0
  28. package/dist/project/manager.d.ts +66 -0
  29. package/dist/project/manager.js +290 -0
  30. package/dist/project/relay-client.d.ts +37 -0
  31. package/dist/project/relay-client.js +204 -0
  32. package/dist/project/types.d.ts +48 -0
  33. package/dist/project/types.js +3 -0
  34. package/package.json +1 -1
@@ -0,0 +1,326 @@
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
+ }
@@ -0,0 +1,26 @@
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 {};
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ /**
3
+ * SourceScanner - Scan and collect relevant source files for context
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.scanSources = scanSources;
40
+ exports.readSourceContent = readSourceContent;
41
+ exports.getSourcesSummary = getSourcesSummary;
42
+ exports.getKeyFilesContent = getKeyFilesContent;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const DEFAULT_OPTIONS = {
46
+ maxFiles: 50,
47
+ maxFileSize: 100 * 1024, // 100KB
48
+ includeTests: false
49
+ };
50
+ // File extensions by project type
51
+ const LANGUAGE_EXTENSIONS = {
52
+ kotlin: ['.kt', '.kts'],
53
+ java: ['.java'],
54
+ swift: ['.swift'],
55
+ typescript: ['.ts', '.tsx'],
56
+ javascript: ['.js', '.jsx'],
57
+ python: ['.py'],
58
+ go: ['.go'],
59
+ rust: ['.rs']
60
+ };
61
+ // Config file patterns
62
+ const CONFIG_PATTERNS = [
63
+ 'package.json',
64
+ 'tsconfig.json',
65
+ 'build.gradle',
66
+ 'build.gradle.kts',
67
+ 'settings.gradle',
68
+ 'settings.gradle.kts',
69
+ 'pom.xml',
70
+ 'Cargo.toml',
71
+ 'go.mod',
72
+ 'pyproject.toml',
73
+ 'requirements.txt',
74
+ 'Podfile',
75
+ '.env.example',
76
+ 'docker-compose.yml',
77
+ 'Dockerfile'
78
+ ];
79
+ // Directories to skip
80
+ const SKIP_DIRS = new Set([
81
+ 'node_modules',
82
+ '.git',
83
+ '.gradle',
84
+ 'build',
85
+ 'dist',
86
+ '__pycache__',
87
+ '.idea',
88
+ '.vscode',
89
+ 'target',
90
+ '.next',
91
+ 'venv',
92
+ 'env',
93
+ '.pytest_cache',
94
+ 'coverage',
95
+ '.nyc_output',
96
+ 'Pods',
97
+ '.build',
98
+ 'DerivedData'
99
+ ]);
100
+ // Test patterns
101
+ const TEST_PATTERNS = [
102
+ /\.test\.[jt]sx?$/,
103
+ /\.spec\.[jt]sx?$/,
104
+ /_test\.go$/,
105
+ /test_.*\.py$/,
106
+ /.*_test\.py$/,
107
+ /Test\.java$/,
108
+ /Test\.kt$/,
109
+ /Tests\.swift$/
110
+ ];
111
+ /**
112
+ * Scan directory for source files
113
+ */
114
+ async function scanSources(dir, projectType, options = {}) {
115
+ const opts = { ...DEFAULT_OPTIONS, ...options };
116
+ const sources = [];
117
+ const mainLanguage = getMainLanguage(projectType);
118
+ const extensions = getExtensionsForProject(projectType);
119
+ await walkDir(dir, dir, sources, extensions, opts);
120
+ // Sort by importance: config files first, then by size (smaller = more likely to be important)
121
+ sources.sort((a, b) => {
122
+ if (a.type === 'config' && b.type !== 'config')
123
+ return -1;
124
+ if (a.type !== 'config' && b.type === 'config')
125
+ return 1;
126
+ return a.size - b.size;
127
+ });
128
+ // Limit number of files
129
+ return sources.slice(0, opts.maxFiles);
130
+ }
131
+ /**
132
+ * Get main language for project type
133
+ */
134
+ function getMainLanguage(projectType) {
135
+ const languageMap = {
136
+ android: 'kotlin',
137
+ ios: 'swift',
138
+ react: 'typescript',
139
+ next: 'typescript',
140
+ vue: 'typescript',
141
+ node: 'javascript',
142
+ python: 'python',
143
+ spring: 'java',
144
+ go: 'go',
145
+ rust: 'rust',
146
+ unknown: 'javascript'
147
+ };
148
+ return languageMap[projectType];
149
+ }
150
+ /**
151
+ * Get file extensions for project type
152
+ */
153
+ function getExtensionsForProject(projectType) {
154
+ const mainLang = getMainLanguage(projectType);
155
+ const extensions = [...(LANGUAGE_EXTENSIONS[mainLang] || [])];
156
+ // Add related extensions
157
+ if (projectType === 'android') {
158
+ extensions.push(...LANGUAGE_EXTENSIONS.java, '.xml');
159
+ }
160
+ if (['react', 'next', 'vue', 'node'].includes(projectType)) {
161
+ extensions.push(...LANGUAGE_EXTENSIONS.javascript);
162
+ }
163
+ return extensions;
164
+ }
165
+ /**
166
+ * Recursively walk directory
167
+ */
168
+ async function walkDir(rootDir, currentDir, sources, extensions, options) {
169
+ try {
170
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
171
+ for (const entry of entries) {
172
+ const fullPath = path.join(currentDir, entry.name);
173
+ const relativePath = path.relative(rootDir, fullPath);
174
+ if (entry.isDirectory()) {
175
+ if (!SKIP_DIRS.has(entry.name)) {
176
+ await walkDir(rootDir, fullPath, sources, extensions, options);
177
+ }
178
+ }
179
+ else if (entry.isFile()) {
180
+ const ext = path.extname(entry.name);
181
+ const isConfig = CONFIG_PATTERNS.includes(entry.name);
182
+ const isSource = extensions.includes(ext);
183
+ const isTest = TEST_PATTERNS.some(p => p.test(entry.name));
184
+ // Skip tests unless explicitly included
185
+ if (isTest && !options.includeTests)
186
+ continue;
187
+ if (isConfig || isSource) {
188
+ try {
189
+ const stats = fs.statSync(fullPath);
190
+ // Skip files that are too large
191
+ if (stats.size > (options.maxFileSize || DEFAULT_OPTIONS.maxFileSize)) {
192
+ continue;
193
+ }
194
+ sources.push({
195
+ path: fullPath,
196
+ relativePath,
197
+ type: getFileType(entry.name, isConfig, isTest),
198
+ language: getLanguageFromExtension(ext),
199
+ size: stats.size
200
+ });
201
+ }
202
+ catch {
203
+ // Skip files we can't stat
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ catch {
210
+ // Ignore directory read errors
211
+ }
212
+ }
213
+ /**
214
+ * Determine file type
215
+ */
216
+ function getFileType(filename, isConfig, isTest) {
217
+ if (isConfig)
218
+ return 'config';
219
+ if (isTest)
220
+ return 'test';
221
+ if (filename.endsWith('.md') || filename.endsWith('.txt'))
222
+ return 'doc';
223
+ return 'code';
224
+ }
225
+ /**
226
+ * Get language from file extension
227
+ */
228
+ function getLanguageFromExtension(ext) {
229
+ for (const [lang, exts] of Object.entries(LANGUAGE_EXTENSIONS)) {
230
+ if (exts.includes(ext))
231
+ return lang;
232
+ }
233
+ return undefined;
234
+ }
235
+ /**
236
+ * Read source file content (for LLM context)
237
+ */
238
+ function readSourceContent(source) {
239
+ try {
240
+ return fs.readFileSync(source.path, 'utf-8');
241
+ }
242
+ catch {
243
+ return null;
244
+ }
245
+ }
246
+ /**
247
+ * Get condensed context for LLM (file list with sizes)
248
+ */
249
+ function getSourcesSummary(sources) {
250
+ const lines = ['Scanned source files:'];
251
+ const byType = new Map();
252
+ for (const source of sources) {
253
+ const list = byType.get(source.type) || [];
254
+ list.push(source);
255
+ byType.set(source.type, list);
256
+ }
257
+ for (const [type, files] of byType) {
258
+ lines.push(`\n[${type.toUpperCase()}]`);
259
+ for (const file of files) {
260
+ const sizeKB = (file.size / 1024).toFixed(1);
261
+ lines.push(` ${file.relativePath} (${sizeKB}KB)`);
262
+ }
263
+ }
264
+ return lines.join('\n');
265
+ }
266
+ /**
267
+ * Get key files content for deep analysis
268
+ */
269
+ function getKeyFilesContent(sources, maxTotalSize = 50000) {
270
+ const content = [];
271
+ let totalSize = 0;
272
+ // Prioritize config files
273
+ const configFiles = sources.filter(s => s.type === 'config');
274
+ const codeFiles = sources.filter(s => s.type === 'code');
275
+ for (const source of [...configFiles, ...codeFiles]) {
276
+ if (totalSize >= maxTotalSize)
277
+ break;
278
+ const fileContent = readSourceContent(source);
279
+ if (fileContent && totalSize + fileContent.length <= maxTotalSize) {
280
+ content.push(`\n--- ${source.relativePath} ---\n${fileContent}`);
281
+ totalSize += fileContent.length;
282
+ }
283
+ }
284
+ return content.join('\n');
285
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Speckit Generator - Convert AutoPilot workflow to Speckit format
3
+ *
4
+ * Speckit is a structured markdown format for development plans:
5
+ * - plan.md: Technical context, goals, architecture decisions
6
+ * - tasks.md: Step-by-step task list with T-x.y IDs, dependencies, validation
7
+ *
8
+ * Reference: https://github.com/devload/claude-planflow-skills
9
+ */
10
+ import { AutoPilotContext, ProjectType } from './types';
11
+ /**
12
+ * Speckit output structure
13
+ */
14
+ export interface SpeckitOutput {
15
+ featureName: string;
16
+ planMd: string;
17
+ tasksMd: string;
18
+ outputDir: string;
19
+ }
20
+ /**
21
+ * Task in Speckit format
22
+ */
23
+ export interface SpeckitTask {
24
+ id: string;
25
+ phase: number;
26
+ sequence: number;
27
+ title: string;
28
+ description: string;
29
+ files: string[];
30
+ dependencies: string[];
31
+ validation: string;
32
+ parallel: boolean;
33
+ }
34
+ /**
35
+ * Phase grouping
36
+ */
37
+ export interface SpeckitPhase {
38
+ number: number;
39
+ name: string;
40
+ tasks: SpeckitTask[];
41
+ }
42
+ /**
43
+ * Generate Speckit files from AutoPilot context
44
+ */
45
+ export declare function generateSpeckit(context: AutoPilotContext): SpeckitOutput;
46
+ /**
47
+ * Save Speckit files to disk
48
+ */
49
+ export declare function saveSpeckit(speckit: SpeckitOutput): {
50
+ planPath: string;
51
+ tasksPath: string;
52
+ };
53
+ /**
54
+ * Quick Speckit generation from just a prompt (without full analysis)
55
+ */
56
+ export declare function generateQuickSpeckit(prompt: string, context: {
57
+ projectType: ProjectType;
58
+ projectName: string;
59
+ workingDir: string;
60
+ }): SpeckitOutput;