skill-any-code 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/adapters/command.schemas.js +18 -0
  2. package/dist/application/analysis.app.service.js +264 -0
  3. package/dist/application/bootstrap.js +21 -0
  4. package/dist/application/services/llm.analysis.service.js +170 -0
  5. package/dist/common/config.js +213 -0
  6. package/dist/common/constants.js +11 -0
  7. package/dist/common/errors.js +37 -0
  8. package/dist/common/logger.js +77 -0
  9. package/dist/common/types.js +2 -0
  10. package/dist/common/ui.js +201 -0
  11. package/dist/common/utils.js +117 -0
  12. package/dist/domain/index.js +17 -0
  13. package/dist/domain/interfaces.js +2 -0
  14. package/dist/domain/services/analysis.service.js +696 -0
  15. package/dist/domain/services/incremental.service.js +81 -0
  16. package/dist/infrastructure/blacklist.service.js +71 -0
  17. package/dist/infrastructure/cache/file.hash.cache.js +140 -0
  18. package/dist/infrastructure/git/git.service.js +159 -0
  19. package/dist/infrastructure/git.service.js +157 -0
  20. package/dist/infrastructure/index.service.js +108 -0
  21. package/dist/infrastructure/llm/llm.usage.tracker.js +58 -0
  22. package/dist/infrastructure/llm/openai.client.js +141 -0
  23. package/{src/infrastructure/llm/prompt.template.ts → dist/infrastructure/llm/prompt.template.js} +31 -36
  24. package/dist/infrastructure/llm.service.js +61 -0
  25. package/dist/infrastructure/skill/skill.generator.js +83 -0
  26. package/{src/infrastructure/skill/templates/resolve.script.ts → dist/infrastructure/skill/templates/resolve.script.js} +18 -15
  27. package/dist/infrastructure/skill/templates/skill.md.template.js +47 -0
  28. package/dist/infrastructure/splitter/code.splitter.js +137 -0
  29. package/dist/infrastructure/storage.service.js +409 -0
  30. package/dist/infrastructure/worker-pool/parse.worker.impl.js +137 -0
  31. package/dist/infrastructure/worker-pool/parse.worker.js +43 -0
  32. package/dist/infrastructure/worker-pool/worker-pool.service.js +171 -0
  33. package/package.json +5 -1
  34. package/jest.config.js +0 -27
  35. package/src/adapters/command.schemas.ts +0 -21
  36. package/src/application/analysis.app.service.ts +0 -272
  37. package/src/application/bootstrap.ts +0 -35
  38. package/src/application/services/llm.analysis.service.ts +0 -237
  39. package/src/cli.ts +0 -297
  40. package/src/common/config.ts +0 -209
  41. package/src/common/constants.ts +0 -8
  42. package/src/common/errors.ts +0 -34
  43. package/src/common/logger.ts +0 -82
  44. package/src/common/types.ts +0 -385
  45. package/src/common/ui.ts +0 -228
  46. package/src/common/utils.ts +0 -81
  47. package/src/domain/index.ts +0 -1
  48. package/src/domain/interfaces.ts +0 -188
  49. package/src/domain/services/analysis.service.ts +0 -735
  50. package/src/domain/services/incremental.service.ts +0 -50
  51. package/src/index.ts +0 -6
  52. package/src/infrastructure/blacklist.service.ts +0 -37
  53. package/src/infrastructure/cache/file.hash.cache.ts +0 -119
  54. package/src/infrastructure/git/git.service.ts +0 -120
  55. package/src/infrastructure/git.service.ts +0 -121
  56. package/src/infrastructure/index.service.ts +0 -94
  57. package/src/infrastructure/llm/llm.usage.tracker.ts +0 -65
  58. package/src/infrastructure/llm/openai.client.ts +0 -162
  59. package/src/infrastructure/llm.service.ts +0 -70
  60. package/src/infrastructure/skill/skill.generator.ts +0 -53
  61. package/src/infrastructure/skill/templates/skill.md.template.ts +0 -45
  62. package/src/infrastructure/splitter/code.splitter.ts +0 -176
  63. package/src/infrastructure/storage.service.ts +0 -413
  64. package/src/infrastructure/worker-pool/parse.worker.impl.ts +0 -135
  65. package/src/infrastructure/worker-pool/parse.worker.ts +0 -9
  66. package/src/infrastructure/worker-pool/worker-pool.service.ts +0 -173
  67. package/tsconfig.json +0 -24
  68. package/tsconfig.test.json +0 -5
@@ -1,209 +0,0 @@
1
- import fs from 'fs-extra';
2
- import yaml from 'js-yaml';
3
- import path from 'path';
4
- import os from 'os';
5
- import { z } from 'zod';
6
- import { DEFAULT_CONCURRENCY, DEFAULT_OUTPUT_DIR } from './constants';
7
- import { logger } from './logger';
8
- import { AppError, ErrorCode } from './errors';
9
-
10
- // V2.5:全局黑名单默认值(.gitignore 语法)
11
- const DEFAULT_BLACKLIST = [
12
- '*.yml', '*.yaml', '*.ini', '*.cfg', '*.json', '*.toml',
13
- '*.md', '*.txt', '*.rst',
14
- 'README*', 'LICENSE*', 'CHANGELOG*',
15
- '*.lock', 'package-lock.json', 'yarn.lock',
16
- '*.env*', 'credentials.*', '*.pem', '*.key',
17
- // 图片与图标等常见二进制资源(需求文档 13.3 / 测试文档 16.6)
18
- '*.png', '*.jpg', '*.jpeg', '*.gif',
19
- '*.bmp', '*.svg', '*.webp', '*.ico',
20
- 'docs/', 'dist/', 'build/', 'coverage/',
21
- 'node_modules/', '.git/', '.skill-any-code-result/',
22
- '.agents/', '.claude/', '.gitignore'
23
- ];
24
-
25
- const DEFAULT_PROVIDERS = ['opencode', 'cursor', 'claude', 'codex'];
26
-
27
- const ConfigSchema = z.object({
28
- global: z.object({
29
- log_level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
30
- output_format: z.enum(['text', 'json', 'markdown']).default('text'),
31
- auto_confirm: z.boolean().default(false),
32
- output_dir: z.string().default(DEFAULT_OUTPUT_DIR),
33
- }).default({}),
34
- analyze: z.object({
35
- default_mode: z.enum(['full', 'incremental', 'auto']).default('auto'),
36
- /**
37
- * 最大并发数上限:
38
- * - 实际默认并发 = CPU核心数 * 2;
39
- * - 若 CPU*2 > max_concurrency,则使用 max_concurrency 作为上限。
40
- */
41
- max_concurrency: z.number().default(DEFAULT_CONCURRENCY),
42
- default_depth: z.number().default(-1),
43
- blacklist: z.array(z.string()).default(DEFAULT_BLACKLIST),
44
- }).default({}),
45
- skills: z.object({
46
- default_providers: z.array(z.string()).default(DEFAULT_PROVIDERS),
47
- }).default({}),
48
- llm: z.object({
49
- // V2.5:默认配置中不再内置具体远程服务与模型,避免误上传代码(需求文档 13.4.1)
50
- base_url: z.string().default(''),
51
- api_key: z.string().default(''),
52
- model: z.string().default(''),
53
- temperature: z.number().min(0).max(2).default(0.1),
54
- max_tokens: z.number().int().min(100).default(4000),
55
- timeout: z.number().int().min(1000).default(60000),
56
- max_retries: z.number().int().min(0).default(3),
57
- retry_delay: z.number().int().min(100).default(1000),
58
- context_window_size: z.number().int().min(1000).default(128000),
59
- cache_enabled: z.boolean().default(true),
60
- cache_dir: z.string().default('~/.cache/skill-any-code/llm'),
61
- // V2.5:新增缓存容量上限(MB),0 表示禁用磁盘缓存(需求文档 13.5.2)
62
- cache_max_size_mb: z.number().int().min(0).default(500),
63
- }).default({}),
64
- });
65
-
66
- export type Config = z.infer<typeof ConfigSchema>;
67
-
68
- class ConfigManager {
69
- private config: Config | null = null;
70
- private configPath: string = '';
71
-
72
- private getDefaultConfigPath(): string {
73
- const home = process.env.HOME || process.env.USERPROFILE || '';
74
- return path.join(home, '.config', 'skill-any-code', 'config.yaml');
75
- }
76
-
77
- private expandTilde(pathStr: string): string {
78
- if (pathStr.startsWith('~') && (pathStr.length === 1 || pathStr[1] === '/' || pathStr[1] === '\\')) {
79
- const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
80
- return path.join(home, pathStr.slice(1));
81
- }
82
- return pathStr;
83
- }
84
-
85
- /**
86
- * 加载配置文件。
87
- *
88
- * V2.5 起,当目标配置文件不存在时:
89
- * - 不再自动创建磁盘文件(由 init 子命令负责);
90
- * - 抛出带有 ErrorCode.CONFIG_NOT_INITIALIZED 的 AppError,表示「配置未初始化」,
91
- * 由 CLI 层捕获并提示用户先执行 init(测试文档 16.3 / 16.1.1)。
92
- */
93
- async load(customPath?: string): Promise<Config> {
94
- this.configPath = customPath ? this.expandTilde(customPath) : this.getDefaultConfigPath();
95
-
96
- // 1. 加载默认配置(作为解析/校验基线)
97
- let config = ConfigSchema.parse({});
98
-
99
- // 2. 加载配置文件;不存在时交由上层处理「未初始化」状态
100
- if (await fs.pathExists(this.configPath)) {
101
- try {
102
- const fileContent = await fs.readFile(this.configPath, 'utf-8');
103
- const fileConfig = yaml.load(fileContent) as Record<string, unknown>;
104
- config = ConfigSchema.parse(fileConfig);
105
- logger.debug(`Loaded config file: ${this.configPath}`);
106
- } catch (error) {
107
- logger.warn(`Failed to parse config file. Using defaults: ${(error as Error).message}`);
108
- }
109
- } else {
110
- // 配置文件不存在:不再隐式创建,由 CLI 的 init 子命令负责初始化
111
- const defaultConfig = ConfigSchema.parse({});
112
- this.config = defaultConfig;
113
- throw new AppError(
114
- ErrorCode.CONFIG_NOT_INITIALIZED,
115
- `Config is not initialized: ${this.configPath}`,
116
- );
117
- }
118
-
119
- // 3. 加载环境变量
120
- const envConfig: any = {};
121
- Object.entries(process.env).forEach(([key, value]) => {
122
- if (!key.startsWith('SKILL_ANY_CODE_')) return;
123
- const configKey = key.replace('SKILL_ANY_CODE_', '').toLowerCase();
124
-
125
- if (!envConfig.global) envConfig.global = {};
126
- if (configKey === 'log_level' && value) envConfig.global.log_level = value;
127
- if (configKey === 'output_format' && value) envConfig.global.output_format = value;
128
- if (configKey === 'auto_confirm' && value) envConfig.global.auto_confirm = value === 'true';
129
- if (configKey === 'output_dir' && value) envConfig.global.output_dir = value;
130
-
131
- if (!envConfig.analyze) envConfig.analyze = {};
132
- if (configKey === 'analyze_default_mode' && value) envConfig.analyze.default_mode = value;
133
- if (configKey === 'analyze_max_concurrency' && value) envConfig.analyze.max_concurrency = Number(value);
134
- if (configKey === 'analyze_default_depth' && value) envConfig.analyze.default_depth = Number(value);
135
-
136
- if (!envConfig.skills) envConfig.skills = {};
137
- if (configKey === 'skills_default_providers' && value) {
138
- envConfig.skills.default_providers = value.split(',').map((s: string) => s.trim());
139
- }
140
-
141
- if (!envConfig.llm) envConfig.llm = {};
142
- if (configKey === 'llm_base_url' && value) envConfig.llm.base_url = value;
143
- if (configKey === 'llm_api_key' && value) envConfig.llm.api_key = value;
144
- if (configKey === 'llm_model' && value) envConfig.llm.model = value;
145
- if (configKey === 'llm_temperature' && value) envConfig.llm.temperature = Number(value);
146
- if (configKey === 'llm_max_tokens' && value) envConfig.llm.max_tokens = Number(value);
147
- if (configKey === 'llm_timeout' && value) envConfig.llm.timeout = Number(value);
148
- if (configKey === 'llm_max_retries' && value) envConfig.llm.max_retries = Number(value);
149
- if (configKey === 'llm_retry_delay' && value) envConfig.llm.retry_delay = Number(value);
150
- if (configKey === 'llm_context_window_size' && value) envConfig.llm.context_window_size = Number(value);
151
- if (configKey === 'llm_cache_enabled' && value) envConfig.llm.cache_enabled = value === 'true';
152
- if (configKey === 'llm_cache_dir' && value) envConfig.llm.cache_dir = value;
153
- if (configKey === 'llm_cache_max_size_mb' && value) envConfig.llm.cache_max_size_mb = Number(value);
154
- });
155
-
156
- // 注意:配置为嵌套对象,环境变量覆盖需要做“深合并”,否则只设置某个 llm 子字段
157
- // 会导致整个 llm 段被浅覆盖成不完整对象(从而触发 LLM_INVALID_CONFIG)。
158
- config = ConfigSchema.parse({
159
- ...config,
160
- global: { ...config.global, ...(envConfig.global ?? {}) },
161
- analyze: { ...config.analyze, ...(envConfig.analyze ?? {}) },
162
- skills: { ...config.skills, ...(envConfig.skills ?? {}) },
163
- llm: { ...config.llm, ...(envConfig.llm ?? {}) },
164
- });
165
-
166
- this.config = config;
167
- return config;
168
- }
169
-
170
- getConfig(): Config {
171
- if (!this.config) {
172
- throw new Error('Config not loaded. Call load() first.');
173
- }
174
- return this.config;
175
- }
176
-
177
- /**
178
- * 显式初始化配置文件内容。
179
- *
180
- * - 当目标文件不存在时:创建目录并写入默认配置;
181
- * - 当目标文件已存在时:直接覆盖为默认配置(是否覆盖由上层 CLI 交互确认)。
182
- */
183
- async init(customPath?: string): Promise<void> {
184
- this.configPath = customPath ? this.expandTilde(customPath) : this.getDefaultConfigPath();
185
- const defaultConfig = ConfigSchema.parse({});
186
-
187
- await fs.ensureDir(path.dirname(this.configPath));
188
- await fs.writeFile(this.configPath, yaml.dump(defaultConfig), 'utf-8');
189
- this.config = defaultConfig;
190
- logger.debug(`Initialized config file: ${this.configPath}`);
191
- }
192
-
193
- async save(config: Partial<Config>): Promise<void> {
194
- const currentConfig = this.config || await this.load();
195
- const mergedConfig = { ...currentConfig, ...config };
196
- const validatedConfig = ConfigSchema.parse(mergedConfig);
197
-
198
- await fs.ensureDir(path.dirname(this.configPath));
199
- await fs.writeFile(this.configPath, yaml.dump(validatedConfig), 'utf-8');
200
- this.config = validatedConfig;
201
- }
202
-
203
- async reset(): Promise<void> {
204
- const defaultConfig = ConfigSchema.parse({});
205
- await this.save(defaultConfig);
206
- }
207
- }
208
-
209
- export const configManager = new ConfigManager();
@@ -1,8 +0,0 @@
1
- export const ANALYSIS_VERSION = '1.0.0'
2
- export const SCHEMA_VERSION = '1.0'
3
- export const MAX_GIT_COMMITS_HISTORY = 50
4
- export const DEFAULT_CONCURRENCY = require('os').cpus().length * 2
5
- // 所有文本代码文件都支持,无后缀限制,由LLM自动识别语言
6
- export const SUPPORTED_EXTENSIONS = ['*']
7
- // V2.3:默认黑名单已迁移至 config.ts 的 DEFAULT_BLACKLIST
8
- export const DEFAULT_OUTPUT_DIR = './.skill-any-code-result'
@@ -1,34 +0,0 @@
1
- export enum ErrorCode {
2
- SUCCESS = 200,
3
- PARAM_VALIDATION_FAILED = 4000,
4
- PROJECT_PATH_NOT_EXIST = 4001,
5
- NO_PARSER_AVAILABLE = 4002,
6
- INCREMENTAL_NOT_AVAILABLE = 4003,
7
- ANALYSIS_NOT_EXIST = 4004,
8
- CONFIG_NOT_INITIALIZED = 4005,
9
- ANALYSIS_EXCEPTION = 5000,
10
- GIT_OPERATION_FAILED = 5001,
11
- STORAGE_WRITE_FAILED = 5002,
12
- WORKER_SCHEDULE_FAILED = 5003,
13
- LLM_CALL_FAILED = 5010,
14
- LLM_RESPONSE_PARSE_FAILED = 5011,
15
- LLM_RATE_LIMITED = 5012,
16
- LLM_TIMEOUT = 5013,
17
- LLM_INVALID_CONFIG = 5014,
18
- FILE_TOO_LARGE = 5020,
19
- FILE_SPLIT_FAILED = 5021,
20
- CHUNK_MERGE_FAILED = 5022,
21
- }
22
-
23
- export class AppError extends Error {
24
- code: ErrorCode
25
- details?: any
26
-
27
- constructor(code: ErrorCode, message: string, details?: any) {
28
- super(message)
29
- this.name = 'AppError'
30
- this.code = code
31
- this.details = details
32
- Object.setPrototypeOf(this, AppError.prototype)
33
- }
34
- }
@@ -1,82 +0,0 @@
1
- import pc from 'picocolors';
2
-
3
- export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
4
-
5
- const LOG_LEVELS: Record<LogLevel, number> = {
6
- debug: 0,
7
- info: 1,
8
- warn: 2,
9
- error: 3,
10
- };
11
-
12
- type LogSink = (line: string, isError?: boolean) => void;
13
-
14
- class Logger {
15
- private level: LogLevel = 'info';
16
-
17
- // 可选的外部日志接收器(例如 CLI 多行渲染器),用于实现“日志固定在进度块下方”。
18
- private sink?: LogSink;
19
-
20
- setLevel(level: LogLevel) {
21
- this.level = level;
22
- }
23
-
24
- setSink(sink?: LogSink) {
25
- this.sink = sink;
26
- }
27
-
28
- private shouldLog(level: LogLevel): boolean {
29
- return LOG_LEVELS[level] >= LOG_LEVELS[this.level];
30
- }
31
-
32
- private write(line: string, isError: boolean) {
33
- if (this.sink) {
34
- this.sink(line, isError);
35
- return;
36
- }
37
- if (isError) {
38
- // stderr 不做颜色降级,交给调用方控制;这里保留 ANSI 颜色。
39
- // eslint-disable-next-line no-console
40
- console.error(line);
41
- } else {
42
- // eslint-disable-next-line no-console
43
- console.log(line);
44
- }
45
- }
46
-
47
- debug(message: string, ...args: any[]) {
48
- if (!this.shouldLog('debug')) return;
49
- const line = pc.gray(`[DEBUG] ${message}${args.length ? ' ' + args.join(' ') : ''}`);
50
- this.write(line, false);
51
- }
52
-
53
- info(message: string, ...args: any[]) {
54
- if (!this.shouldLog('info')) return;
55
- const line = pc.blue(`[INFO] ${message}${args.length ? ' ' + args.join(' ') : ''}`);
56
- this.write(line, false);
57
- }
58
-
59
- success(message: string, ...args: any[]) {
60
- if (!this.shouldLog('info')) return;
61
- const line = pc.green(`[SUCCESS] ${message}${args.length ? ' ' + args.join(' ') : ''}`);
62
- this.write(line, false);
63
- }
64
-
65
- warn(message: string, ...args: any[]) {
66
- if (!this.shouldLog('warn')) return;
67
- const line = pc.yellow(`[WARN] ${message}${args.length ? ' ' + args.join(' ') : ''}`);
68
- this.write(line, false);
69
- }
70
-
71
- error(message: string, error?: Error, ...args: any[]) {
72
- if (!this.shouldLog('error')) return;
73
- const base = pc.red(`[ERROR] ${message}${args.length ? ' ' + args.join(' ') : ''}`);
74
- this.write(base, true);
75
- if (error && this.level === 'debug') {
76
- const stackLine = pc.red(error.stack || String(error));
77
- this.write(stackLine, true);
78
- }
79
- }
80
- }
81
-
82
- export const logger = new Logger();