@nogataka/smart-edit 0.0.14

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 (186) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +244 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +7 -0
  5. package/dist/devtools/generate_prompt_factory.d.ts +5 -0
  6. package/dist/devtools/generate_prompt_factory.js +114 -0
  7. package/dist/index.d.ts +34 -0
  8. package/dist/index.js +34 -0
  9. package/dist/interprompt/index.d.ts +2 -0
  10. package/dist/interprompt/index.js +1 -0
  11. package/dist/interprompt/jinja_template.d.ts +10 -0
  12. package/dist/interprompt/jinja_template.js +174 -0
  13. package/dist/interprompt/multilang_prompt.d.ts +54 -0
  14. package/dist/interprompt/multilang_prompt.js +302 -0
  15. package/dist/interprompt/prompt_factory.d.ts +16 -0
  16. package/dist/interprompt/prompt_factory.js +189 -0
  17. package/dist/interprompt/util/class_decorators.d.ts +1 -0
  18. package/dist/interprompt/util/class_decorators.js +1 -0
  19. package/dist/interprompt/util/index.d.ts +1 -0
  20. package/dist/interprompt/util/index.js +1 -0
  21. package/dist/serena/agent.d.ts +118 -0
  22. package/dist/serena/agent.js +675 -0
  23. package/dist/serena/agno.d.ts +111 -0
  24. package/dist/serena/agno.js +278 -0
  25. package/dist/serena/analytics.d.ts +24 -0
  26. package/dist/serena/analytics.js +119 -0
  27. package/dist/serena/cli.d.ts +9 -0
  28. package/dist/serena/cli.js +731 -0
  29. package/dist/serena/code_editor.d.ts +42 -0
  30. package/dist/serena/code_editor.js +239 -0
  31. package/dist/serena/config/context_mode.d.ts +41 -0
  32. package/dist/serena/config/context_mode.js +239 -0
  33. package/dist/serena/config/serena_config.d.ts +134 -0
  34. package/dist/serena/config/serena_config.js +718 -0
  35. package/dist/serena/constants.d.ts +18 -0
  36. package/dist/serena/constants.js +27 -0
  37. package/dist/serena/dashboard.d.ts +55 -0
  38. package/dist/serena/dashboard.js +472 -0
  39. package/dist/serena/generated/generated_prompt_factory.d.ts +27 -0
  40. package/dist/serena/generated/generated_prompt_factory.js +42 -0
  41. package/dist/serena/gui_log_viewer.d.ts +41 -0
  42. package/dist/serena/gui_log_viewer.js +436 -0
  43. package/dist/serena/mcp.d.ts +118 -0
  44. package/dist/serena/mcp.js +904 -0
  45. package/dist/serena/project.d.ts +62 -0
  46. package/dist/serena/project.js +321 -0
  47. package/dist/serena/prompt_factory.d.ts +20 -0
  48. package/dist/serena/prompt_factory.js +42 -0
  49. package/dist/serena/resources/config/contexts/agent.yml +8 -0
  50. package/dist/serena/resources/config/contexts/chatgpt.yml +28 -0
  51. package/dist/serena/resources/config/contexts/codex.yml +27 -0
  52. package/dist/serena/resources/config/contexts/context.template.yml +11 -0
  53. package/dist/serena/resources/config/contexts/desktop-app.yml +17 -0
  54. package/dist/serena/resources/config/contexts/ide-assistant.yml +26 -0
  55. package/dist/serena/resources/config/contexts/oaicompat-agent.yml +8 -0
  56. package/dist/serena/resources/config/internal_modes/jetbrains.yml +15 -0
  57. package/dist/serena/resources/config/modes/editing.yml +112 -0
  58. package/dist/serena/resources/config/modes/interactive.yml +11 -0
  59. package/dist/serena/resources/config/modes/mode.template.yml +7 -0
  60. package/dist/serena/resources/config/modes/no-onboarding.yml +8 -0
  61. package/dist/serena/resources/config/modes/onboarding.yml +16 -0
  62. package/dist/serena/resources/config/modes/one-shot.yml +15 -0
  63. package/dist/serena/resources/config/modes/planning.yml +15 -0
  64. package/dist/serena/resources/config/prompt_templates/simple_tool_outputs.yml +75 -0
  65. package/dist/serena/resources/config/prompt_templates/system_prompt.yml +66 -0
  66. package/dist/serena/resources/dashboard/dashboard.js +815 -0
  67. package/dist/serena/resources/dashboard/index.html +314 -0
  68. package/dist/serena/resources/dashboard/jquery.min.js +3 -0
  69. package/dist/serena/resources/dashboard/serena-icon-16.png +0 -0
  70. package/dist/serena/resources/dashboard/serena-icon-32.png +0 -0
  71. package/dist/serena/resources/dashboard/serena-icon-48.png +0 -0
  72. package/dist/serena/resources/dashboard/serena-logs-dark-mode.png +0 -0
  73. package/dist/serena/resources/dashboard/serena-logs.png +0 -0
  74. package/dist/serena/resources/project.template.yml +67 -0
  75. package/dist/serena/resources/serena_config.template.yml +85 -0
  76. package/dist/serena/symbol.d.ts +199 -0
  77. package/dist/serena/symbol.js +616 -0
  78. package/dist/serena/text_utils.d.ts +51 -0
  79. package/dist/serena/text_utils.js +267 -0
  80. package/dist/serena/tools/cmd_tools.d.ts +31 -0
  81. package/dist/serena/tools/cmd_tools.js +48 -0
  82. package/dist/serena/tools/config_tools.d.ts +53 -0
  83. package/dist/serena/tools/config_tools.js +176 -0
  84. package/dist/serena/tools/file_tools.d.ts +231 -0
  85. package/dist/serena/tools/file_tools.js +511 -0
  86. package/dist/serena/tools/index.d.ts +7 -0
  87. package/dist/serena/tools/index.js +7 -0
  88. package/dist/serena/tools/memory_tools.d.ts +60 -0
  89. package/dist/serena/tools/memory_tools.js +135 -0
  90. package/dist/serena/tools/symbol_tools.d.ts +165 -0
  91. package/dist/serena/tools/symbol_tools.js +362 -0
  92. package/dist/serena/tools/tools_base.d.ts +162 -0
  93. package/dist/serena/tools/tools_base.js +378 -0
  94. package/dist/serena/tools/workflow_tools.d.ts +35 -0
  95. package/dist/serena/tools/workflow_tools.js +161 -0
  96. package/dist/serena/util/class_decorators.d.ts +7 -0
  97. package/dist/serena/util/class_decorators.js +37 -0
  98. package/dist/serena/util/exception.d.ts +8 -0
  99. package/dist/serena/util/exception.js +53 -0
  100. package/dist/serena/util/file_system.d.ts +30 -0
  101. package/dist/serena/util/file_system.js +352 -0
  102. package/dist/serena/util/general.d.ts +11 -0
  103. package/dist/serena/util/general.js +42 -0
  104. package/dist/serena/util/git.d.ts +11 -0
  105. package/dist/serena/util/git.js +37 -0
  106. package/dist/serena/util/inspection.d.ts +45 -0
  107. package/dist/serena/util/inspection.js +221 -0
  108. package/dist/serena/util/logging.d.ts +46 -0
  109. package/dist/serena/util/logging.js +205 -0
  110. package/dist/serena/util/shell.d.ts +21 -0
  111. package/dist/serena/util/shell.js +95 -0
  112. package/dist/serena/util/thread.d.ts +23 -0
  113. package/dist/serena/util/thread.js +88 -0
  114. package/dist/serena/version.d.ts +1 -0
  115. package/dist/serena/version.js +23 -0
  116. package/dist/solidlsp/language_servers/autoload.d.ts +23 -0
  117. package/dist/solidlsp/language_servers/autoload.js +25 -0
  118. package/dist/solidlsp/language_servers/bash_language_server.d.ts +10 -0
  119. package/dist/solidlsp/language_servers/bash_language_server.js +64 -0
  120. package/dist/solidlsp/language_servers/clangd_language_server.d.ts +13 -0
  121. package/dist/solidlsp/language_servers/clangd_language_server.js +110 -0
  122. package/dist/solidlsp/language_servers/clojure_lsp.d.ts +13 -0
  123. package/dist/solidlsp/language_servers/clojure_lsp.js +137 -0
  124. package/dist/solidlsp/language_servers/common.d.ts +41 -0
  125. package/dist/solidlsp/language_servers/common.js +365 -0
  126. package/dist/solidlsp/language_servers/csharp_language_server.d.ts +21 -0
  127. package/dist/solidlsp/language_servers/csharp_language_server.js +694 -0
  128. package/dist/solidlsp/language_servers/dart_language_server.d.ts +10 -0
  129. package/dist/solidlsp/language_servers/dart_language_server.js +122 -0
  130. package/dist/solidlsp/language_servers/eclipse_jdtls.d.ts +24 -0
  131. package/dist/solidlsp/language_servers/eclipse_jdtls.js +671 -0
  132. package/dist/solidlsp/language_servers/erlang_language_server.d.ts +22 -0
  133. package/dist/solidlsp/language_servers/erlang_language_server.js +327 -0
  134. package/dist/solidlsp/language_servers/gopls.d.ts +12 -0
  135. package/dist/solidlsp/language_servers/gopls.js +59 -0
  136. package/dist/solidlsp/language_servers/intelephense.d.ts +13 -0
  137. package/dist/solidlsp/language_servers/intelephense.js +121 -0
  138. package/dist/solidlsp/language_servers/jedi_server.d.ts +18 -0
  139. package/dist/solidlsp/language_servers/jedi_server.js +234 -0
  140. package/dist/solidlsp/language_servers/kotlin_language_server.d.ts +19 -0
  141. package/dist/solidlsp/language_servers/kotlin_language_server.js +474 -0
  142. package/dist/solidlsp/language_servers/lua_ls.d.ts +18 -0
  143. package/dist/solidlsp/language_servers/lua_ls.js +319 -0
  144. package/dist/solidlsp/language_servers/nixd_language_server.d.ts +17 -0
  145. package/dist/solidlsp/language_servers/nixd_language_server.js +341 -0
  146. package/dist/solidlsp/language_servers/pyright_server.d.ts +19 -0
  147. package/dist/solidlsp/language_servers/pyright_server.js +180 -0
  148. package/dist/solidlsp/language_servers/r_language_server.d.ts +19 -0
  149. package/dist/solidlsp/language_servers/r_language_server.js +184 -0
  150. package/dist/solidlsp/language_servers/ruby_common.d.ts +10 -0
  151. package/dist/solidlsp/language_servers/ruby_common.js +136 -0
  152. package/dist/solidlsp/language_servers/ruby_lsp.d.ts +18 -0
  153. package/dist/solidlsp/language_servers/ruby_lsp.js +230 -0
  154. package/dist/solidlsp/language_servers/rust_analyzer.d.ts +13 -0
  155. package/dist/solidlsp/language_servers/rust_analyzer.js +96 -0
  156. package/dist/solidlsp/language_servers/solargraph.d.ts +18 -0
  157. package/dist/solidlsp/language_servers/solargraph.js +208 -0
  158. package/dist/solidlsp/language_servers/sourcekit_lsp.d.ts +24 -0
  159. package/dist/solidlsp/language_servers/sourcekit_lsp.js +449 -0
  160. package/dist/solidlsp/language_servers/terraform_ls.d.ts +13 -0
  161. package/dist/solidlsp/language_servers/terraform_ls.js +139 -0
  162. package/dist/solidlsp/language_servers/typescript_language_server.d.ts +20 -0
  163. package/dist/solidlsp/language_servers/typescript_language_server.js +237 -0
  164. package/dist/solidlsp/language_servers/vts_language_server.d.ts +13 -0
  165. package/dist/solidlsp/language_servers/vts_language_server.js +121 -0
  166. package/dist/solidlsp/language_servers/zls.d.ts +20 -0
  167. package/dist/solidlsp/language_servers/zls.js +254 -0
  168. package/dist/solidlsp/ls.d.ts +197 -0
  169. package/dist/solidlsp/ls.js +507 -0
  170. package/dist/solidlsp/ls_config.d.ts +43 -0
  171. package/dist/solidlsp/ls_config.js +157 -0
  172. package/dist/solidlsp/ls_exceptions.d.ts +5 -0
  173. package/dist/solidlsp/ls_exceptions.js +14 -0
  174. package/dist/solidlsp/ls_handler.d.ts +54 -0
  175. package/dist/solidlsp/ls_handler.js +406 -0
  176. package/dist/solidlsp/ls_request.d.ts +31 -0
  177. package/dist/solidlsp/ls_request.js +42 -0
  178. package/dist/solidlsp/ls_types.d.ts +7 -0
  179. package/dist/solidlsp/ls_types.js +8 -0
  180. package/dist/solidlsp/lsp_protocol_handler/server.d.ts +61 -0
  181. package/dist/solidlsp/lsp_protocol_handler/server.js +68 -0
  182. package/dist/solidlsp/util/subprocess_util.d.ts +6 -0
  183. package/dist/solidlsp/util/subprocess_util.js +11 -0
  184. package/dist/solidlsp/util/zip.d.ts +25 -0
  185. package/dist/solidlsp/util/zip.js +188 -0
  186. package/package.json +65 -0
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ const require = createRequire(import.meta.url);
5
+ let cachedYamlModule;
6
+ function loadYamlModule() {
7
+ if (cachedYamlModule) {
8
+ return cachedYamlModule;
9
+ }
10
+ try {
11
+ const module = require('yaml');
12
+ cachedYamlModule = module;
13
+ return module;
14
+ }
15
+ catch {
16
+ throw new Error('YAML サポートが利用できません。依存パッケージ "yaml" をインストールしてください。');
17
+ }
18
+ }
19
+ export function loadYaml(pathname, preserveComments = false) {
20
+ const yaml = loadYamlModule();
21
+ const source = fs.readFileSync(pathname, 'utf-8');
22
+ if (preserveComments) {
23
+ return yaml.parseDocument(source);
24
+ }
25
+ const parsed = yaml.parse(source);
26
+ if (parsed && typeof parsed === 'object') {
27
+ return parsed;
28
+ }
29
+ return {};
30
+ }
31
+ export function saveYaml(pathname, data, preserveComments = false) {
32
+ const yaml = loadYamlModule();
33
+ const directory = path.dirname(pathname);
34
+ fs.mkdirSync(directory, { recursive: true });
35
+ const serialized = preserveComments && isYamlDocument(data)
36
+ ? data.toString()
37
+ : yaml.stringify(data);
38
+ fs.writeFileSync(pathname, serialized, 'utf-8');
39
+ }
40
+ function isYamlDocument(data) {
41
+ return typeof data === 'object' && data !== null && typeof data.toString === 'function';
42
+ }
@@ -0,0 +1,11 @@
1
+ export interface GitStatus {
2
+ commit: string;
3
+ hasUnstagedChanges: boolean;
4
+ hasStagedUncommittedChanges: boolean;
5
+ hasUntrackedFiles: boolean;
6
+ }
7
+ export interface GetGitStatusOptions {
8
+ cwd?: string;
9
+ env?: NodeJS.ProcessEnv;
10
+ }
11
+ export declare function getGitStatus(options?: GetGitStatusOptions): Promise<GitStatus | null>;
@@ -0,0 +1,37 @@
1
+ import { createSerenaLogger } from './logging.js';
2
+ import { subprocessCheckOutput } from './shell.js';
3
+ const { logger: gitLogger } = createSerenaLogger({
4
+ level: 'debug',
5
+ emitToConsole: false,
6
+ name: 'SerenaGit'
7
+ });
8
+ export async function getGitStatus(options = {}) {
9
+ try {
10
+ const commitHash = await runGitCommand(['rev-parse', 'HEAD'], options);
11
+ const hasUnstagedChanges = await hasGitOutput(['diff', '--name-only'], options);
12
+ const hasStagedUncommittedChanges = await hasGitOutput(['diff', '--staged', '--name-only'], options);
13
+ const hasUntrackedFiles = await hasGitOutput(['ls-files', '--others', '--exclude-standard'], options);
14
+ return {
15
+ commit: commitHash,
16
+ hasUnstagedChanges,
17
+ hasStagedUncommittedChanges,
18
+ hasUntrackedFiles
19
+ };
20
+ }
21
+ catch (error) {
22
+ gitLogger.debug('Failed to read git status', { error });
23
+ return null;
24
+ }
25
+ }
26
+ async function runGitCommand(args, { cwd, env }) {
27
+ const output = await subprocessCheckOutput(['git', ...args], {
28
+ cwd,
29
+ env,
30
+ strip: true
31
+ });
32
+ return output;
33
+ }
34
+ async function hasGitOutput(args, options) {
35
+ const output = await runGitCommand(args, options);
36
+ return output.length > 0;
37
+ }
@@ -0,0 +1,45 @@
1
+ export type Constructor<T = unknown> = abstract new (...args: unknown[]) => T;
2
+ export interface RegisterSubclassOptions {
3
+ /**
4
+ * 任意で明示的な基底クラスを指定できます。指定しない場合は prototype 連鎖から推測します。
5
+ */
6
+ base?: Constructor<unknown>;
7
+ }
8
+ /**
9
+ * サブクラスをレジストリに登録します。TypeScript では Python のように __subclasses__() が無いため、
10
+ * 各クラス定義側からこの関数を呼び出して静的メタデータを構築します。
11
+ */
12
+ export declare function registerSubclass<T>(derived: Constructor<T>, options?: RegisterSubclassOptions): void;
13
+ /**
14
+ * 指定した基底クラスのサブクラスを列挙します。recursive = true の場合は再帰的に辿ります。
15
+ */
16
+ export declare function iterSubclasses<T>(base: Constructor<T>, recursive?: boolean): Generator<Constructor<T>, void, unknown>;
17
+ /**
18
+ * テスト用途などでレジストリを初期化したい場合に使用します。
19
+ */
20
+ export declare function clearSubclassRegistry(): void;
21
+ export interface FilenameMatcher {
22
+ isRelevantFilename(filename: string): boolean;
23
+ }
24
+ export interface ProgrammingLanguageDefinition {
25
+ name: string;
26
+ matcher: FilenameMatcher;
27
+ experimental?: boolean;
28
+ }
29
+ export interface DetermineLanguageOptions {
30
+ languages?: Iterable<LanguageInput>;
31
+ includeExperimental?: boolean;
32
+ }
33
+ type LanguageInput = ProgrammingLanguageDefinition | SolidLanguageLike;
34
+ interface SolidLanguageLike {
35
+ toString(): string;
36
+ getSourceFnMatcher?: () => unknown;
37
+ getSourceFilenameMatcher?: () => unknown;
38
+ isExperimental?: () => boolean;
39
+ }
40
+ export declare function registerLanguageDefinition(language: ProgrammingLanguageDefinition): void;
41
+ export declare function registerLanguageDefinitions(languages: Iterable<ProgrammingLanguageDefinition>): void;
42
+ export declare function clearLanguageRegistry(): void;
43
+ export declare function getRegisteredLanguages(): ProgrammingLanguageDefinition[];
44
+ export declare function determineProgrammingLanguageComposition(repoPath: string, options?: DetermineLanguageOptions): Record<string, number>;
45
+ export {};
@@ -0,0 +1,221 @@
1
+ import path from 'node:path';
2
+ import { findAllNonIgnoredFiles } from './file_system.js';
3
+ const subclassRegistry = new Map();
4
+ /**
5
+ * サブクラスをレジストリに登録します。TypeScript では Python のように __subclasses__() が無いため、
6
+ * 各クラス定義側からこの関数を呼び出して静的メタデータを構築します。
7
+ */
8
+ export function registerSubclass(derived, options) {
9
+ const base = options?.base ?? getDirectBaseConstructor(derived);
10
+ if (!base || base === derived) {
11
+ return;
12
+ }
13
+ let entries = subclassRegistry.get(base);
14
+ if (!entries) {
15
+ entries = new Set();
16
+ subclassRegistry.set(base, entries);
17
+ }
18
+ entries.add(derived);
19
+ }
20
+ /**
21
+ * 指定した基底クラスのサブクラスを列挙します。recursive = true の場合は再帰的に辿ります。
22
+ */
23
+ export function* iterSubclasses(base, recursive = true) {
24
+ yield* iterateSubclassesInternal(base, recursive, new Set());
25
+ }
26
+ function* iterateSubclassesInternal(base, recursive, seen) {
27
+ const direct = subclassRegistry.get(base);
28
+ if (!direct) {
29
+ return;
30
+ }
31
+ for (const subclass of direct) {
32
+ if (seen.has(subclass)) {
33
+ continue;
34
+ }
35
+ seen.add(subclass);
36
+ yield subclass;
37
+ if (recursive) {
38
+ yield* iterateSubclassesInternal(subclass, true, seen);
39
+ }
40
+ }
41
+ }
42
+ function getDirectBaseConstructor(ctor) {
43
+ const prototype = Object.getPrototypeOf(ctor.prototype);
44
+ if (!prototype) {
45
+ return undefined;
46
+ }
47
+ const parentCtor = prototype.constructor;
48
+ if (typeof parentCtor !== 'function') {
49
+ return undefined;
50
+ }
51
+ if (parentCtor === Object || parentCtor === Function) {
52
+ return undefined;
53
+ }
54
+ return parentCtor;
55
+ }
56
+ /**
57
+ * テスト用途などでレジストリを初期化したい場合に使用します。
58
+ */
59
+ export function clearSubclassRegistry() {
60
+ subclassRegistry.clear();
61
+ }
62
+ const languageRegistry = new Map();
63
+ export function registerLanguageDefinition(language) {
64
+ const matcherFn = bindMatcher(language.matcher);
65
+ languageRegistry.set(language.name, {
66
+ name: language.name,
67
+ matcher: {
68
+ isRelevantFilename: matcherFn
69
+ },
70
+ experimental: language.experimental ?? false
71
+ });
72
+ }
73
+ export function registerLanguageDefinitions(languages) {
74
+ for (const language of languages) {
75
+ registerLanguageDefinition(language);
76
+ }
77
+ }
78
+ export function clearLanguageRegistry() {
79
+ languageRegistry.clear();
80
+ }
81
+ export function getRegisteredLanguages() {
82
+ return Array.from(languageRegistry.values()).map((language) => ({
83
+ name: language.name,
84
+ matcher: bindMatcherObject(language.matcher),
85
+ experimental: language.experimental ?? false
86
+ }));
87
+ }
88
+ export function determineProgrammingLanguageComposition(repoPath, options) {
89
+ const files = findAllNonIgnoredFiles(repoPath);
90
+ if (files.length === 0) {
91
+ return {};
92
+ }
93
+ const includeExperimental = options?.includeExperimental ?? false;
94
+ const languages = resolveLanguages(options?.languages, includeExperimental);
95
+ if (!languages.length) {
96
+ return {};
97
+ }
98
+ const totalFiles = files.length;
99
+ const counts = new Map();
100
+ for (const language of languages) {
101
+ let count = 0;
102
+ for (const filePath of files) {
103
+ const filename = path.basename(filePath);
104
+ if (language.matcher.isRelevantFilename(filename)) {
105
+ count += 1;
106
+ }
107
+ }
108
+ if (count > 0) {
109
+ counts.set(language.name, count);
110
+ }
111
+ }
112
+ const result = {};
113
+ for (const [name, count] of counts) {
114
+ const percentage = (count / totalFiles) * 100;
115
+ result[name] = Math.round(percentage * 100) / 100;
116
+ }
117
+ return result;
118
+ }
119
+ function resolveLanguages(explicit, includeExperimental) {
120
+ const source = explicit ?? languageRegistry.values();
121
+ const normalized = new Map();
122
+ for (const item of source) {
123
+ const definition = normalizeLanguageInput(item, includeExperimental);
124
+ if (definition) {
125
+ normalized.set(definition.name, definition);
126
+ }
127
+ }
128
+ return Array.from(normalized.values());
129
+ }
130
+ function normalizeLanguageInput(input, includeExperimental) {
131
+ if (isProgrammingLanguageDefinition(input)) {
132
+ if (!includeExperimental && input.experimental) {
133
+ return undefined;
134
+ }
135
+ const matcher = bindMatcher(input.matcher);
136
+ return {
137
+ name: input.name,
138
+ matcher: {
139
+ isRelevantFilename: matcher
140
+ },
141
+ experimental: input.experimental ?? false
142
+ };
143
+ }
144
+ if (isSolidLanguageLike(input)) {
145
+ const experimental = typeof input.isExperimental === 'function' ? input.isExperimental() : false;
146
+ if (!includeExperimental && experimental) {
147
+ return undefined;
148
+ }
149
+ const matcherCandidate = typeof input.getSourceFnMatcher === 'function'
150
+ ? input.getSourceFnMatcher()
151
+ : typeof input.getSourceFilenameMatcher === 'function'
152
+ ? input.getSourceFilenameMatcher()
153
+ : undefined;
154
+ if (!matcherCandidate) {
155
+ throw new TypeError('Language object does not expose a source filename matcher');
156
+ }
157
+ const matcher = bindMatcher(normalizeMatcher(matcherCandidate));
158
+ return {
159
+ name: String(input),
160
+ matcher: {
161
+ isRelevantFilename: matcher
162
+ },
163
+ experimental
164
+ };
165
+ }
166
+ throw new TypeError('Unsupported language definition encountered');
167
+ }
168
+ function bindMatcherObject(matcher) {
169
+ const bound = bindMatcher(matcher);
170
+ return {
171
+ isRelevantFilename: bound
172
+ };
173
+ }
174
+ function bindMatcher(matcher) {
175
+ if (typeof matcher.isRelevantFilename !== 'function') {
176
+ return () => false;
177
+ }
178
+ return matcher.isRelevantFilename.bind(matcher);
179
+ }
180
+ function normalizeMatcher(candidate) {
181
+ if (isFilenameMatcher(candidate)) {
182
+ return candidate;
183
+ }
184
+ if (candidate && typeof candidate === 'object') {
185
+ const recordCandidate = candidate;
186
+ const maybeCamel = recordCandidate.isRelevantFilename;
187
+ if (typeof maybeCamel === 'function') {
188
+ return {
189
+ isRelevantFilename: maybeCamel.bind(candidate)
190
+ };
191
+ }
192
+ const maybeSnake = recordCandidate.is_relevant_filename;
193
+ if (typeof maybeSnake === 'function') {
194
+ return {
195
+ isRelevantFilename: maybeSnake.bind(candidate)
196
+ };
197
+ }
198
+ }
199
+ throw new TypeError('Unsupported filename matcher');
200
+ }
201
+ function isProgrammingLanguageDefinition(value) {
202
+ if (!value || typeof value !== 'object') {
203
+ return false;
204
+ }
205
+ const candidate = value;
206
+ return (typeof candidate.name === 'string' &&
207
+ typeof candidate.matcher === 'object' &&
208
+ candidate.matcher !== null &&
209
+ typeof candidate.matcher.isRelevantFilename === 'function');
210
+ }
211
+ function isSolidLanguageLike(value) {
212
+ if (!value || typeof value !== 'object') {
213
+ return false;
214
+ }
215
+ const candidate = value;
216
+ return (typeof candidate.toString === 'function' &&
217
+ (typeof candidate.getSourceFnMatcher === 'function' || typeof candidate.getSourceFilenameMatcher === 'function'));
218
+ }
219
+ function isFilenameMatcher(value) {
220
+ return !!value && typeof value === 'object' && typeof value.isRelevantFilename === 'function';
221
+ }
@@ -0,0 +1,46 @@
1
+ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
2
+ export interface SerenaLogger {
3
+ trace(message: string, meta?: unknown): void;
4
+ debug(message: string, meta?: unknown): void;
5
+ info(message: string, meta?: unknown): void;
6
+ warn(message: string, meta?: unknown): void;
7
+ error(message: string, meta?: unknown): void;
8
+ fatal(message: string, meta?: unknown): void;
9
+ }
10
+ export interface SerenaLoggerOptions {
11
+ level?: LogLevel;
12
+ memoryHandler?: MemoryLogHandler;
13
+ emitToConsole?: boolean;
14
+ name?: string;
15
+ }
16
+ export declare function setConsoleLoggingEnabled(enabled: boolean): void;
17
+ export declare function isConsoleLoggingEnabled(): boolean;
18
+ export interface SerenaLogMessage {
19
+ timestamp: Date;
20
+ level: LogLevel;
21
+ message: string;
22
+ meta?: unknown;
23
+ loggerName: string;
24
+ }
25
+ export declare class LogBuffer {
26
+ private readonly logMessages;
27
+ append(message: string): void;
28
+ getLogMessages(): string[];
29
+ }
30
+ export type EmitCallback = (message: string) => void;
31
+ export declare class MemoryLogHandler {
32
+ private readonly buffer;
33
+ private readonly callbacks;
34
+ private queue;
35
+ private draining;
36
+ addEmitCallback(callback: EmitCallback): void;
37
+ removeEmitCallback(callback: EmitCallback): void;
38
+ handle(message: string): void;
39
+ getLogMessages(): string[];
40
+ private flush;
41
+ }
42
+ export declare function createSerenaLogger(options?: SerenaLoggerOptions): {
43
+ logger: SerenaLogger;
44
+ memoryHandler: MemoryLogHandler;
45
+ };
46
+ export declare function formatLogMessage(message: SerenaLogMessage): string;
@@ -0,0 +1,205 @@
1
+ import { setImmediate } from 'node:timers';
2
+ import { SERENA_LOG_FORMAT } from '../constants.js';
3
+ const LEVEL_ORDER = {
4
+ trace: 10,
5
+ debug: 20,
6
+ info: 30,
7
+ warn: 40,
8
+ error: 50,
9
+ fatal: 60
10
+ };
11
+ let consoleLoggingEnabled = true;
12
+ export function setConsoleLoggingEnabled(enabled) {
13
+ consoleLoggingEnabled = enabled;
14
+ }
15
+ export function isConsoleLoggingEnabled() {
16
+ return consoleLoggingEnabled;
17
+ }
18
+ export class LogBuffer {
19
+ logMessages = [];
20
+ append(message) {
21
+ this.logMessages.push(message);
22
+ }
23
+ getLogMessages() {
24
+ return [...this.logMessages];
25
+ }
26
+ }
27
+ export class MemoryLogHandler {
28
+ buffer = new LogBuffer();
29
+ callbacks = new Set();
30
+ queue = [];
31
+ draining = false;
32
+ addEmitCallback(callback) {
33
+ this.callbacks.add(callback);
34
+ }
35
+ removeEmitCallback(callback) {
36
+ this.callbacks.delete(callback);
37
+ }
38
+ handle(message) {
39
+ this.queue.push(message);
40
+ if (!this.draining) {
41
+ this.draining = true;
42
+ setImmediate(() => this.flush());
43
+ }
44
+ }
45
+ getLogMessages() {
46
+ return this.buffer.getLogMessages();
47
+ }
48
+ flush() {
49
+ while (this.queue.length > 0) {
50
+ const message = this.queue.shift();
51
+ if (message === undefined) {
52
+ continue;
53
+ }
54
+ this.buffer.append(message);
55
+ for (const callback of this.callbacks) {
56
+ try {
57
+ callback(message);
58
+ }
59
+ catch {
60
+ // ignore listener errors to avoid breaking logging pipeline
61
+ }
62
+ }
63
+ }
64
+ this.draining = false;
65
+ }
66
+ }
67
+ class SerenaConsoleLogger {
68
+ level;
69
+ emitToConsole;
70
+ memoryHandler;
71
+ loggerName;
72
+ constructor(options) {
73
+ this.level = options.level;
74
+ this.emitToConsole = options.emitToConsole;
75
+ this.memoryHandler = options.memoryHandler;
76
+ this.loggerName = options.name ?? 'SerenaLogger';
77
+ }
78
+ trace(message, meta) {
79
+ this.log('trace', message, meta);
80
+ }
81
+ debug(message, meta) {
82
+ this.log('debug', message, meta);
83
+ }
84
+ info(message, meta) {
85
+ this.log('info', message, meta);
86
+ }
87
+ warn(message, meta) {
88
+ this.log('warn', message, meta);
89
+ }
90
+ error(message, meta) {
91
+ this.log('error', message, meta);
92
+ }
93
+ fatal(message, meta) {
94
+ this.log('fatal', message, meta);
95
+ }
96
+ setMemoryHandler(memoryHandler) {
97
+ this.memoryHandler = memoryHandler;
98
+ }
99
+ log(level, message, meta) {
100
+ if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
101
+ return;
102
+ }
103
+ const formatted = formatLogMessage({
104
+ timestamp: new Date(),
105
+ level,
106
+ message,
107
+ meta,
108
+ loggerName: this.loggerName
109
+ });
110
+ this.memoryHandler?.handle(formatted);
111
+ if (!this.emitToConsole || !consoleLoggingEnabled) {
112
+ return;
113
+ }
114
+ const consoleMessage = meta instanceof Error && meta.stack
115
+ ? `${formatted}\n${meta.stack}`
116
+ : formatted;
117
+ switch (level) {
118
+ case 'trace':
119
+ console.trace(consoleMessage);
120
+ break;
121
+ case 'debug':
122
+ console.debug(consoleMessage);
123
+ break;
124
+ case 'info':
125
+ console.info(consoleMessage);
126
+ break;
127
+ case 'warn':
128
+ console.warn(consoleMessage);
129
+ break;
130
+ case 'error':
131
+ case 'fatal':
132
+ console.error(consoleMessage);
133
+ break;
134
+ default:
135
+ console.log(consoleMessage);
136
+ }
137
+ }
138
+ }
139
+ const sharedLoggers = new Set();
140
+ let sharedMemoryHandler = null;
141
+ function getOrCreateSharedMemoryHandler() {
142
+ sharedMemoryHandler ??= new MemoryLogHandler();
143
+ return sharedMemoryHandler;
144
+ }
145
+ export function createSerenaLogger(options = {}) {
146
+ const useSharedHandler = options.memoryHandler === undefined;
147
+ const memoryHandler = useSharedHandler ? getOrCreateSharedMemoryHandler() : options.memoryHandler;
148
+ const emitToConsole = (options.emitToConsole ?? true) && consoleLoggingEnabled;
149
+ const loggerInstance = new SerenaConsoleLogger({
150
+ level: options.level ?? 'info',
151
+ emitToConsole,
152
+ memoryHandler,
153
+ name: options.name
154
+ });
155
+ if (useSharedHandler) {
156
+ sharedLoggers.add(loggerInstance);
157
+ }
158
+ return { logger: loggerInstance, memoryHandler };
159
+ }
160
+ export function formatLogMessage(message) {
161
+ const level = message.level.toUpperCase().padEnd(5).slice(0, 5);
162
+ const timestamp = message.timestamp.toISOString().replace('T', ' ').replace('Z', '');
163
+ const threadName = 'main';
164
+ const location = `${message.loggerName}:log`;
165
+ let formatted = SERENA_LOG_FORMAT.replace('%(levelname)-5s', level)
166
+ .replace('%(asctime)-15s', timestamp.padEnd(15).slice(0, 15))
167
+ .replace('%(threadName)s', threadName)
168
+ .replace('%(name)s', message.loggerName)
169
+ .replace('%(funcName)s', 'log')
170
+ .replace('%(lineno)d', '0')
171
+ .replace('%(message)s', enrichMessage(message.message, message.meta));
172
+ if (!SERENA_LOG_FORMAT.includes('%(name)s:%(funcName)s:%(lineno)d')) {
173
+ formatted = `${level} ${timestamp} [${threadName}] ${location} - ${enrichMessage(message.message, message.meta)}`;
174
+ }
175
+ return formatted;
176
+ }
177
+ function enrichMessage(message, meta) {
178
+ if (meta === undefined || meta === null) {
179
+ return message;
180
+ }
181
+ if (meta instanceof Error) {
182
+ return `${message} | error=${meta.message}`;
183
+ }
184
+ if (typeof meta === 'object') {
185
+ try {
186
+ return `${message} | ${JSON.stringify(meta)}`;
187
+ }
188
+ catch {
189
+ return `${message} | [unserializable meta]`;
190
+ }
191
+ }
192
+ if (typeof meta === 'string') {
193
+ return `${message} | ${meta}`;
194
+ }
195
+ if (typeof meta === 'number' || typeof meta === 'boolean' || typeof meta === 'bigint') {
196
+ return `${message} | ${meta.toString()}`;
197
+ }
198
+ if (typeof meta === 'symbol') {
199
+ return `${message} | ${meta.toString()}`;
200
+ }
201
+ if (typeof meta === 'function') {
202
+ return `${message} | [function]`;
203
+ }
204
+ return message;
205
+ }
@@ -0,0 +1,21 @@
1
+ export interface ShellCommandResult {
2
+ stdout: string;
3
+ stderr?: string;
4
+ returnCode: number;
5
+ cwd: string;
6
+ signal?: NodeJS.Signals;
7
+ }
8
+ export interface ExecuteShellCommandOptions {
9
+ cwd?: string;
10
+ captureStderr?: boolean;
11
+ env?: NodeJS.ProcessEnv;
12
+ }
13
+ export declare function executeShellCommand(command: string, { cwd, captureStderr, env }?: ExecuteShellCommandOptions): Promise<ShellCommandResult>;
14
+ export interface SubprocessCheckOutputOptions {
15
+ encoding?: BufferEncoding;
16
+ strip?: boolean;
17
+ timeoutMs?: number;
18
+ cwd?: string;
19
+ env?: NodeJS.ProcessEnv;
20
+ }
21
+ export declare function subprocessCheckOutput(args: string[], { encoding, strip, timeoutMs, cwd, env }?: SubprocessCheckOutputOptions): Promise<string>;