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
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.IndexService = void 0;
37
+ const fs = __importStar(require("fs-extra"));
38
+ const path = __importStar(require("path"));
39
+ const utils_1 = require("../common/utils");
40
+ // V2.6 起不再在主流程生成/依赖 analysis-index.json。
41
+ // 为兼容历史代码/测试,此类暂保留,但不再实现 IIndexService 接口。
42
+ class IndexService {
43
+ getIndexFilePath(storageRoot) {
44
+ return path.join(storageRoot, 'analysis-index.json');
45
+ }
46
+ async buildIndex(projectRoot, storageRoot, fileEntries, dirEntries) {
47
+ const entries = {};
48
+ for (const entry of fileEntries) {
49
+ entries[(0, utils_1.normalizePath)(entry.sourcePath)] = {
50
+ resultPath: (0, utils_1.normalizePath)(entry.resultPath),
51
+ type: 'file',
52
+ };
53
+ }
54
+ for (const entry of dirEntries) {
55
+ entries[(0, utils_1.normalizePath)(entry.sourcePath)] = {
56
+ resultPath: (0, utils_1.normalizePath)(entry.resultPath),
57
+ type: 'directory',
58
+ };
59
+ }
60
+ const indexData = {
61
+ version: '1.0',
62
+ projectRoot: (0, utils_1.normalizePath)(projectRoot),
63
+ storageRoot: (0, utils_1.normalizePath)(storageRoot),
64
+ generatedAt: new Date().toISOString(),
65
+ entries,
66
+ };
67
+ const indexPath = this.getIndexFilePath(storageRoot);
68
+ await fs.ensureDir(path.dirname(indexPath));
69
+ await fs.writeJson(indexPath, indexData, { spaces: 2 });
70
+ }
71
+ async updateIndex(storageRoot, updatedEntries, removedPaths) {
72
+ const existing = (await this.readIndex(storageRoot)) ?? {
73
+ version: '1.0',
74
+ projectRoot: '',
75
+ storageRoot,
76
+ generatedAt: new Date().toISOString(),
77
+ entries: {},
78
+ };
79
+ for (const removedPath of removedPaths) {
80
+ delete existing.entries[(0, utils_1.normalizePath)(removedPath)];
81
+ }
82
+ for (const entry of updatedEntries) {
83
+ existing.entries[(0, utils_1.normalizePath)(entry.sourcePath)] = {
84
+ resultPath: (0, utils_1.normalizePath)(entry.resultPath),
85
+ type: entry.type,
86
+ };
87
+ }
88
+ existing.generatedAt = new Date().toISOString();
89
+ const indexPath = this.getIndexFilePath(storageRoot);
90
+ await fs.writeJson(indexPath, existing, { spaces: 2 });
91
+ }
92
+ async readIndex(storageRoot) {
93
+ const indexPath = this.getIndexFilePath(storageRoot);
94
+ if (await fs.pathExists(indexPath)) {
95
+ return await fs.readJson(indexPath);
96
+ }
97
+ return null;
98
+ }
99
+ async resolve(storageRoot, absolutePath) {
100
+ const indexData = await this.readIndex(storageRoot);
101
+ if (!indexData)
102
+ return null;
103
+ const normalized = (0, utils_1.normalizePath)(absolutePath);
104
+ const entry = indexData.entries[normalized];
105
+ return entry ? entry.resultPath : null;
106
+ }
107
+ }
108
+ exports.IndexService = IndexService;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LLMUsageTracker = void 0;
4
+ /**
5
+ * 单次解析周期内的 LLM Token 使用统计器。
6
+ *
7
+ * - 对所有调用做累计统计;
8
+ * - 通过 onSnapshot 回调向上层(如 CLI 渲染器)推送最新快照;
9
+ * - 不直接向 stdout 打印 Token 行,由 CLI 统一渲染「Tokens: ...」。
10
+ */
11
+ class LLMUsageTracker {
12
+ onSnapshot;
13
+ stats = {
14
+ totalPromptTokens: 0,
15
+ totalCompletionTokens: 0,
16
+ totalTokens: 0,
17
+ totalCalls: 0,
18
+ };
19
+ constructor(onSnapshot) {
20
+ this.onSnapshot = onSnapshot;
21
+ }
22
+ addUsage(usage) {
23
+ this.stats.totalPromptTokens += usage.promptTokens ?? 0;
24
+ this.stats.totalCompletionTokens += usage.completionTokens ?? 0;
25
+ this.stats.totalTokens += usage.totalTokens ?? 0;
26
+ this.stats.totalCalls += 1;
27
+ const snapshot = this.getStats();
28
+ if (this.onSnapshot) {
29
+ this.onSnapshot(snapshot);
30
+ }
31
+ }
32
+ /**
33
+ * 将外部汇总(例如 worker 线程返回的本任务 usage delta)累加到总量中。
34
+ * 注意:这里的 totalCalls 表示调用次数增量,而不是“任务数”。
35
+ */
36
+ addTotals(delta) {
37
+ this.stats.totalPromptTokens += delta.totalPromptTokens ?? 0;
38
+ this.stats.totalCompletionTokens += delta.totalCompletionTokens ?? 0;
39
+ this.stats.totalTokens += delta.totalTokens ?? 0;
40
+ this.stats.totalCalls += delta.totalCalls ?? 0;
41
+ const snapshot = this.getStats();
42
+ if (this.onSnapshot) {
43
+ this.onSnapshot(snapshot);
44
+ }
45
+ }
46
+ getStats() {
47
+ return { ...this.stats };
48
+ }
49
+ reset() {
50
+ this.stats = {
51
+ totalPromptTokens: 0,
52
+ totalCompletionTokens: 0,
53
+ totalTokens: 0,
54
+ totalCalls: 0,
55
+ };
56
+ }
57
+ }
58
+ exports.LLMUsageTracker = LLMUsageTracker;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OpenAIClient = void 0;
7
+ const openai_1 = __importDefault(require("openai"));
8
+ const errors_1 = require("../../common/errors");
9
+ const logger_1 = require("../../common/logger");
10
+ class OpenAIClient {
11
+ client;
12
+ config;
13
+ tracker;
14
+ constructor(config, tracker) {
15
+ this.config = config;
16
+ this.tracker = tracker;
17
+ this.client = new openai_1.default({
18
+ apiKey: config.api_key,
19
+ baseURL: config.base_url,
20
+ timeout: config.timeout,
21
+ dangerouslyAllowBrowser: true,
22
+ });
23
+ }
24
+ /**
25
+ * 连接可用性校验(V2.5)
26
+ * - 在进入任何解析流程前调用;
27
+ * - 配置不完整或服务不可用时抛出带有明确 ErrorCode 的 AppError。
28
+ */
29
+ async testConnection(config) {
30
+ // 保持与最新配置一致(允许运行时通过 CLI/环境变量覆盖)
31
+ this.config = config;
32
+ // 基本配置校验:base_url / api_key / model 不能为空
33
+ if (!this.config.base_url || !this.config.api_key || !this.config.model) {
34
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_INVALID_CONFIG, 'Incomplete LLM config. Please set base_url/api_key/model via config file, env vars, or CLI options.', {
35
+ missing: {
36
+ base_url: !this.config.base_url,
37
+ api_key: !this.config.api_key,
38
+ model: !this.config.model,
39
+ },
40
+ });
41
+ }
42
+ try {
43
+ const res = await this.client.chat.completions.create({
44
+ model: this.config.model,
45
+ temperature: 0,
46
+ max_tokens: 1,
47
+ messages: [{ role: 'system', content: 'health-check' }],
48
+ });
49
+ const status = res.status ?? 200;
50
+ if (status === 401) {
51
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_CALL_FAILED, 'LLM authentication failed (401)', { status });
52
+ }
53
+ if (status === 404) {
54
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_CALL_FAILED, 'LLM model not found (404)', { status });
55
+ }
56
+ if (status < 200 || status >= 300) {
57
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_CALL_FAILED, `LLM connectivity check returned non-2xx status: ${status}`, { status });
58
+ }
59
+ }
60
+ catch (e) {
61
+ if (e instanceof errors_1.AppError) {
62
+ throw e;
63
+ }
64
+ const code = e?.code || e?.status;
65
+ if (code === 'ETIMEDOUT') {
66
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_TIMEOUT, 'LLM connectivity check timed out', e);
67
+ }
68
+ if (code === 'ENOTFOUND' || code === 'ECONNREFUSED' || code === 'ECONNRESET') {
69
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_CALL_FAILED, 'Unable to reach LLM service. Check network or base_url.', e);
70
+ }
71
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_CALL_FAILED, `LLM connectivity check failed: ${e?.message || String(e)}`, e);
72
+ }
73
+ }
74
+ /**
75
+ * 向后兼容旧版本/测试中使用的 connectTest 名称。
76
+ * 内部直接代理到 V2.5 的 testConnection。
77
+ */
78
+ async connectTest() {
79
+ await this.testConnection(this.config);
80
+ }
81
+ async call(prompt, options) {
82
+ const startTime = Date.now();
83
+ const retries = options?.retries ?? this.config.max_retries;
84
+ for (let attempt = 0; attempt <= retries; attempt++) {
85
+ try {
86
+ const response = await this.client.chat.completions.create({
87
+ model: options?.model ?? this.config.model,
88
+ temperature: options?.temperature ?? this.config.temperature,
89
+ max_tokens: options?.maxTokens ?? this.config.max_tokens,
90
+ messages: [
91
+ { role: 'user', content: prompt }
92
+ ]
93
+ });
94
+ const content = response.choices[0].message.content || '';
95
+ const usage = response.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 };
96
+ const normalizedUsage = {
97
+ promptTokens: usage.prompt_tokens ?? 0,
98
+ completionTokens: usage.completion_tokens ?? 0,
99
+ totalTokens: usage.total_tokens ?? 0,
100
+ };
101
+ if (this.tracker) {
102
+ this.tracker.addUsage(normalizedUsage);
103
+ }
104
+ return {
105
+ content,
106
+ usage: normalizedUsage,
107
+ model: response.model,
108
+ responseTime: Date.now() - startTime,
109
+ };
110
+ }
111
+ catch (error) {
112
+ const errorMessage = error?.message || 'Unknown LLM call error';
113
+ logger_1.logger.debug(`LLM call attempt ${attempt + 1} failed: ${errorMessage}`);
114
+ if (attempt === retries) {
115
+ if (error?.status === 429) {
116
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_RATE_LIMITED, 'LLM service rate limited', error);
117
+ }
118
+ else if (error?.code === 'ETIMEDOUT') {
119
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_TIMEOUT, 'LLM call timeout', error);
120
+ }
121
+ else {
122
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_CALL_FAILED, `LLM call failed: ${errorMessage}`, error);
123
+ }
124
+ }
125
+ await this.sleep(this.config.retry_delay * Math.pow(2, attempt));
126
+ }
127
+ }
128
+ throw new errors_1.AppError(errors_1.ErrorCode.LLM_CALL_FAILED, 'Max retries exceeded');
129
+ }
130
+ async batchCall(prompts, options) {
131
+ const results = [];
132
+ for (const prompt of prompts) {
133
+ results.push(await this.call(prompt, options));
134
+ }
135
+ return results;
136
+ }
137
+ sleep(ms) {
138
+ return new Promise(resolve => setTimeout(resolve, ms));
139
+ }
140
+ }
141
+ exports.OpenAIClient = OpenAIClient;
@@ -1,6 +1,9 @@
1
- // ===== Three-step protocol: single file =====
2
- /** Step 1: Extract structure only (classes / globals / functions). No summary/description/basic info. */
3
- export const FILE_STRUCTURE_PROMPT = `
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CHUNK_ANALYSIS_PROMPT = exports.CODE_ANALYSIS_PROMPT = exports.DIRECTORY_SUMMARY_PROMPT = exports.DIRECTORY_DESCRIPTION_PROMPT = exports.MERGE_STRUCTURE_PROMPT = exports.PARSE_RETRY_HINT = exports.FILE_SUMMARY_PROMPT = exports.FILE_DESCRIPTION_PROMPT = exports.FILE_STRUCTURE_PROMPT = void 0;
4
+ // ===== Three-step protocol: single file =====
5
+ /** Step 1: Extract structure only (classes / globals / functions). No summary/description/basic info. */
6
+ exports.FILE_STRUCTURE_PROMPT = `
4
7
  Extract ONLY the following structure from the code file. Return STRICT JSON and nothing else.
5
8
  Do NOT generate summary/description or any basic info (language, LOC, dependencies, etc.).
6
9
 
@@ -29,19 +32,17 @@ Return JSON with ONLY these fields:
29
32
  }
30
33
 
31
34
  If nothing is found, return empty arrays. Do NOT output any other fields.
32
- `;
33
-
34
- /** Step 2: Generate description only (<= 200 words) */
35
- export const FILE_DESCRIPTION_PROMPT = `
35
+ `;
36
+ /** Step 2: Generate description only (<= 200 words) */
37
+ exports.FILE_DESCRIPTION_PROMPT = `
36
38
  Based on the extracted structure JSON below, describe the overall purpose of this file in <= 200 words.
37
39
  Return ONLY one JSON object: {"description": "..."}. No other text.
38
40
 
39
41
  Structure JSON:
40
42
  {{structureJson}}
41
- `;
42
-
43
- /** Step 3: Generate summary only (<= 100 words) */
44
- export const FILE_SUMMARY_PROMPT = `
43
+ `;
44
+ /** Step 3: Generate summary only (<= 100 words) */
45
+ exports.FILE_SUMMARY_PROMPT = `
45
46
  Based on the structure and description below, write a one-sentence high-level summary in <= 100 words.
46
47
  Return ONLY one JSON object: {"summary": "..."}. No other text.
47
48
 
@@ -50,18 +51,16 @@ Structure JSON:
50
51
 
51
52
  Description:
52
53
  {{description}}
53
- `;
54
-
55
- /** Retry hint when parsing fails (append to original prompt) */
56
- export const PARSE_RETRY_HINT = `
54
+ `;
55
+ /** Retry hint when parsing fails (append to original prompt) */
56
+ exports.PARSE_RETRY_HINT = `
57
57
 
58
58
  [IMPORTANT] Your previous output did NOT match the required format.
59
59
  Return STRICTLY the JSON specified above and NOTHING ELSE (no markdown, no explanations, no extra text).
60
- `;
61
-
62
- // ===== Merge stage =====
63
- /** Merge step: merge and deduplicate multiple chunk results into one structure */
64
- export const MERGE_STRUCTURE_PROMPT = `
60
+ `;
61
+ // ===== Merge stage =====
62
+ /** Merge step: merge and deduplicate multiple chunk results into one structure */
63
+ exports.MERGE_STRUCTURE_PROMPT = `
65
64
  Below are analysis results for multiple chunks of the same file. Merge and deduplicate them into ONE complete structure (classes and global functions).
66
65
  Return ONLY JSON. Do NOT generate description or summary.
67
66
 
@@ -90,11 +89,10 @@ Return JSON with ONLY these fields:
90
89
  }
91
90
 
92
91
  Keep item formats consistent with the chunk results. Do NOT output any other fields.
93
- `;
94
-
95
- // ===== Directory two-step protocol =====
96
- /** Directory step 1: generate description (<= 200 words) */
97
- export const DIRECTORY_DESCRIPTION_PROMPT = `
92
+ `;
93
+ // ===== Directory two-step protocol =====
94
+ /** Directory step 1: generate description (<= 200 words) */
95
+ exports.DIRECTORY_DESCRIPTION_PROMPT = `
98
96
  You are a codebase structure analysis assistant. Below is a JSON list of all direct child directories and files (with brief summaries).
99
97
  Write an English paragraph (<= 200 words) describing the directory's role and responsibilities in the project.
100
98
 
@@ -102,10 +100,9 @@ Return ONLY one JSON object: {"description": "..."}. No other text.
102
100
 
103
101
  Children (JSON):
104
102
  {{childrenJson}}
105
- `;
106
-
107
- /** Directory step 2: generate summary (<= 100 words) */
108
- export const DIRECTORY_SUMMARY_PROMPT = `
103
+ `;
104
+ /** Directory step 2: generate summary (<= 100 words) */
105
+ exports.DIRECTORY_SUMMARY_PROMPT = `
109
106
  Based on the directory description and children JSON below, write a one-sentence high-level summary in English (<= 100 words).
110
107
  Focus on the big picture and avoid details.
111
108
 
@@ -116,13 +113,11 @@ Directory description:
116
113
 
117
114
  Children (JSON):
118
115
  {{childrenJson}}
119
- `;
120
-
121
- export const CODE_ANALYSIS_PROMPT = `
116
+ `;
117
+ exports.CODE_ANALYSIS_PROMPT = `
122
118
  (Deprecated legacy prompt. Kept empty for backward compatibility; not used in the main pipeline.)
123
- `;
124
-
125
- export const CHUNK_ANALYSIS_PROMPT = `
119
+ `;
120
+ exports.CHUNK_ANALYSIS_PROMPT = `
126
121
  Analyze the following code chunk. Extract ONLY the structure that is confidently present in THIS chunk (classes, global variables, global functions).
127
122
  Return STRICT JSON and nothing else. Do NOT generate description, summary, or diagrams.
128
123
 
@@ -172,4 +167,4 @@ Rules:
172
167
  3) Analyze ONLY this chunk; context is reference only
173
168
  4) Do NOT output fields like basicInfo, partialDiagrams, summary, description, etc.
174
169
  5) Write all descriptions in English
175
- `;
170
+ `;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.llmService = exports.OpenAILLMService = void 0;
7
+ const openai_1 = __importDefault(require("openai"));
8
+ const config_1 = require("../common/config");
9
+ const errors_1 = require("../common/errors");
10
+ const logger_1 = require("../common/logger");
11
+ class OpenAILLMService {
12
+ client = null;
13
+ async getClient() {
14
+ if (!this.client) {
15
+ let config;
16
+ try {
17
+ config = config_1.configManager.getConfig();
18
+ }
19
+ catch (e) {
20
+ await config_1.configManager.load();
21
+ config = config_1.configManager.getConfig();
22
+ }
23
+ if (!config.llm.api_key) {
24
+ throw new errors_1.AppError(errors_1.ErrorCode.ANALYSIS_EXCEPTION, 'LLM API key not configured. Please set it in config file or via SKILL_ANY_CODE_LLM_API_KEY environment variable.');
25
+ }
26
+ this.client = new openai_1.default({
27
+ baseURL: config.llm.base_url,
28
+ apiKey: config.llm.api_key,
29
+ });
30
+ }
31
+ return this.client;
32
+ }
33
+ async generateCompletion(prompt, systemPrompt = 'You are a code analysis expert. You help analyze code files, generate summaries, class diagrams, and other analysis results. Be concise and accurate.') {
34
+ const client = await this.getClient();
35
+ const config = config_1.configManager.getConfig();
36
+ try {
37
+ logger_1.logger.debug(`Calling LLM model ${config.llm.model} with prompt length: ${prompt.length}`);
38
+ const response = await client.chat.completions.create({
39
+ model: config.llm.model,
40
+ messages: [
41
+ { role: 'system', content: systemPrompt },
42
+ { role: 'user', content: prompt },
43
+ ],
44
+ temperature: 0.1,
45
+ max_tokens: 2048,
46
+ });
47
+ const result = response.choices[0]?.message?.content?.trim() || '';
48
+ if (!result) {
49
+ throw new errors_1.AppError(errors_1.ErrorCode.ANALYSIS_EXCEPTION, 'LLM returned empty response');
50
+ }
51
+ logger_1.logger.debug(`LLM response received, length: ${result.length}`);
52
+ return result;
53
+ }
54
+ catch (error) {
55
+ logger_1.logger.error('LLM call failed:', error);
56
+ throw new errors_1.AppError(errors_1.ErrorCode.ANALYSIS_EXCEPTION, `LLM call failed: ${error.message}`);
57
+ }
58
+ }
59
+ }
60
+ exports.OpenAILLMService = OpenAILLMService;
61
+ exports.llmService = new OpenAILLMService();
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SkillGenerator = void 0;
37
+ const fs = __importStar(require("fs-extra"));
38
+ const path = __importStar(require("path"));
39
+ const skill_md_template_1 = require("./templates/skill.md.template");
40
+ const resolve_script_1 = require("./templates/resolve.script");
41
+ const logger_1 = require("../../common/logger");
42
+ const PROVIDER_DIRECTORY_MAP = {
43
+ opencode: '.agents/skills/skill-any-code',
44
+ cursor: '.agents/skills/skill-any-code',
45
+ codex: '.agents/skills/skill-any-code',
46
+ claude: '.claude/skills/skill-any-code',
47
+ };
48
+ class SkillGenerator {
49
+ async generate(options) {
50
+ const { projectRoot, providers } = options;
51
+ const deployedPaths = [];
52
+ const uniqueDirs = new Set();
53
+ for (const p of providers) {
54
+ const lowerProvider = p.toLowerCase();
55
+ const dir = PROVIDER_DIRECTORY_MAP[lowerProvider];
56
+ if (dir) {
57
+ uniqueDirs.add(dir);
58
+ }
59
+ else {
60
+ logger_1.logger.warn(`Unknown provider: ${p}. Skipped.`);
61
+ }
62
+ }
63
+ const skillMd = (0, skill_md_template_1.getSkillMdContent)();
64
+ const resolveScript = (0, resolve_script_1.getResolveScriptContent)();
65
+ for (const relativeDir of uniqueDirs) {
66
+ const targetDir = path.join(projectRoot, relativeDir);
67
+ try {
68
+ await fs.ensureDir(targetDir);
69
+ await fs.ensureDir(path.join(targetDir, 'scripts'));
70
+ await fs.writeFile(path.join(targetDir, 'SKILL.md'), skillMd, 'utf-8');
71
+ await fs.writeFile(path.join(targetDir, 'scripts', 'get-summary.py'), resolveScript, 'utf-8');
72
+ deployedPaths.push(targetDir);
73
+ logger_1.logger.debug(`Skill deployed to: ${targetDir}`);
74
+ }
75
+ catch (error) {
76
+ const msg = error instanceof Error ? error.message : String(error);
77
+ logger_1.logger.warn(`Failed to deploy skill (${targetDir}): ${msg}`);
78
+ }
79
+ }
80
+ return deployedPaths;
81
+ }
82
+ }
83
+ exports.SkillGenerator = SkillGenerator;
@@ -1,16 +1,19 @@
1
- /**
2
- * scripts/get-summary.py 内容模板(部署到 Skill 目录中的独立脚本,仅使用 Python 标准库)
3
- *
4
- * 约定:
5
- * - 输入:命令行参数 argv[1],应为项目内文件或目录「相对项目根目录」的相对路径
6
- * - 行为:遵循主程序的结果 md 命名规则,推导目标对象对应的结果 md 路径;若结果 md 存在则输出其相对项目根路径
7
- * - 输出:
8
- * - 命中:stdout 输出对应 Markdown 结果文件的相对路径(相对项目根目录,单行)
9
- * - 未命中:stdout 输出字符串 "N/A"(单行)
10
- * - 参数错误:stderr 输出错误信息并以 exit code 1 退出
11
- */
12
- export function getResolveScriptContent(): string {
13
- return `#!/usr/bin/env python3
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getResolveScriptContent = getResolveScriptContent;
4
+ /**
5
+ * scripts/get-summary.py 内容模板(部署到 Skill 目录中的独立脚本,仅使用 Python 标准库)
6
+ *
7
+ * 约定:
8
+ * - 输入:命令行参数 argv[1],应为项目内文件或目录「相对项目根目录」的相对路径
9
+ * - 行为:遵循主程序的结果 md 命名规则,推导目标对象对应的结果 md 路径;若结果 md 存在则输出其相对项目根路径
10
+ * - 输出:
11
+ * - 命中:stdout 输出对应 Markdown 结果文件的相对路径(相对项目根目录,单行)
12
+ * - 未命中:stdout 输出字符串 "N/A"(单行)
13
+ * - 参数错误:stderr 输出错误信息并以 exit code 1 退出
14
+ */
15
+ function getResolveScriptContent() {
16
+ return `#!/usr/bin/env python3
14
17
  from __future__ import annotations
15
18
 
16
19
  import os
@@ -93,5 +96,5 @@ def main() -> int:
93
96
 
94
97
  if __name__ == "__main__":
95
98
  raise SystemExit(main())
96
- `
97
- }
99
+ `;
100
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSkillMdContent = getSkillMdContent;
4
+ function getSkillMdContent() {
5
+ const lines = [
6
+ '---',
7
+ 'name: code-atlas-navigator',
8
+ 'description: Use this skill when you need to explore, understand, or search this codebase. Input a file or directory path relative to the project root to retrieve the path to its detailed natural language summary. Use this progressively to navigate from the root directory down to specific target files without reading the full raw source code.',
9
+ '---',
10
+ '',
11
+ '# Codebase Navigation Guide',
12
+ '',
13
+ 'This repository has been pre-analyzed and summarized into natural language Markdown files. To save context window and improve accuracy, **do not read the raw source code directly**. Instead, use this skill to progressively navigate the repository layer by layer.',
14
+ '',
15
+ '## 🧭 How to Explore the Codebase',
16
+ '',
17
+ "1. **Start High-Level**: If you don't know the exact file location, begin by querying the summary of the root directory (`.`).",
18
+ '2. **Progressive Disclosure**: Read the directory summary to understand its sub-components. Identify the next relevant sub-directory or file based on your current task.',
19
+ '3. **Drill Down**: Query the summaries of those specific sub-components. Repeat this until you locate the target function, class, or logic.',
20
+ '',
21
+ '## 🛠️ How to Locate a Summary File',
22
+ '',
23
+ 'Use the provided Python script to map the original codebase path to its corresponding Markdown summary path.',
24
+ '',
25
+ '**Script Specification:**',
26
+ '* **Input Parameter**: The relative path of the target file or directory with respect to the project root.',
27
+ '* **Output**: The relative path of the Markdown summary file with respect to the project root.',
28
+ '* **Fallback**: If the summary Markdown file cannot be found, the script will strictly output `N/A`.',
29
+ '',
30
+ '### Execution Commands',
31
+ '',
32
+ 'Since the execution environment may vary, please use the appropriate command based on your current operating system:',
33
+ '',
34
+ '**For Linux / macOS (Bash/Zsh):**',
35
+ '```bash',
36
+ 'python3 scripts/get_summary.py <relative/path/to/target>',
37
+ '```',
38
+ '(Note: If python3 is not found, fallback to python).',
39
+ '',
40
+ '**For Windows (CMD/PowerShell):**',
41
+ '```dos',
42
+ 'python scripts\\get_summary.py <relative\\path\\to\\target>',
43
+ '```',
44
+ '(Note: You may also use py or python3 depending on the Windows environment setup).',
45
+ ];
46
+ return lines.join('\n') + '\n';
47
+ }