@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.
- package/LICENSE +22 -0
- package/README.md +244 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +7 -0
- package/dist/devtools/generate_prompt_factory.d.ts +5 -0
- package/dist/devtools/generate_prompt_factory.js +114 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +34 -0
- package/dist/interprompt/index.d.ts +2 -0
- package/dist/interprompt/index.js +1 -0
- package/dist/interprompt/jinja_template.d.ts +10 -0
- package/dist/interprompt/jinja_template.js +174 -0
- package/dist/interprompt/multilang_prompt.d.ts +54 -0
- package/dist/interprompt/multilang_prompt.js +302 -0
- package/dist/interprompt/prompt_factory.d.ts +16 -0
- package/dist/interprompt/prompt_factory.js +189 -0
- package/dist/interprompt/util/class_decorators.d.ts +1 -0
- package/dist/interprompt/util/class_decorators.js +1 -0
- package/dist/interprompt/util/index.d.ts +1 -0
- package/dist/interprompt/util/index.js +1 -0
- package/dist/serena/agent.d.ts +118 -0
- package/dist/serena/agent.js +675 -0
- package/dist/serena/agno.d.ts +111 -0
- package/dist/serena/agno.js +278 -0
- package/dist/serena/analytics.d.ts +24 -0
- package/dist/serena/analytics.js +119 -0
- package/dist/serena/cli.d.ts +9 -0
- package/dist/serena/cli.js +731 -0
- package/dist/serena/code_editor.d.ts +42 -0
- package/dist/serena/code_editor.js +239 -0
- package/dist/serena/config/context_mode.d.ts +41 -0
- package/dist/serena/config/context_mode.js +239 -0
- package/dist/serena/config/serena_config.d.ts +134 -0
- package/dist/serena/config/serena_config.js +718 -0
- package/dist/serena/constants.d.ts +18 -0
- package/dist/serena/constants.js +27 -0
- package/dist/serena/dashboard.d.ts +55 -0
- package/dist/serena/dashboard.js +472 -0
- package/dist/serena/generated/generated_prompt_factory.d.ts +27 -0
- package/dist/serena/generated/generated_prompt_factory.js +42 -0
- package/dist/serena/gui_log_viewer.d.ts +41 -0
- package/dist/serena/gui_log_viewer.js +436 -0
- package/dist/serena/mcp.d.ts +118 -0
- package/dist/serena/mcp.js +904 -0
- package/dist/serena/project.d.ts +62 -0
- package/dist/serena/project.js +321 -0
- package/dist/serena/prompt_factory.d.ts +20 -0
- package/dist/serena/prompt_factory.js +42 -0
- package/dist/serena/resources/config/contexts/agent.yml +8 -0
- package/dist/serena/resources/config/contexts/chatgpt.yml +28 -0
- package/dist/serena/resources/config/contexts/codex.yml +27 -0
- package/dist/serena/resources/config/contexts/context.template.yml +11 -0
- package/dist/serena/resources/config/contexts/desktop-app.yml +17 -0
- package/dist/serena/resources/config/contexts/ide-assistant.yml +26 -0
- package/dist/serena/resources/config/contexts/oaicompat-agent.yml +8 -0
- package/dist/serena/resources/config/internal_modes/jetbrains.yml +15 -0
- package/dist/serena/resources/config/modes/editing.yml +112 -0
- package/dist/serena/resources/config/modes/interactive.yml +11 -0
- package/dist/serena/resources/config/modes/mode.template.yml +7 -0
- package/dist/serena/resources/config/modes/no-onboarding.yml +8 -0
- package/dist/serena/resources/config/modes/onboarding.yml +16 -0
- package/dist/serena/resources/config/modes/one-shot.yml +15 -0
- package/dist/serena/resources/config/modes/planning.yml +15 -0
- package/dist/serena/resources/config/prompt_templates/simple_tool_outputs.yml +75 -0
- package/dist/serena/resources/config/prompt_templates/system_prompt.yml +66 -0
- package/dist/serena/resources/dashboard/dashboard.js +815 -0
- package/dist/serena/resources/dashboard/index.html +314 -0
- package/dist/serena/resources/dashboard/jquery.min.js +3 -0
- package/dist/serena/resources/dashboard/serena-icon-16.png +0 -0
- package/dist/serena/resources/dashboard/serena-icon-32.png +0 -0
- package/dist/serena/resources/dashboard/serena-icon-48.png +0 -0
- package/dist/serena/resources/dashboard/serena-logs-dark-mode.png +0 -0
- package/dist/serena/resources/dashboard/serena-logs.png +0 -0
- package/dist/serena/resources/project.template.yml +67 -0
- package/dist/serena/resources/serena_config.template.yml +85 -0
- package/dist/serena/symbol.d.ts +199 -0
- package/dist/serena/symbol.js +616 -0
- package/dist/serena/text_utils.d.ts +51 -0
- package/dist/serena/text_utils.js +267 -0
- package/dist/serena/tools/cmd_tools.d.ts +31 -0
- package/dist/serena/tools/cmd_tools.js +48 -0
- package/dist/serena/tools/config_tools.d.ts +53 -0
- package/dist/serena/tools/config_tools.js +176 -0
- package/dist/serena/tools/file_tools.d.ts +231 -0
- package/dist/serena/tools/file_tools.js +511 -0
- package/dist/serena/tools/index.d.ts +7 -0
- package/dist/serena/tools/index.js +7 -0
- package/dist/serena/tools/memory_tools.d.ts +60 -0
- package/dist/serena/tools/memory_tools.js +135 -0
- package/dist/serena/tools/symbol_tools.d.ts +165 -0
- package/dist/serena/tools/symbol_tools.js +362 -0
- package/dist/serena/tools/tools_base.d.ts +162 -0
- package/dist/serena/tools/tools_base.js +378 -0
- package/dist/serena/tools/workflow_tools.d.ts +35 -0
- package/dist/serena/tools/workflow_tools.js +161 -0
- package/dist/serena/util/class_decorators.d.ts +7 -0
- package/dist/serena/util/class_decorators.js +37 -0
- package/dist/serena/util/exception.d.ts +8 -0
- package/dist/serena/util/exception.js +53 -0
- package/dist/serena/util/file_system.d.ts +30 -0
- package/dist/serena/util/file_system.js +352 -0
- package/dist/serena/util/general.d.ts +11 -0
- package/dist/serena/util/general.js +42 -0
- package/dist/serena/util/git.d.ts +11 -0
- package/dist/serena/util/git.js +37 -0
- package/dist/serena/util/inspection.d.ts +45 -0
- package/dist/serena/util/inspection.js +221 -0
- package/dist/serena/util/logging.d.ts +46 -0
- package/dist/serena/util/logging.js +205 -0
- package/dist/serena/util/shell.d.ts +21 -0
- package/dist/serena/util/shell.js +95 -0
- package/dist/serena/util/thread.d.ts +23 -0
- package/dist/serena/util/thread.js +88 -0
- package/dist/serena/version.d.ts +1 -0
- package/dist/serena/version.js +23 -0
- package/dist/solidlsp/language_servers/autoload.d.ts +23 -0
- package/dist/solidlsp/language_servers/autoload.js +25 -0
- package/dist/solidlsp/language_servers/bash_language_server.d.ts +10 -0
- package/dist/solidlsp/language_servers/bash_language_server.js +64 -0
- package/dist/solidlsp/language_servers/clangd_language_server.d.ts +13 -0
- package/dist/solidlsp/language_servers/clangd_language_server.js +110 -0
- package/dist/solidlsp/language_servers/clojure_lsp.d.ts +13 -0
- package/dist/solidlsp/language_servers/clojure_lsp.js +137 -0
- package/dist/solidlsp/language_servers/common.d.ts +41 -0
- package/dist/solidlsp/language_servers/common.js +365 -0
- package/dist/solidlsp/language_servers/csharp_language_server.d.ts +21 -0
- package/dist/solidlsp/language_servers/csharp_language_server.js +694 -0
- package/dist/solidlsp/language_servers/dart_language_server.d.ts +10 -0
- package/dist/solidlsp/language_servers/dart_language_server.js +122 -0
- package/dist/solidlsp/language_servers/eclipse_jdtls.d.ts +24 -0
- package/dist/solidlsp/language_servers/eclipse_jdtls.js +671 -0
- package/dist/solidlsp/language_servers/erlang_language_server.d.ts +22 -0
- package/dist/solidlsp/language_servers/erlang_language_server.js +327 -0
- package/dist/solidlsp/language_servers/gopls.d.ts +12 -0
- package/dist/solidlsp/language_servers/gopls.js +59 -0
- package/dist/solidlsp/language_servers/intelephense.d.ts +13 -0
- package/dist/solidlsp/language_servers/intelephense.js +121 -0
- package/dist/solidlsp/language_servers/jedi_server.d.ts +18 -0
- package/dist/solidlsp/language_servers/jedi_server.js +234 -0
- package/dist/solidlsp/language_servers/kotlin_language_server.d.ts +19 -0
- package/dist/solidlsp/language_servers/kotlin_language_server.js +474 -0
- package/dist/solidlsp/language_servers/lua_ls.d.ts +18 -0
- package/dist/solidlsp/language_servers/lua_ls.js +319 -0
- package/dist/solidlsp/language_servers/nixd_language_server.d.ts +17 -0
- package/dist/solidlsp/language_servers/nixd_language_server.js +341 -0
- package/dist/solidlsp/language_servers/pyright_server.d.ts +19 -0
- package/dist/solidlsp/language_servers/pyright_server.js +180 -0
- package/dist/solidlsp/language_servers/r_language_server.d.ts +19 -0
- package/dist/solidlsp/language_servers/r_language_server.js +184 -0
- package/dist/solidlsp/language_servers/ruby_common.d.ts +10 -0
- package/dist/solidlsp/language_servers/ruby_common.js +136 -0
- package/dist/solidlsp/language_servers/ruby_lsp.d.ts +18 -0
- package/dist/solidlsp/language_servers/ruby_lsp.js +230 -0
- package/dist/solidlsp/language_servers/rust_analyzer.d.ts +13 -0
- package/dist/solidlsp/language_servers/rust_analyzer.js +96 -0
- package/dist/solidlsp/language_servers/solargraph.d.ts +18 -0
- package/dist/solidlsp/language_servers/solargraph.js +208 -0
- package/dist/solidlsp/language_servers/sourcekit_lsp.d.ts +24 -0
- package/dist/solidlsp/language_servers/sourcekit_lsp.js +449 -0
- package/dist/solidlsp/language_servers/terraform_ls.d.ts +13 -0
- package/dist/solidlsp/language_servers/terraform_ls.js +139 -0
- package/dist/solidlsp/language_servers/typescript_language_server.d.ts +20 -0
- package/dist/solidlsp/language_servers/typescript_language_server.js +237 -0
- package/dist/solidlsp/language_servers/vts_language_server.d.ts +13 -0
- package/dist/solidlsp/language_servers/vts_language_server.js +121 -0
- package/dist/solidlsp/language_servers/zls.d.ts +20 -0
- package/dist/solidlsp/language_servers/zls.js +254 -0
- package/dist/solidlsp/ls.d.ts +197 -0
- package/dist/solidlsp/ls.js +507 -0
- package/dist/solidlsp/ls_config.d.ts +43 -0
- package/dist/solidlsp/ls_config.js +157 -0
- package/dist/solidlsp/ls_exceptions.d.ts +5 -0
- package/dist/solidlsp/ls_exceptions.js +14 -0
- package/dist/solidlsp/ls_handler.d.ts +54 -0
- package/dist/solidlsp/ls_handler.js +406 -0
- package/dist/solidlsp/ls_request.d.ts +31 -0
- package/dist/solidlsp/ls_request.js +42 -0
- package/dist/solidlsp/ls_types.d.ts +7 -0
- package/dist/solidlsp/ls_types.js +8 -0
- package/dist/solidlsp/lsp_protocol_handler/server.d.ts +61 -0
- package/dist/solidlsp/lsp_protocol_handler/server.js +68 -0
- package/dist/solidlsp/util/subprocess_util.d.ts +6 -0
- package/dist/solidlsp/util/subprocess_util.js +11 -0
- package/dist/solidlsp/util/zip.d.ts +25 -0
- package/dist/solidlsp/util/zip.js +188 -0
- 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>;
|