@luckydraw/cumulus 0.5.1

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 (142) hide show
  1. package/README.md +148 -0
  2. package/dist/cli/cumulus.d.ts +3 -0
  3. package/dist/cli/cumulus.d.ts.map +1 -0
  4. package/dist/cli/cumulus.js +233 -0
  5. package/dist/cli/cumulus.js.map +1 -0
  6. package/dist/index.d.ts +33 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +43 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib/config.d.ts +86 -0
  11. package/dist/lib/config.d.ts.map +1 -0
  12. package/dist/lib/config.js +241 -0
  13. package/dist/lib/config.js.map +1 -0
  14. package/dist/lib/content-detector.d.ts +46 -0
  15. package/dist/lib/content-detector.d.ts.map +1 -0
  16. package/dist/lib/content-detector.js +359 -0
  17. package/dist/lib/content-detector.js.map +1 -0
  18. package/dist/lib/content-store.d.ts +255 -0
  19. package/dist/lib/content-store.d.ts.map +1 -0
  20. package/dist/lib/content-store.js +955 -0
  21. package/dist/lib/content-store.js.map +1 -0
  22. package/dist/lib/context-budget.d.ts +83 -0
  23. package/dist/lib/context-budget.d.ts.map +1 -0
  24. package/dist/lib/context-budget.js +101 -0
  25. package/dist/lib/context-budget.js.map +1 -0
  26. package/dist/lib/embeddings.d.ts +64 -0
  27. package/dist/lib/embeddings.d.ts.map +1 -0
  28. package/dist/lib/embeddings.js +176 -0
  29. package/dist/lib/embeddings.js.map +1 -0
  30. package/dist/lib/history.d.ts +120 -0
  31. package/dist/lib/history.d.ts.map +1 -0
  32. package/dist/lib/history.js +205 -0
  33. package/dist/lib/history.js.map +1 -0
  34. package/dist/lib/image-utils.d.ts +41 -0
  35. package/dist/lib/image-utils.d.ts.map +1 -0
  36. package/dist/lib/image-utils.js +288 -0
  37. package/dist/lib/image-utils.js.map +1 -0
  38. package/dist/lib/migrate.d.ts +35 -0
  39. package/dist/lib/migrate.d.ts.map +1 -0
  40. package/dist/lib/migrate.js +196 -0
  41. package/dist/lib/migrate.js.map +1 -0
  42. package/dist/lib/retriever.d.ts +56 -0
  43. package/dist/lib/retriever.d.ts.map +1 -0
  44. package/dist/lib/retriever.js +644 -0
  45. package/dist/lib/retriever.js.map +1 -0
  46. package/dist/lib/revert.d.ts +23 -0
  47. package/dist/lib/revert.d.ts.map +1 -0
  48. package/dist/lib/revert.js +75 -0
  49. package/dist/lib/revert.js.map +1 -0
  50. package/dist/lib/session.d.ts +65 -0
  51. package/dist/lib/session.d.ts.map +1 -0
  52. package/dist/lib/session.js +289 -0
  53. package/dist/lib/session.js.map +1 -0
  54. package/dist/lib/snapshots.d.ts +39 -0
  55. package/dist/lib/snapshots.d.ts.map +1 -0
  56. package/dist/lib/snapshots.js +99 -0
  57. package/dist/lib/snapshots.js.map +1 -0
  58. package/dist/lib/stream-processor.d.ts +149 -0
  59. package/dist/lib/stream-processor.d.ts.map +1 -0
  60. package/dist/lib/stream-processor.js +389 -0
  61. package/dist/lib/stream-processor.js.map +1 -0
  62. package/dist/lib/summarizer.d.ts +67 -0
  63. package/dist/lib/summarizer.d.ts.map +1 -0
  64. package/dist/lib/summarizer.js +213 -0
  65. package/dist/lib/summarizer.js.map +1 -0
  66. package/dist/mcp/index.d.ts +3 -0
  67. package/dist/mcp/index.d.ts.map +1 -0
  68. package/dist/mcp/index.js +16 -0
  69. package/dist/mcp/index.js.map +1 -0
  70. package/dist/mcp/proxy.d.ts +19 -0
  71. package/dist/mcp/proxy.d.ts.map +1 -0
  72. package/dist/mcp/proxy.js +120 -0
  73. package/dist/mcp/proxy.js.map +1 -0
  74. package/dist/mcp/server.d.ts +6 -0
  75. package/dist/mcp/server.d.ts.map +1 -0
  76. package/dist/mcp/server.js +29 -0
  77. package/dist/mcp/server.js.map +1 -0
  78. package/dist/mcp/shared-server.d.ts +21 -0
  79. package/dist/mcp/shared-server.d.ts.map +1 -0
  80. package/dist/mcp/shared-server.js +210 -0
  81. package/dist/mcp/shared-server.js.map +1 -0
  82. package/dist/mcp/tool-handler.d.ts +20 -0
  83. package/dist/mcp/tool-handler.d.ts.map +1 -0
  84. package/dist/mcp/tool-handler.js +1405 -0
  85. package/dist/mcp/tool-handler.js.map +1 -0
  86. package/dist/tui/components/App.d.ts +11 -0
  87. package/dist/tui/components/App.d.ts.map +1 -0
  88. package/dist/tui/components/App.js +607 -0
  89. package/dist/tui/components/App.js.map +1 -0
  90. package/dist/tui/components/DebugContextView.d.ts +13 -0
  91. package/dist/tui/components/DebugContextView.d.ts.map +1 -0
  92. package/dist/tui/components/DebugContextView.js +78 -0
  93. package/dist/tui/components/DebugContextView.js.map +1 -0
  94. package/dist/tui/components/IncludeMenu.d.ts +12 -0
  95. package/dist/tui/components/IncludeMenu.d.ts.map +1 -0
  96. package/dist/tui/components/IncludeMenu.js +127 -0
  97. package/dist/tui/components/IncludeMenu.js.map +1 -0
  98. package/dist/tui/components/InputArea.d.ts +27 -0
  99. package/dist/tui/components/InputArea.d.ts.map +1 -0
  100. package/dist/tui/components/InputArea.js +366 -0
  101. package/dist/tui/components/InputArea.js.map +1 -0
  102. package/dist/tui/components/MarkdownText.d.ts +38 -0
  103. package/dist/tui/components/MarkdownText.d.ts.map +1 -0
  104. package/dist/tui/components/MarkdownText.js +234 -0
  105. package/dist/tui/components/MarkdownText.js.map +1 -0
  106. package/dist/tui/components/MessageBubble.d.ts +11 -0
  107. package/dist/tui/components/MessageBubble.d.ts.map +1 -0
  108. package/dist/tui/components/MessageBubble.js +16 -0
  109. package/dist/tui/components/MessageBubble.js.map +1 -0
  110. package/dist/tui/components/MessageHistory.d.ts +11 -0
  111. package/dist/tui/components/MessageHistory.d.ts.map +1 -0
  112. package/dist/tui/components/MessageHistory.js +12 -0
  113. package/dist/tui/components/MessageHistory.js.map +1 -0
  114. package/dist/tui/components/RevertMenu.d.ts +17 -0
  115. package/dist/tui/components/RevertMenu.d.ts.map +1 -0
  116. package/dist/tui/components/RevertMenu.js +144 -0
  117. package/dist/tui/components/RevertMenu.js.map +1 -0
  118. package/dist/tui/components/StatusBar.d.ts +14 -0
  119. package/dist/tui/components/StatusBar.d.ts.map +1 -0
  120. package/dist/tui/components/StatusBar.js +13 -0
  121. package/dist/tui/components/StatusBar.js.map +1 -0
  122. package/dist/tui/components/StreamingResponse.d.ts +15 -0
  123. package/dist/tui/components/StreamingResponse.d.ts.map +1 -0
  124. package/dist/tui/components/StreamingResponse.js +52 -0
  125. package/dist/tui/components/StreamingResponse.js.map +1 -0
  126. package/dist/tui/hooks/useAppState.d.ts +147 -0
  127. package/dist/tui/hooks/useAppState.d.ts.map +1 -0
  128. package/dist/tui/hooks/useAppState.js +110 -0
  129. package/dist/tui/hooks/useAppState.js.map +1 -0
  130. package/dist/tui/hooks/useClaudeProcess.d.ts +19 -0
  131. package/dist/tui/hooks/useClaudeProcess.d.ts.map +1 -0
  132. package/dist/tui/hooks/useClaudeProcess.js +185 -0
  133. package/dist/tui/hooks/useClaudeProcess.js.map +1 -0
  134. package/dist/tui/index.d.ts +10 -0
  135. package/dist/tui/index.d.ts.map +1 -0
  136. package/dist/tui/index.js +11 -0
  137. package/dist/tui/index.js.map +1 -0
  138. package/dist/tui/utils/streamParser.d.ts +31 -0
  139. package/dist/tui/utils/streamParser.d.ts.map +1 -0
  140. package/dist/tui/utils/streamParser.js +63 -0
  141. package/dist/tui/utils/streamParser.js.map +1 -0
  142. package/package.json +94 -0
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Configuration loading and always-include file management.
3
+ *
4
+ * Handles global and per-thread configuration, including reading
5
+ * files that should always be included in the context window.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as os from 'os';
9
+ import * as path from 'path';
10
+ import { estimateTokens } from './context-budget.js';
11
+ const CUMULUS_DIR = path.join(os.homedir(), '.cumulus');
12
+ const THREADS_DIR = path.join(CUMULUS_DIR, 'threads');
13
+ /**
14
+ * Load the global configuration from ~/.cumulus/config.json
15
+ */
16
+ export async function loadGlobalConfig() {
17
+ const configPath = path.join(CUMULUS_DIR, 'config.json');
18
+ return loadConfigFile(configPath);
19
+ }
20
+ /**
21
+ * Load thread-specific configuration from ~/.cumulus/threads/{thread}.config.json
22
+ */
23
+ export async function loadThreadConfig(threadName) {
24
+ const configPath = path.join(THREADS_DIR, `${threadName}.config.json`);
25
+ return loadConfigFile(configPath);
26
+ }
27
+ /**
28
+ * Load a configuration file, returning empty config if not found or invalid.
29
+ */
30
+ async function loadConfigFile(configPath) {
31
+ try {
32
+ if (!fs.existsSync(configPath)) {
33
+ return {};
34
+ }
35
+ const content = fs.readFileSync(configPath, 'utf-8');
36
+ const config = JSON.parse(content);
37
+ // Validate structure
38
+ if (typeof config !== 'object' || config === null) {
39
+ console.warn(`[Config] Invalid config format in ${configPath}, using empty config`);
40
+ return {};
41
+ }
42
+ // Validate alwaysInclude if present
43
+ if (config.alwaysInclude !== undefined) {
44
+ if (!Array.isArray(config.alwaysInclude)) {
45
+ console.warn(`[Config] alwaysInclude must be an array in ${configPath}`);
46
+ return {};
47
+ }
48
+ for (const item of config.alwaysInclude) {
49
+ if (typeof item !== 'string') {
50
+ console.warn(`[Config] alwaysInclude items must be strings in ${configPath}`);
51
+ return {};
52
+ }
53
+ }
54
+ }
55
+ // Validate projectDir if present
56
+ if (config.projectDir !== undefined) {
57
+ if (typeof config.projectDir !== 'string') {
58
+ console.warn(`[Config] projectDir must be a string in ${configPath}`);
59
+ delete config.projectDir;
60
+ }
61
+ }
62
+ return config;
63
+ }
64
+ catch (err) {
65
+ if (err instanceof SyntaxError) {
66
+ console.warn(`[Config] Failed to parse ${configPath}: ${err.message}`);
67
+ }
68
+ return {};
69
+ }
70
+ }
71
+ /**
72
+ * Merge global and thread configs. Thread config adds to global (doesn't override).
73
+ */
74
+ export function mergeConfigs(global, thread) {
75
+ const merged = {};
76
+ // Merge alwaysInclude arrays (deduplicated, thread additions come after global)
77
+ const globalIncludes = global.alwaysInclude ?? [];
78
+ const threadIncludes = thread.alwaysInclude ?? [];
79
+ if (globalIncludes.length > 0 || threadIncludes.length > 0) {
80
+ const seen = new Set();
81
+ merged.alwaysInclude = [];
82
+ for (const item of [...globalIncludes, ...threadIncludes]) {
83
+ if (!seen.has(item)) {
84
+ seen.add(item);
85
+ merged.alwaysInclude.push(item);
86
+ }
87
+ }
88
+ }
89
+ // projectDir: thread overrides global (simple override, not array merge)
90
+ const projectDir = thread.projectDir ?? global.projectDir;
91
+ if (projectDir) {
92
+ merged.projectDir = projectDir;
93
+ }
94
+ return merged;
95
+ }
96
+ /**
97
+ * Resolve a file path that may use ~ or be relative.
98
+ * @param filePath - The path from config (may use ~, be relative, or absolute)
99
+ * @param cwd - Current working directory for relative paths
100
+ */
101
+ export function resolveFilePath(filePath, cwd) {
102
+ // Handle ~ for home directory
103
+ if (filePath.startsWith('~/')) {
104
+ return path.join(os.homedir(), filePath.slice(2));
105
+ }
106
+ if (filePath === '~') {
107
+ return os.homedir();
108
+ }
109
+ // Handle absolute paths
110
+ if (path.isAbsolute(filePath)) {
111
+ return filePath;
112
+ }
113
+ // Relative path - resolve from cwd
114
+ return path.resolve(cwd, filePath);
115
+ }
116
+ /**
117
+ * Read and format all always-include files from config.
118
+ * @param config - Merged configuration
119
+ * @param cwd - Current working directory for relative paths
120
+ */
121
+ export async function readAlwaysIncludeFiles(config, cwd) {
122
+ const files = [];
123
+ let totalTokens = 0;
124
+ const paths = config.alwaysInclude ?? [];
125
+ for (const filePath of paths) {
126
+ const resolvedPath = resolveFilePath(filePath, cwd);
127
+ const fileInfo = {
128
+ path: filePath,
129
+ resolvedPath,
130
+ content: '',
131
+ tokens: 0,
132
+ truncated: false,
133
+ };
134
+ try {
135
+ if (!fs.existsSync(resolvedPath)) {
136
+ fileInfo.error = 'File not found';
137
+ console.warn(`[Config] Always-include file not found: ${resolvedPath}`);
138
+ }
139
+ else {
140
+ const stat = fs.statSync(resolvedPath);
141
+ if (stat.isDirectory()) {
142
+ fileInfo.error = 'Path is a directory';
143
+ console.warn(`[Config] Always-include path is a directory: ${resolvedPath}`);
144
+ }
145
+ else {
146
+ const content = fs.readFileSync(resolvedPath, 'utf-8');
147
+ fileInfo.content = content;
148
+ fileInfo.tokens = estimateTokens(content);
149
+ totalTokens += fileInfo.tokens;
150
+ }
151
+ }
152
+ }
153
+ catch (err) {
154
+ const errorMsg = err instanceof Error ? err.message : 'Unknown error';
155
+ fileInfo.error = errorMsg;
156
+ console.warn(`[Config] Failed to read always-include file ${resolvedPath}: ${errorMsg}`);
157
+ }
158
+ files.push(fileInfo);
159
+ }
160
+ // Format the context string
161
+ const formattedContext = formatAlwaysIncludeContext(files);
162
+ return {
163
+ files,
164
+ totalTokens,
165
+ formattedContext,
166
+ };
167
+ }
168
+ /**
169
+ * Format always-include files into a context string for the system prompt.
170
+ */
171
+ function formatAlwaysIncludeContext(files) {
172
+ const validFiles = files.filter(f => !f.error && f.content);
173
+ if (validFiles.length === 0) {
174
+ return '';
175
+ }
176
+ const lines = ['\nALWAYS-INCLUDED FILES (project context):', '---'];
177
+ for (const file of validFiles) {
178
+ lines.push(`[${file.path}]`);
179
+ lines.push(file.content);
180
+ lines.push('');
181
+ }
182
+ lines.push('---\n');
183
+ return lines.join('\n');
184
+ }
185
+ /**
186
+ * Save configuration to a file.
187
+ */
188
+ export async function saveConfig(config, threadName) {
189
+ const configPath = threadName
190
+ ? path.join(THREADS_DIR, `${threadName}.config.json`)
191
+ : path.join(CUMULUS_DIR, 'config.json');
192
+ // Ensure directory exists
193
+ const dir = path.dirname(configPath);
194
+ if (!fs.existsSync(dir)) {
195
+ fs.mkdirSync(dir, { recursive: true });
196
+ }
197
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
198
+ }
199
+ /**
200
+ * Add a file to the always-include list.
201
+ * @param filePath - Path to add
202
+ * @param threadName - Thread name for thread-specific config, undefined for global
203
+ */
204
+ export async function addAlwaysIncludeFile(filePath, threadName) {
205
+ const config = threadName ? await loadThreadConfig(threadName) : await loadGlobalConfig();
206
+ const alwaysInclude = config.alwaysInclude ?? [];
207
+ if (!alwaysInclude.includes(filePath)) {
208
+ alwaysInclude.push(filePath);
209
+ config.alwaysInclude = alwaysInclude;
210
+ await saveConfig(config, threadName);
211
+ }
212
+ }
213
+ /**
214
+ * Remove a file from the always-include list.
215
+ * @param filePath - Path to remove
216
+ * @param threadName - Thread name for thread-specific config, undefined for global
217
+ */
218
+ export async function removeAlwaysIncludeFile(filePath, threadName) {
219
+ const config = threadName ? await loadThreadConfig(threadName) : await loadGlobalConfig();
220
+ const alwaysInclude = config.alwaysInclude ?? [];
221
+ const index = alwaysInclude.indexOf(filePath);
222
+ if (index !== -1) {
223
+ alwaysInclude.splice(index, 1);
224
+ config.alwaysInclude = alwaysInclude.length > 0 ? alwaysInclude : undefined;
225
+ await saveConfig(config, threadName);
226
+ }
227
+ }
228
+ /**
229
+ * List all always-include files from config (without reading them).
230
+ */
231
+ export async function listAlwaysIncludeFiles(threadName) {
232
+ const globalConfig = await loadGlobalConfig();
233
+ const threadConfig = threadName ? await loadThreadConfig(threadName) : {};
234
+ const mergedConfig = mergeConfigs(globalConfig, threadConfig);
235
+ return {
236
+ global: globalConfig.alwaysInclude ?? [],
237
+ thread: threadConfig.alwaysInclude ?? [],
238
+ merged: mergedConfig.alwaysInclude ?? [],
239
+ };
240
+ }
241
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { ContentType } from './content-store.js';
2
+ /**
3
+ * Detailed content analysis result.
4
+ */
5
+ export interface ContentAnalysis {
6
+ /** Detected content type */
7
+ contentType: ContentType;
8
+ /** Confidence score (0-1) */
9
+ confidence: number;
10
+ /** Detected language/format if applicable */
11
+ language?: string;
12
+ /** Key features detected */
13
+ features: string[];
14
+ }
15
+ /**
16
+ * Options for summary generation.
17
+ */
18
+ export interface SummaryOptions {
19
+ /** Maximum summary length in characters */
20
+ maxLength?: number;
21
+ /** Whether to use LLM for summarization */
22
+ useLLM?: boolean;
23
+ /** Model to use for LLM summarization */
24
+ model?: 'haiku' | 'sonnet';
25
+ }
26
+ /**
27
+ * Analyze content to determine its type and characteristics.
28
+ */
29
+ export declare function analyzeContent(content: string): ContentAnalysis;
30
+ /**
31
+ * Generate a heuristic summary without LLM.
32
+ */
33
+ export declare function generateHeuristicSummary(content: string, contentType: ContentType, maxLength?: number): string;
34
+ /**
35
+ * Generate summary using LLM (via claude CLI).
36
+ */
37
+ export declare function generateLLMSummary(content: string, contentType: ContentType, options?: SummaryOptions): Promise<string>;
38
+ /**
39
+ * Generate summary for content, optionally using LLM.
40
+ */
41
+ export declare function generateSummary(content: string, contentType: ContentType, options?: SummaryOptions): Promise<string>;
42
+ /**
43
+ * Detect content type from content string.
44
+ */
45
+ export declare function detectContentType(content: string): ContentType;
46
+ //# sourceMappingURL=content-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-detector.d.ts","sourceRoot":"","sources":["../../src/lib/content-detector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAgJ/D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,WAAW,EACxB,SAAS,GAAE,MAAY,GACtB,MAAM,CA8JR;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAyDjB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAE9D"}
@@ -0,0 +1,359 @@
1
+ import { spawn } from 'child_process';
2
+ /**
3
+ * Analyze content to determine its type and characteristics.
4
+ */
5
+ export function analyzeContent(content) {
6
+ const lines = content.split('\n');
7
+ const sample = content.slice(0, 5000);
8
+ const features = [];
9
+ let language;
10
+ // Score different content types
11
+ const scores = {
12
+ code: 0,
13
+ logs: 0,
14
+ json: 0,
15
+ prose: 0,
16
+ mixed: 0,
17
+ };
18
+ // JSON detection
19
+ if (sample.trim().startsWith('{') || sample.trim().startsWith('[')) {
20
+ try {
21
+ JSON.parse(content);
22
+ scores.json = 10;
23
+ features.push('valid_json');
24
+ }
25
+ catch {
26
+ scores.json = 2; // Looks like JSON but invalid
27
+ }
28
+ }
29
+ // Code detection patterns
30
+ const codePatterns = [
31
+ // JavaScript/TypeScript
32
+ { pattern: /^import\s+.*from\s+['"]/, weight: 3, lang: 'javascript' },
33
+ {
34
+ pattern: /^export\s+(default\s+)?(?:function|class|const|let|interface|type)/,
35
+ weight: 3,
36
+ lang: 'javascript',
37
+ },
38
+ { pattern: /^(?:const|let|var)\s+\w+\s*[=:]/, weight: 2, lang: 'javascript' },
39
+ { pattern: /=>\s*[{(]/, weight: 2, lang: 'javascript' },
40
+ // Python
41
+ { pattern: /^def\s+\w+\s*\(/, weight: 3, lang: 'python' },
42
+ { pattern: /^class\s+\w+.*:$/, weight: 3, lang: 'python' },
43
+ { pattern: /^import\s+\w+$/, weight: 2, lang: 'python' },
44
+ { pattern: /^from\s+\w+\s+import/, weight: 3, lang: 'python' },
45
+ // Rust
46
+ { pattern: /^fn\s+\w+/, weight: 3, lang: 'rust' },
47
+ { pattern: /^impl\s+/, weight: 3, lang: 'rust' },
48
+ { pattern: /^use\s+\w+::/, weight: 2, lang: 'rust' },
49
+ // Go
50
+ { pattern: /^func\s+(\(\w+\s+\*?\w+\)\s+)?\w+/, weight: 3, lang: 'go' },
51
+ { pattern: /^package\s+\w+/, weight: 3, lang: 'go' },
52
+ // General
53
+ { pattern: /^\s*(?:if|for|while|switch)\s*\(/, weight: 1 },
54
+ { pattern: /^\s*(?:public|private|protected)\s+/, weight: 2, lang: 'java' },
55
+ { pattern: /^\s*#include\s*</, weight: 3, lang: 'c' },
56
+ { pattern: /^\s*@\w+/, weight: 1 }, // Decorators/annotations
57
+ ];
58
+ const langCounts = {};
59
+ for (const { pattern, weight, lang } of codePatterns) {
60
+ const matches = sample.match(new RegExp(pattern.source, 'gm'));
61
+ if (matches) {
62
+ scores.code += weight * Math.min(matches.length, 5);
63
+ if (lang) {
64
+ langCounts[lang] = (langCounts[lang] ?? 0) + weight * matches.length;
65
+ }
66
+ }
67
+ }
68
+ // Detect most likely language
69
+ if (Object.keys(langCounts).length > 0) {
70
+ language = Object.entries(langCounts).sort((a, b) => b[1] - a[1])[0]?.[0];
71
+ features.push(`language_${language}`);
72
+ }
73
+ // Log detection patterns
74
+ const logPatterns = [
75
+ { pattern: /^\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}/, weight: 3, feature: 'timestamps' },
76
+ { pattern: /^\[\d{4}-\d{2}-\d{2}/, weight: 3, feature: 'bracketed_timestamps' },
77
+ { pattern: /^\[(?:DEBUG|INFO|WARN|ERROR|FATAL)\]/, weight: 3, feature: 'log_levels' },
78
+ { pattern: /^(?:DEBUG|INFO|WARN|ERROR|FATAL)[:\s]/, weight: 2, feature: 'log_levels' },
79
+ { pattern: /^\d+:\d+:\d+\.\d+/, weight: 2, feature: 'timestamps' },
80
+ {
81
+ pattern: /^[A-Za-z]{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}/,
82
+ weight: 2,
83
+ feature: 'syslog_timestamps',
84
+ },
85
+ { pattern: /^\s*at\s+[\w.$]+\s*\(/, weight: 2, feature: 'stack_traces' },
86
+ { pattern: /^Traceback \(most recent call last\):/, weight: 3, feature: 'python_traceback' },
87
+ ];
88
+ for (const { pattern, weight, feature } of logPatterns) {
89
+ const matches = sample.match(new RegExp(pattern.source, 'gm'));
90
+ if (matches) {
91
+ scores.logs += weight * Math.min(matches.length, 10);
92
+ if (feature && !features.includes(feature)) {
93
+ features.push(feature);
94
+ }
95
+ }
96
+ }
97
+ // Prose detection
98
+ const avgLineLength = content.length / Math.max(lines.length, 1);
99
+ const nonEmptyLines = lines.filter(l => l.trim()).length;
100
+ const avgWordsPerLine = content.split(/\s+/).length / Math.max(nonEmptyLines, 1);
101
+ // Prose characteristics: long lines, sentence structure, paragraphs
102
+ if (avgLineLength > 60)
103
+ scores.prose += 2;
104
+ if (avgWordsPerLine > 10)
105
+ scores.prose += 2;
106
+ if (/[.!?]\s+[A-Z]/.test(sample))
107
+ scores.prose += 3; // Sentence boundaries
108
+ if (/\n\n/.test(content))
109
+ scores.prose += 1; // Paragraphs
110
+ if (!/[{}()[\];]/.test(sample.slice(0, 500)))
111
+ scores.prose += 2; // No code punctuation
112
+ // Determine winner
113
+ const maxScore = Math.max(scores.code, scores.logs, scores.json, scores.prose);
114
+ let contentType;
115
+ let confidence;
116
+ if (maxScore === 0) {
117
+ contentType = 'mixed';
118
+ confidence = 0.3;
119
+ }
120
+ else if (scores.json === maxScore && scores.json >= 5) {
121
+ contentType = 'json';
122
+ confidence = Math.min(0.95, 0.5 + scores.json * 0.05);
123
+ features.push('structured_data');
124
+ }
125
+ else if (scores.code === maxScore && scores.code >= 4) {
126
+ contentType = 'code';
127
+ confidence = Math.min(0.9, 0.4 + scores.code * 0.03);
128
+ }
129
+ else if (scores.logs === maxScore && scores.logs >= 4) {
130
+ contentType = 'logs';
131
+ confidence = Math.min(0.9, 0.4 + scores.logs * 0.03);
132
+ }
133
+ else if (scores.prose === maxScore && scores.prose >= 4) {
134
+ contentType = 'prose';
135
+ confidence = Math.min(0.85, 0.4 + scores.prose * 0.05);
136
+ }
137
+ else {
138
+ // Mixed or unclear
139
+ contentType = 'mixed';
140
+ confidence = 0.5;
141
+ }
142
+ return {
143
+ contentType,
144
+ confidence,
145
+ language,
146
+ features,
147
+ };
148
+ }
149
+ /**
150
+ * Generate a heuristic summary without LLM.
151
+ */
152
+ export function generateHeuristicSummary(content, contentType, maxLength = 300) {
153
+ const lines = content.split('\n').filter(l => l.trim());
154
+ const lineCount = lines.length;
155
+ const charCount = content.length;
156
+ let summary = `${lineCount} lines, ${Math.round(charCount / 1024)}KB. `;
157
+ switch (contentType) {
158
+ case 'code': {
159
+ const items = [];
160
+ // Extract import sources (module paths)
161
+ const importSourceMatches = content.match(/(?:from\s+['"]([^'"]+)['"]|require\(\s*['"]([^'"]+)['"]|use\s+([\w:]+))/gm);
162
+ if (importSourceMatches && importSourceMatches.length > 0) {
163
+ const sources = importSourceMatches
164
+ .map(m => {
165
+ const fromMatch = m.match(/from\s+['"]([^'"]+)['"]/);
166
+ if (fromMatch)
167
+ return fromMatch[1];
168
+ const reqMatch = m.match(/require\(\s*['"]([^'"]+)['"]/);
169
+ if (reqMatch)
170
+ return reqMatch[1];
171
+ const useMatch = m.match(/use\s+([\w:]+)/);
172
+ if (useMatch)
173
+ return useMatch[1];
174
+ return null;
175
+ })
176
+ .filter(Boolean)
177
+ .slice(0, 5);
178
+ if (sources.length > 0) {
179
+ items.push(`imports: ${sources.join(', ')}`);
180
+ }
181
+ }
182
+ else {
183
+ // Fallback: count import lines
184
+ const importLines = content.match(/^(?:import|from|require|use)\s+.+$/gm);
185
+ if (importLines && importLines.length > 0) {
186
+ items.push(`${importLines.length} imports`);
187
+ }
188
+ }
189
+ // Extract exports/public API
190
+ const exportMatches = content.match(/^export\s+(?:default\s+)?(?:function|class|const|interface|type)\s+(\w+)/gm);
191
+ if (exportMatches) {
192
+ const names = exportMatches
193
+ .map(m => m.match(/(\w+)$/)?.[1])
194
+ .filter(Boolean)
195
+ .slice(0, 5);
196
+ if (names.length > 0) {
197
+ items.push(`exports: ${names.join(', ')}`);
198
+ }
199
+ }
200
+ // Extract function signatures with parameters
201
+ const funcSigMatches = content.match(/(?:(?:export\s+)?(?:async\s+)?function\s+\w+\s*\([^)]*\)|(?:async\s+)?def\s+\w+\s*\([^)]*\)|(?:pub\s+)?(?:async\s+)?fn\s+\w+\s*\([^)]*\)|func\s+(?:\([^)]*\)\s*)?\w+\s*\([^)]*\))/gm);
202
+ if (funcSigMatches) {
203
+ const sigs = funcSigMatches.map(m => m.replace(/\s+/g, ' ').trim()).slice(0, 5);
204
+ if (sigs.length > 0 && !items.some(i => i.includes('exports'))) {
205
+ items.push(`functions: ${sigs.join(', ')}`);
206
+ }
207
+ }
208
+ // Extract class hierarchies (extends/implements)
209
+ const classHierarchyMatches = content.match(/(?:class|struct)\s+\w+(?:\s+(?:extends|implements)\s+[\w,\s]+)?/gm);
210
+ if (classHierarchyMatches) {
211
+ const classes = classHierarchyMatches.map(m => m.replace(/\s+/g, ' ').trim()).slice(0, 3);
212
+ if (classes.length > 0) {
213
+ items.push(`classes: ${classes.join(', ')}`);
214
+ }
215
+ }
216
+ // Extract type/interface names
217
+ const typeMatches = content.match(/(?:interface|type|enum)\s+(\w+)/g);
218
+ if (typeMatches) {
219
+ const names = typeMatches
220
+ .map(m => m.split(/\s+/)[1])
221
+ .filter(Boolean)
222
+ .slice(0, 5);
223
+ if (names.length > 0) {
224
+ items.push(`types: ${names.join(', ')}`);
225
+ }
226
+ }
227
+ // Extract UPPER_CASE constants
228
+ const constMatches = content.match(/(?:const|let|var)\s+([A-Z][A-Z0-9_]{2,})\s*=/gm);
229
+ if (constMatches) {
230
+ const names = constMatches
231
+ .map(m => m.match(/([A-Z][A-Z0-9_]{2,})/)?.[1])
232
+ .filter(Boolean)
233
+ .slice(0, 5);
234
+ if (names.length > 0) {
235
+ items.push(`constants: ${names.join(', ')}`);
236
+ }
237
+ }
238
+ summary += items.length > 0 ? items.join('; ') : 'Code file';
239
+ break;
240
+ }
241
+ case 'logs': {
242
+ const items = [];
243
+ // Count log levels
244
+ const errorCount = (content.match(/\b(?:ERROR|FATAL|Exception|Error:)/gi) || []).length;
245
+ const warnCount = (content.match(/\bWARN(?:ING)?/gi) || []).length;
246
+ const infoCount = (content.match(/\bINFO/gi) || []).length;
247
+ if (errorCount > 0)
248
+ items.push(`${errorCount} errors`);
249
+ if (warnCount > 0)
250
+ items.push(`${warnCount} warnings`);
251
+ if (infoCount > 0)
252
+ items.push(`${infoCount} info`);
253
+ // Try to extract time range
254
+ const timestamps = content.match(/\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}/g);
255
+ if (timestamps && timestamps.length >= 2) {
256
+ items.push(`from ${timestamps[0]} to ${timestamps[timestamps.length - 1]}`);
257
+ }
258
+ summary += items.length > 0 ? items.join(', ') : 'Log output';
259
+ break;
260
+ }
261
+ case 'json': {
262
+ try {
263
+ const parsed = JSON.parse(content);
264
+ if (Array.isArray(parsed)) {
265
+ summary += `Array with ${parsed.length} items`;
266
+ if (parsed.length > 0 && typeof parsed[0] === 'object') {
267
+ const keys = Object.keys(parsed[0]).slice(0, 5);
268
+ summary += `. Item keys: ${keys.join(', ')}`;
269
+ }
270
+ }
271
+ else if (typeof parsed === 'object' && parsed !== null) {
272
+ const keys = Object.keys(parsed);
273
+ summary += `Object with ${keys.length} keys: ${keys.slice(0, 7).join(', ')}`;
274
+ if (keys.length > 7)
275
+ summary += '...';
276
+ }
277
+ }
278
+ catch {
279
+ summary += 'JSON data';
280
+ }
281
+ break;
282
+ }
283
+ case 'prose': {
284
+ // Take first paragraph or sentences
285
+ const firstPara = content.split(/\n\n/)[0] ?? '';
286
+ const preview = firstPara.slice(0, maxLength - summary.length - 10);
287
+ summary += preview + (firstPara.length > preview.length ? '...' : '');
288
+ break;
289
+ }
290
+ default:
291
+ summary += 'Mixed content';
292
+ }
293
+ return summary.slice(0, maxLength);
294
+ }
295
+ /**
296
+ * Generate summary using LLM (via claude CLI).
297
+ */
298
+ export async function generateLLMSummary(content, contentType, options = {}) {
299
+ const maxLength = options.maxLength ?? 300;
300
+ const model = options.model ?? 'haiku';
301
+ // Truncate content if too long
302
+ const truncatedContent = content.slice(0, 15000);
303
+ const prompt = `Summarize this ${contentType} content in ${maxLength} characters or less. Focus on:
304
+ - What it is/does
305
+ - Key elements (functions, errors, data structure)
306
+ - Important details
307
+
308
+ Content:
309
+ ${truncatedContent}
310
+
311
+ Summary (be concise):`;
312
+ return new Promise(resolve => {
313
+ const args = ['--print', '--model', model, prompt];
314
+ const cleanEnv = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('CLAUDE') && key !== 'CLAUDECODE'));
315
+ const claude = spawn('claude', args, {
316
+ stdio: ['ignore', 'pipe', 'pipe'],
317
+ env: cleanEnv,
318
+ });
319
+ let stdout = '';
320
+ const timeout = setTimeout(() => {
321
+ claude.kill();
322
+ // Fall back to heuristic summary on timeout
323
+ resolve(generateHeuristicSummary(content, contentType, maxLength));
324
+ }, 30000);
325
+ claude.stdout.on('data', (data) => {
326
+ stdout += data.toString();
327
+ });
328
+ claude.on('close', code => {
329
+ clearTimeout(timeout);
330
+ if (code === 0 && stdout.trim()) {
331
+ resolve(stdout.trim().slice(0, maxLength));
332
+ }
333
+ else {
334
+ // Fall back to heuristic summary
335
+ resolve(generateHeuristicSummary(content, contentType, maxLength));
336
+ }
337
+ });
338
+ claude.on('error', () => {
339
+ clearTimeout(timeout);
340
+ resolve(generateHeuristicSummary(content, contentType, maxLength));
341
+ });
342
+ });
343
+ }
344
+ /**
345
+ * Generate summary for content, optionally using LLM.
346
+ */
347
+ export async function generateSummary(content, contentType, options = {}) {
348
+ if (options.useLLM) {
349
+ return generateLLMSummary(content, contentType, options);
350
+ }
351
+ return generateHeuristicSummary(content, contentType, options.maxLength);
352
+ }
353
+ /**
354
+ * Detect content type from content string.
355
+ */
356
+ export function detectContentType(content) {
357
+ return analyzeContent(content).contentType;
358
+ }
359
+ //# sourceMappingURL=content-detector.js.map