autonomous-flow-daemon 1.0.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.ko.md +142 -125
  3. package/README.md +119 -134
  4. package/package.json +11 -5
  5. package/src/adapters/index.ts +247 -35
  6. package/src/cli.ts +79 -1
  7. package/src/commands/benchmark.ts +187 -0
  8. package/src/commands/diagnose.ts +56 -14
  9. package/src/commands/doctor.ts +243 -0
  10. package/src/commands/evolution.ts +107 -0
  11. package/src/commands/fix.ts +22 -2
  12. package/src/commands/hooks.ts +136 -0
  13. package/src/commands/lang.ts +41 -0
  14. package/src/commands/mcp.ts +129 -0
  15. package/src/commands/restart.ts +14 -0
  16. package/src/commands/score.ts +192 -64
  17. package/src/commands/start.ts +137 -37
  18. package/src/commands/stats.ts +103 -0
  19. package/src/commands/status.ts +157 -0
  20. package/src/commands/stop.ts +42 -9
  21. package/src/commands/sync.ts +253 -20
  22. package/src/commands/vaccine.ts +177 -0
  23. package/src/constants.ts +26 -1
  24. package/src/core/boast.ts +280 -0
  25. package/src/core/config.ts +49 -0
  26. package/src/core/db.ts +74 -3
  27. package/src/core/discovery.ts +65 -0
  28. package/src/core/evolution.ts +215 -0
  29. package/src/core/hologram/engine.ts +71 -0
  30. package/src/core/hologram/fallback.ts +11 -0
  31. package/src/core/hologram/incremental.ts +227 -0
  32. package/src/core/hologram/py-extractor.ts +132 -0
  33. package/src/core/hologram/ts-extractor.ts +320 -0
  34. package/src/core/hologram/types.ts +25 -0
  35. package/src/core/hologram.ts +64 -236
  36. package/src/core/hook-manager.ts +259 -0
  37. package/src/core/i18n/messages.ts +309 -0
  38. package/src/core/immune.ts +8 -123
  39. package/src/core/locale.ts +88 -0
  40. package/src/core/log-rotate.ts +33 -0
  41. package/src/core/log-utils.ts +38 -0
  42. package/src/core/lru-map.ts +61 -0
  43. package/src/core/notify.ts +53 -14
  44. package/src/core/rule-engine.ts +287 -0
  45. package/src/core/semantic-diff.ts +432 -0
  46. package/src/core/telemetry.ts +94 -0
  47. package/src/core/vaccine-registry.ts +212 -0
  48. package/src/core/workspace.ts +28 -0
  49. package/src/core/yaml-minimal.ts +176 -0
  50. package/src/daemon/client.ts +34 -6
  51. package/src/daemon/event-batcher.ts +108 -0
  52. package/src/daemon/guards.ts +13 -0
  53. package/src/daemon/http-routes.ts +293 -0
  54. package/src/daemon/mcp-handler.ts +270 -0
  55. package/src/daemon/server.ts +492 -273
  56. package/src/daemon/types.ts +100 -0
  57. package/src/daemon/workspace-map.ts +92 -0
  58. package/src/platform.ts +60 -0
  59. package/src/version.ts +15 -0
@@ -0,0 +1,320 @@
1
+ import type { Node, Tree } from "web-tree-sitter";
2
+ import type { LanguageExtractor, HologramOptions } from "./types";
3
+ import { readFileSync } from "fs";
4
+
5
+ /** Extract imported symbols from a context file using regex (L1 filtering) */
6
+ function extractImportedSymbols(contextSource: string, targetPath: string): Set<string> | "all" {
7
+ const symbols = new Set<string>();
8
+ const targetBase = targetPath.replace(/\.[tj]sx?$/, "").replace(/\\/g, "/");
9
+ const targetName = targetBase.split("/").pop() ?? targetBase;
10
+
11
+ function matchesTarget(from: string): boolean {
12
+ const normalized = from.replace(/\.[tj]sx?$/, "").replace(/\\/g, "/");
13
+ return normalized.endsWith(targetName) || normalized.endsWith(targetBase);
14
+ }
15
+
16
+ // Namespace import
17
+ const nsRe = /import\s+\*\s+as\s+\w+\s+from\s+["']([^"']+)["']/g;
18
+ for (const m of contextSource.matchAll(nsRe)) {
19
+ if (matchesTarget(m[1])) return "all";
20
+ }
21
+
22
+ // Named imports
23
+ const namedRe = /import\s+\{([^}]+)\}\s+from\s+["']([^"']+)["']/g;
24
+ for (const m of contextSource.matchAll(namedRe)) {
25
+ if (matchesTarget(m[2])) {
26
+ m[1].split(",").forEach(s => {
27
+ const name = s.trim().split(/\s+as\s+/)[0].trim();
28
+ if (name) symbols.add(name);
29
+ });
30
+ }
31
+ }
32
+
33
+ // Default import
34
+ const defaultRe = /import\s+(\w+)\s+from\s+["']([^"']+)["']/g;
35
+ for (const m of contextSource.matchAll(defaultRe)) {
36
+ if (matchesTarget(m[2])) symbols.add("default");
37
+ }
38
+
39
+ // Combined: import X, { A, B } from "./target"
40
+ const combinedRe = /import\s+(\w+)\s*,\s*\{([^}]+)\}\s+from\s+["']([^"']+)["']/g;
41
+ for (const m of contextSource.matchAll(combinedRe)) {
42
+ if (matchesTarget(m[3])) {
43
+ symbols.add("default");
44
+ m[2].split(",").forEach(s => {
45
+ const name = s.trim().split(/\s+as\s+/)[0].trim();
46
+ if (name) symbols.add(name);
47
+ });
48
+ }
49
+ }
50
+
51
+ return symbols;
52
+ }
53
+
54
+ /** Get the exported name from a top-level declaration node */
55
+ function getExportedName(node: Node): string | null {
56
+ // Check if wrapped in export_statement
57
+ const parent = node.parent;
58
+ const isExported = parent?.type === "export_statement" || node.type === "export_statement";
59
+ if (!isExported && parent?.type !== "program") return null;
60
+ if (!isExported) return null;
61
+
62
+ const decl = node.type === "export_statement"
63
+ ? node.namedChildren.find(c =>
64
+ c.type === "function_declaration" ||
65
+ c.type === "class_declaration" ||
66
+ c.type === "interface_declaration" ||
67
+ c.type === "type_alias_declaration" ||
68
+ c.type === "enum_declaration" ||
69
+ c.type === "lexical_declaration")
70
+ : node;
71
+
72
+ if (!decl) return null;
73
+
74
+ const nameNode = decl.childForFieldName("name");
75
+ if (nameNode) return nameNode.text;
76
+
77
+ // Variable declarations
78
+ if (decl.type === "lexical_declaration") {
79
+ const declarator = decl.namedChildren.find(c => c.type === "variable_declarator");
80
+ return declarator?.childForFieldName("name")?.text ?? null;
81
+ }
82
+
83
+ return null;
84
+ }
85
+
86
+ /** Stub a node's body, keeping only the signature */
87
+ function stubBody(node: Node, source: string): string {
88
+ const body = node.childForFieldName("body");
89
+ if (!body) return collapseWhitespace(node.text);
90
+ return collapseWhitespace(source.slice(node.startIndex, body.startIndex).trimEnd()) + " {…}";
91
+ }
92
+
93
+ /** Extract function signature */
94
+ function extractFunction(node: Node, source: string): string {
95
+ return stubBody(node, source);
96
+ }
97
+
98
+ /** Extract class with member signatures */
99
+ function extractClass(node: Node, source: string): string {
100
+ const nameNode = node.childForFieldName("name");
101
+ const name = nameNode?.text ?? "Anonymous";
102
+ const body = node.childForFieldName("body");
103
+
104
+ // Heritage (extends/implements)
105
+ let heritage = "";
106
+ const heritageNodes = node.children.filter(c =>
107
+ c.type === "extends_clause" || c.type === "implements_clause" ||
108
+ c.type === "class_heritage");
109
+ if (heritageNodes.length > 0) {
110
+ heritage = " " + heritageNodes.map(h => collapseWhitespace(h.text)).join(" ");
111
+ }
112
+
113
+ // Prefix (export, abstract, etc.)
114
+ const prefix = collapseWhitespace(
115
+ source.slice(node.startIndex, (nameNode ?? body ?? node).startIndex).trimEnd()
116
+ ).replace(name, "").trimEnd();
117
+ const classPrefix = prefix ? `${prefix} ${name}` : `class ${name}`;
118
+
119
+ if (!body) return `${classPrefix}${heritage} {}`;
120
+
121
+ const members: string[] = [];
122
+ for (const member of body.namedChildren) {
123
+ switch (member.type) {
124
+ case "public_field_definition":
125
+ case "property_definition": {
126
+ members.push(" " + collapseWhitespace(member.text).replace(/;$/, "") + ";");
127
+ break;
128
+ }
129
+ case "method_definition": {
130
+ const methodBody = member.childForFieldName("body");
131
+ if (methodBody) {
132
+ const sig = collapseWhitespace(source.slice(member.startIndex, methodBody.startIndex).trimEnd());
133
+ members.push(" " + sig + ";");
134
+ } else {
135
+ members.push(" " + collapseWhitespace(member.text) + ";");
136
+ }
137
+ break;
138
+ }
139
+ }
140
+ }
141
+
142
+ return `${classPrefix}${heritage} {\n${members.join("\n")}\n}`;
143
+ }
144
+
145
+ /** Extract interface with all members */
146
+ function extractInterface(node: Node, source: string): string {
147
+ const nameNode = node.childForFieldName("name");
148
+ const name = nameNode?.text ?? "Anonymous";
149
+ const body = node.childForFieldName("body");
150
+
151
+ // Heritage (extends)
152
+ const extendsClause = node.children.find(c => c.type === "extends_type_clause");
153
+ const ext = extendsClause ? " " + collapseWhitespace(extendsClause.text) : "";
154
+
155
+ // Prefix
156
+ const prefixEnd = (nameNode ?? body ?? node).startIndex;
157
+ const rawPrefix = source.slice(node.startIndex, prefixEnd).trimEnd();
158
+ const prefix = collapseWhitespace(rawPrefix).replace(name, "").trimEnd();
159
+ const ifacePrefix = prefix ? `${prefix} ${name}` : `interface ${name}`;
160
+
161
+ if (!body) return `${ifacePrefix}${ext} {}`;
162
+
163
+ const members = body.namedChildren.map(m => {
164
+ const text = collapseWhitespace(m.text).replace(/;$/, "");
165
+ return " " + text + ";";
166
+ });
167
+
168
+ return `${ifacePrefix}${ext} {\n${members.join("\n")}\n}`;
169
+ }
170
+
171
+ /** Extract enum */
172
+ function extractEnum(node: Node): string {
173
+ const nameNode = node.childForFieldName("name");
174
+ const name = nameNode?.text ?? "Anonymous";
175
+ const body = node.childForFieldName("body");
176
+
177
+ const isExport = node.parent?.type === "export_statement" ? "export " : "";
178
+ const isConst = node.children.some(c => c.text === "const") ? "const " : "";
179
+
180
+ if (!body) return `${isExport}${isConst}enum ${name} {}`;
181
+
182
+ const members = body.namedChildren
183
+ .filter(m => m.type === "enum_member" || m.type === "property_identifier")
184
+ .map(m => collapseWhitespace(m.text));
185
+
186
+ return `${isExport}${isConst}enum ${name} { ${members.join(", ")} }`;
187
+ }
188
+
189
+ /** Extract variable statement (const/let/var with possible arrow functions) */
190
+ function extractVariable(node: Node, source: string): string {
191
+ const keyword = node.children[0]?.text ?? "const";
192
+ const isExport = node.parent?.type === "export_statement" ? "export " : "";
193
+
194
+ const declarators = node.namedChildren.filter(c => c.type === "variable_declarator");
195
+ const parts = declarators.map(d => {
196
+ const name = d.childForFieldName("name")?.text ?? "?";
197
+ const typeAnn = d.childForFieldName("type")
198
+ ? ": " + collapseWhitespace(d.childForFieldName("type")!.text)
199
+ : "";
200
+ const value = d.childForFieldName("value");
201
+
202
+ if (value && (value.type === "arrow_function" || value.type === "function_expression" || value.type === "function")) {
203
+ return `${name} = ${stubBody(value, source)}`;
204
+ }
205
+ if (typeAnn) return `${name}${typeAnn}`;
206
+ if (value) return `${name} = …`;
207
+ return name;
208
+ });
209
+
210
+ return `${isExport}${keyword} ${parts.join(", ")};`;
211
+ }
212
+
213
+ /** Extract type alias */
214
+ function extractTypeAlias(node: Node): string {
215
+ const isExport = node.parent?.type === "export_statement" ? "export " : "";
216
+ return isExport + collapseWhitespace(node.text);
217
+ }
218
+
219
+ function collapseWhitespace(s: string): string {
220
+ return s.replace(/\s+/g, " ").trim();
221
+ }
222
+
223
+ /** Process a single top-level statement */
224
+ function extractTopLevel(node: Node, source: string): string | null {
225
+ // Unwrap export_statement to get the inner declaration
226
+ if (node.type === "export_statement") {
227
+ // Re-export: export { ... } from "..."
228
+ const exportClause = node.namedChildren.find(c => c.type === "export_clause");
229
+ if (exportClause) return collapseWhitespace(node.text);
230
+
231
+ // export default expression
232
+ const defaultKw = node.children.find(c => c.text === "default");
233
+ if (defaultKw) {
234
+ const inner = node.namedChildren.find(c => c.type !== "export_clause");
235
+ if (inner && (inner.type === "function_declaration" || inner.type === "class_declaration")) {
236
+ return "export default " + extractTopLevel(inner, source);
237
+ }
238
+ return `export default …;`;
239
+ }
240
+
241
+ // export <declaration>
242
+ const inner = node.namedChildren[0];
243
+ if (inner) {
244
+ const result = extractTopLevel(inner, source);
245
+ if (result) {
246
+ const alreadyHasExport = result.startsWith("export ");
247
+ return alreadyHasExport ? result : "export " + result;
248
+ }
249
+ }
250
+ return collapseWhitespace(node.text);
251
+ }
252
+
253
+ switch (node.type) {
254
+ case "import_statement":
255
+ return collapseWhitespace(node.text);
256
+ case "function_declaration":
257
+ case "generator_function_declaration":
258
+ return extractFunction(node, source);
259
+ case "class_declaration":
260
+ return extractClass(node, source);
261
+ case "interface_declaration":
262
+ return extractInterface(node, source);
263
+ case "type_alias_declaration":
264
+ return extractTypeAlias(node);
265
+ case "enum_declaration":
266
+ return extractEnum(node);
267
+ case "lexical_declaration":
268
+ case "variable_declaration":
269
+ return extractVariable(node, source);
270
+ case "export_statement":
271
+ return collapseWhitespace(node.text);
272
+ default:
273
+ return null;
274
+ }
275
+ }
276
+
277
+ export const tsExtractor: LanguageExtractor = {
278
+ extensions: ["ts", "tsx", "js", "jsx", "mts", "cts"],
279
+ grammarName: "typescript",
280
+
281
+ extract(tree: Tree, source: string, options?: HologramOptions): string[] {
282
+ const lines: string[] = [];
283
+
284
+ // L1 filtering setup
285
+ let importedSymbols: Set<string> | "all" | null = null;
286
+ if (options?.contextFile) {
287
+ try {
288
+ const contextSource = readFileSync(options.contextFile, "utf-8");
289
+ importedSymbols = extractImportedSymbols(contextSource, "");
290
+ if (importedSymbols !== "all" && importedSymbols.size === 0) importedSymbols = null;
291
+ } catch {
292
+ importedSymbols = null;
293
+ }
294
+ }
295
+
296
+ for (const stmt of tree.rootNode.namedChildren) {
297
+ // L1: filter non-imported exports
298
+ if (importedSymbols && importedSymbols !== "all") {
299
+ const exportedName = getExportedName(stmt);
300
+ if (exportedName !== null && !importedSymbols.has(exportedName)) {
301
+ const line = extractTopLevel(stmt, source);
302
+ if (line) {
303
+ const stub = line.split("\n")[0].replace(/\{[^}]*\}?\s*$/, "").trimEnd();
304
+ lines.push(`${stub} // details omitted — read directly if needed`);
305
+ }
306
+ continue;
307
+ }
308
+ }
309
+
310
+ const line = extractTopLevel(stmt, source);
311
+ if (line) lines.push(line);
312
+ }
313
+
314
+ if (importedSymbols && importedSymbols !== "all") {
315
+ lines.push("\n// [afd L1] Non-imported exports are shown as stubs. Use afd_read for full details.");
316
+ }
317
+
318
+ return lines;
319
+ },
320
+ };
@@ -0,0 +1,25 @@
1
+ import type { Tree } from "web-tree-sitter";
2
+
3
+ export interface HologramResult {
4
+ hologram: string;
5
+ originalLength: number;
6
+ hologramLength: number;
7
+ savings: number; // percentage 0-100
8
+ language?: string;
9
+ isDiff?: boolean;
10
+ changedNodes?: number;
11
+ }
12
+
13
+ export interface HologramOptions {
14
+ contextFile?: string;
15
+ diffOnly?: boolean;
16
+ }
17
+
18
+ export interface LanguageExtractor {
19
+ /** Supported file extensions (without dot) */
20
+ extensions: string[];
21
+ /** Tree-sitter grammar name for WASM resolution */
22
+ grammarName: string;
23
+ /** Extract type signatures from AST */
24
+ extract(tree: Tree, source: string, options?: HologramOptions): string[];
25
+ }
@@ -1,243 +1,71 @@
1
- import ts from "typescript";
2
-
3
- export interface HologramResult {
4
- hologram: string;
5
- originalLength: number;
6
- hologramLength: number;
7
- savings: number; // percentage 0-100
8
- }
9
-
10
- export function generateHologram(filePath: string, source: string): HologramResult {
11
- const sf = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);
12
- const lines: string[] = [];
13
-
14
- for (const stmt of sf.statements) {
15
- const line = extractNode(stmt, source);
16
- if (line) lines.push(line);
17
- }
18
-
19
- const hologram = lines.join("\n");
20
- const originalLength = source.length;
21
- const hologramLength = hologram.length;
22
- const savings = originalLength > 0
23
- ? Math.round((originalLength - hologramLength) / originalLength * 1000) / 10
24
- : 0;
25
-
26
- return { hologram, originalLength, hologramLength, savings };
27
- }
28
-
29
- function extractNode(node: ts.Node, source: string): string | null {
30
- // Import declarations — keep as-is
31
- if (ts.isImportDeclaration(node)) {
32
- return node.getText().replace(/\s+/g, " ").trim();
33
- }
34
-
35
- // Export declarations (re-exports)
36
- if (ts.isExportDeclaration(node)) {
37
- return node.getText().replace(/\s+/g, " ").trim();
38
- }
39
-
40
- // Export assignment (export default ...)
41
- if (ts.isExportAssignment(node)) {
42
- return `export default ${getTypeName(node.expression)};`;
43
- }
44
-
45
- // Type alias
46
- if (ts.isTypeAliasDeclaration(node)) {
47
- return collapseWhitespace(node.getText());
1
+ /**
2
+ * Hologram Engine — Language Dispatcher
3
+ *
4
+ * Routes file parsing to the appropriate tree-sitter extractor based on extension.
5
+ * Falls back to L0 (full source) for unsupported languages or parse errors.
6
+ */
7
+
8
+ import { TreeSitterEngine } from "./hologram/engine";
9
+ import { tsExtractor } from "./hologram/ts-extractor";
10
+ import { pyExtractor } from "./hologram/py-extractor";
11
+ import { fallbackL0 } from "./hologram/fallback";
12
+ import { generateIncrementalHologram, setCachedHologram } from "./hologram/incremental";
13
+ import type { HologramResult, HologramOptions, LanguageExtractor } from "./hologram/types";
14
+
15
+ // Re-export types for backward compatibility
16
+ export type { HologramResult, HologramOptions } from "./hologram/types";
17
+ export { clearHologramCache } from "./hologram/incremental";
18
+
19
+ const extractors: LanguageExtractor[] = [tsExtractor, pyExtractor];
20
+
21
+ function detectExtractor(filePath: string): LanguageExtractor | null {
22
+ const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
23
+ return extractors.find(e => e.extensions.includes(ext)) ?? null;
24
+ }
25
+
26
+ export async function generateHologram(
27
+ filePath: string,
28
+ source: string,
29
+ options?: HologramOptions,
30
+ ): Promise<HologramResult> {
31
+ const extractor = detectExtractor(filePath);
32
+
33
+ if (!extractor) {
34
+ return fallbackL0(filePath, source);
48
35
  }
49
36
 
50
- // Interface
51
- if (ts.isInterfaceDeclaration(node)) {
52
- return extractInterface(node);
53
- }
54
-
55
- // Enum
56
- if (ts.isEnumDeclaration(node)) {
57
- return extractEnum(node);
58
- }
59
-
60
- // Class
61
- if (ts.isClassDeclaration(node)) {
62
- return extractClass(node);
63
- }
64
-
65
- // Function declaration
66
- if (ts.isFunctionDeclaration(node)) {
67
- return extractFunction(node);
68
- }
69
-
70
- // Variable statement (const/let/var — may contain arrow functions, objects, etc.)
71
- if (ts.isVariableStatement(node)) {
72
- return extractVariableStatement(node);
73
- }
74
-
75
- // Fallback: skip unknown top-level statements
76
- return null;
77
- }
78
-
79
- function extractInterface(node: ts.InterfaceDeclaration): string {
80
- const mods = getModifiers(node);
81
- const name = node.name.text;
82
- const ext = node.heritageClauses
83
- ? " " + node.heritageClauses.map(h => h.getText()).join(", ")
84
- : "";
85
- const members = node.members.map(m => {
86
- const text = collapseWhitespace(m.getText()).replace(/;$/, "");
87
- return " " + text + ";";
88
- }).join("\n");
89
- return `${mods}interface ${name}${ext} {\n${members}\n}`;
90
- }
91
-
92
- function extractEnum(node: ts.EnumDeclaration): string {
93
- const mods = getModifiers(node);
94
- const name = node.name.text;
95
- const members = node.members.map(m => collapseWhitespace(m.getText())).join(", ");
96
- return `${mods}enum ${name} { ${members} }`;
97
- }
98
-
99
- function extractClass(node: ts.ClassDeclaration): string {
100
- const mods = getModifiers(node);
101
- const name = node.name?.text ?? "Anonymous";
102
- const ext = node.heritageClauses
103
- ? " " + node.heritageClauses.map(h => h.getText()).join(", ")
104
- : "";
105
-
106
- const members: string[] = [];
107
- for (const member of node.members) {
108
- if (ts.isPropertyDeclaration(member)) {
109
- members.push(" " + extractProperty(member) + ";");
110
- } else if (ts.isMethodDeclaration(member) || ts.isGetAccessor(member) || ts.isSetAccessor(member)) {
111
- members.push(" " + extractMethodSignature(member) + ";");
112
- } else if (ts.isConstructorDeclaration(member)) {
113
- const params = extractParams(member.parameters);
114
- members.push(` constructor(${params});`);
37
+ // Incremental diff mode
38
+ if (options?.diffOnly) {
39
+ try {
40
+ return await generateIncrementalHologram(filePath, source, extractor, options);
41
+ } catch {
42
+ return fallbackL0(filePath, source);
115
43
  }
116
44
  }
117
45
 
118
- return `${mods}class ${name}${ext} {\n${members.join("\n")}\n}`;
119
- }
120
-
121
- function extractFunction(node: ts.FunctionDeclaration): string {
122
- const mods = getModifiers(node);
123
- const name = node.name?.text ?? "anonymous";
124
- const typeParams = node.typeParameters
125
- ? `<${node.typeParameters.map(t => t.getText()).join(", ")}>`
126
- : "";
127
- const params = extractParams(node.parameters);
128
- const ret = node.type ? ": " + collapseWhitespace(node.type.getText()) : "";
129
- const async = hasModifier(node, ts.SyntaxKind.AsyncKeyword) ? "async " : "";
130
- return `${mods}${async}function ${name}${typeParams}(${params})${ret} {…}`;
131
- }
132
-
133
- function extractVariableStatement(node: ts.VariableStatement): string {
134
- const mods = getModifiers(node);
135
- const keyword = node.declarationList.flags & ts.NodeFlags.Const ? "const"
136
- : node.declarationList.flags & ts.NodeFlags.Let ? "let" : "var";
137
-
138
- const decls = node.declarationList.declarations.map(d => {
139
- const name = d.name.getText();
140
- const typeAnn = d.type ? ": " + collapseWhitespace(d.type.getText()) : "";
141
-
142
- if (d.initializer) {
143
- // Arrow function or function expression
144
- if (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer)) {
145
- const fn = d.initializer;
146
- const async = hasModifier(fn, ts.SyntaxKind.AsyncKeyword) ? "async " : "";
147
- const typeParams = fn.typeParameters
148
- ? `<${fn.typeParameters.map(t => t.getText()).join(", ")}>`
149
- : "";
150
- const params = extractParams(fn.parameters);
151
- const ret = fn.type ? ": " + collapseWhitespace(fn.type.getText()) : "";
152
- return `${name} = ${async}${typeParams}(${params})${ret} => {…}`;
153
- }
154
- // Object/array/other — just show type or truncated value
155
- if (typeAnn) return `${name}${typeAnn}`;
156
- return `${name} = …`;
157
- }
158
-
159
- return `${name}${typeAnn}`;
160
- });
161
-
162
- return `${mods}${keyword} ${decls.join(", ")};`;
163
- }
164
-
165
- function extractProperty(node: ts.PropertyDeclaration): string {
166
- const mods = getMemberModifiers(node);
167
- const name = node.name.getText();
168
- const type = node.type ? ": " + collapseWhitespace(node.type.getText()) : "";
169
- const optional = node.questionToken ? "?" : "";
170
- return `${mods}${name}${optional}${type}`;
171
- }
172
-
173
- function extractMethodSignature(node: ts.MethodDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration): string {
174
- const mods = getMemberModifiers(node);
175
- const name = node.name.getText();
176
- const async = hasModifier(node, ts.SyntaxKind.AsyncKeyword) ? "async " : "";
177
-
178
- if (ts.isGetAccessor(node)) {
179
- const ret = node.type ? ": " + collapseWhitespace(node.type.getText()) : "";
180
- return `${mods}get ${name}()${ret}`;
181
- }
182
- if (ts.isSetAccessor(node)) {
183
- const params = extractParams(node.parameters);
184
- return `${mods}set ${name}(${params})`;
46
+ try {
47
+ const engine = await TreeSitterEngine.getInstance();
48
+ const tree = await engine.parse(source, extractor.grammarName);
49
+ const lines = extractor.extract(tree, source, options);
50
+ tree.delete();
51
+
52
+ // Cache for future incremental diffs
53
+ setCachedHologram(filePath, lines);
54
+
55
+ const hologram = lines.join("\n");
56
+ const hologramLength = hologram.length;
57
+ const savings = source.length > 0
58
+ ? Math.round((source.length - hologramLength) / source.length * 1000) / 10
59
+ : 0;
60
+
61
+ return {
62
+ hologram,
63
+ originalLength: source.length,
64
+ hologramLength,
65
+ savings,
66
+ language: extractor.grammarName,
67
+ };
68
+ } catch {
69
+ return fallbackL0(filePath, source);
185
70
  }
186
-
187
- const md = node as ts.MethodDeclaration;
188
- const typeParams = md.typeParameters
189
- ? `<${md.typeParameters.map(t => t.getText()).join(", ")}>`
190
- : "";
191
- const params = extractParams(md.parameters);
192
- const ret = md.type ? ": " + collapseWhitespace(md.type.getText()) : "";
193
- return `${mods}${async}${name}${typeParams}(${params})${ret}`;
194
- }
195
-
196
- function extractParams(params: ts.NodeArray<ts.ParameterDeclaration>): string {
197
- return params.map(p => {
198
- const name = p.name.getText();
199
- const optional = p.questionToken ? "?" : "";
200
- const type = p.type ? ": " + collapseWhitespace(p.type.getText()) : "";
201
- const rest = p.dotDotDotToken ? "..." : "";
202
- return `${rest}${name}${optional}${type}`;
203
- }).join(", ");
204
- }
205
-
206
- function getModifiers(node: ts.Node): string {
207
- const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
208
- if (!mods) return "";
209
- const relevant = mods
210
- .filter(m => m.kind === ts.SyntaxKind.ExportKeyword || m.kind === ts.SyntaxKind.DefaultKeyword || m.kind === ts.SyntaxKind.DeclareKeyword)
211
- .map(m => m.getText());
212
- return relevant.length ? relevant.join(" ") + " " : "";
213
- }
214
-
215
- function getMemberModifiers(node: ts.Node): string {
216
- const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
217
- if (!mods) return "";
218
- const relevant = mods
219
- .filter(m =>
220
- m.kind === ts.SyntaxKind.PublicKeyword ||
221
- m.kind === ts.SyntaxKind.PrivateKeyword ||
222
- m.kind === ts.SyntaxKind.ProtectedKeyword ||
223
- m.kind === ts.SyntaxKind.StaticKeyword ||
224
- m.kind === ts.SyntaxKind.ReadonlyKeyword ||
225
- m.kind === ts.SyntaxKind.AbstractKeyword
226
- )
227
- .map(m => m.getText());
228
- return relevant.length ? relevant.join(" ") + " " : "";
229
- }
230
-
231
- function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean {
232
- const mods = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
233
- return mods?.some(m => m.kind === kind) ?? false;
234
- }
235
-
236
- function getTypeName(node: ts.Node): string {
237
- if (ts.isIdentifier(node)) return node.text;
238
- return "…";
239
- }
240
-
241
- function collapseWhitespace(s: string): string {
242
- return s.replace(/\s+/g, " ").trim();
243
71
  }