autosnippet 3.3.6 → 3.3.7

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 (107) hide show
  1. package/dashboard/dist/assets/icons-FHns2ypa.js +1 -0
  2. package/dashboard/dist/assets/index-BRJv5Y3r.js +135 -0
  3. package/dashboard/dist/assets/index-DzoB7kxK.css +1 -0
  4. package/dashboard/dist/index.html +3 -3
  5. package/dist/bin/cli.js +1 -0
  6. package/dist/lib/agent/AgentRuntime.d.ts +2 -2
  7. package/dist/lib/agent/AgentRuntime.js +26 -18
  8. package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
  9. package/dist/lib/agent/forced-summary.js +7 -2
  10. package/dist/lib/cli/AiScanService.js +4 -4
  11. package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
  12. package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
  13. package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +30 -0
  14. package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1305 -0
  15. package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
  16. package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
  17. package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
  18. package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
  19. package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
  20. package/dist/lib/core/discovery/index.d.ts +2 -0
  21. package/dist/lib/core/discovery/index.js +4 -0
  22. package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
  23. package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
  24. package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
  25. package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
  26. package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
  27. package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
  28. package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
  29. package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
  30. package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
  31. package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
  32. package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
  33. package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
  34. package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +7 -1
  35. package/dist/lib/domain/knowledge/KnowledgeEntry.js +17 -3
  36. package/dist/lib/external/ai/AiProvider.d.ts +12 -0
  37. package/dist/lib/external/ai/AiProvider.js +24 -0
  38. package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
  39. package/dist/lib/external/ai/AiProviderManager.js +193 -0
  40. package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
  41. package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
  42. package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
  43. package/dist/lib/external/ai/providers/MockProvider.js +290 -14
  44. package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
  45. package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
  46. package/dist/lib/external/lark/LarkTransport.js +10 -2
  47. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
  48. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
  49. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +16 -8
  50. package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
  51. package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +9 -0
  52. package/dist/lib/external/mcp/handlers/bootstrap-external.js +2 -0
  53. package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
  54. package/dist/lib/external/mcp/handlers/consolidated.js +2 -1
  55. package/dist/lib/external/mcp/handlers/dimension-complete-external.js +2 -1
  56. package/dist/lib/external/mcp/handlers/evolve-external.js +5 -2
  57. package/dist/lib/external/mcp/handlers/knowledge.js +5 -4
  58. package/dist/lib/http/routes/ai.js +111 -30
  59. package/dist/lib/http/routes/candidates.js +11 -4
  60. package/dist/lib/http/routes/commands.js +10 -1
  61. package/dist/lib/http/routes/health.js +11 -0
  62. package/dist/lib/http/routes/modules.js +27 -0
  63. package/dist/lib/http/routes/recipes.js +7 -0
  64. package/dist/lib/http/utils/routeHelpers.js +2 -1
  65. package/dist/lib/injection/ServiceContainer.d.ts +6 -5
  66. package/dist/lib/injection/ServiceContainer.js +11 -27
  67. package/dist/lib/injection/ServiceMap.d.ts +2 -0
  68. package/dist/lib/injection/modules/AiModule.d.ts +6 -9
  69. package/dist/lib/injection/modules/AiModule.js +82 -39
  70. package/dist/lib/injection/modules/PanoramaModule.js +1 -1
  71. package/dist/lib/service/cleanup/CleanupService.d.ts +54 -7
  72. package/dist/lib/service/cleanup/CleanupService.js +284 -37
  73. package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +6 -0
  74. package/dist/lib/service/knowledge/CodeEntityGraph.js +16 -0
  75. package/dist/lib/service/knowledge/KnowledgeService.js +23 -10
  76. package/dist/lib/service/module/ModuleService.js +10 -19
  77. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +10 -1
  78. package/dist/lib/service/panorama/CouplingAnalyzer.js +44 -2
  79. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +1 -1
  80. package/dist/lib/service/panorama/DimensionAnalyzer.js +31 -17
  81. package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
  82. package/dist/lib/service/panorama/LayerInferrer.js +118 -1
  83. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +9 -0
  84. package/dist/lib/service/panorama/ModuleDiscoverer.js +58 -2
  85. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +6 -2
  86. package/dist/lib/service/panorama/PanoramaAggregator.js +84 -6
  87. package/dist/lib/service/panorama/PanoramaScanner.js +28 -0
  88. package/dist/lib/service/panorama/PanoramaService.js +10 -5
  89. package/dist/lib/service/panorama/PanoramaTypes.d.ts +38 -0
  90. package/dist/lib/service/panorama/RoleRefiner.d.ts +2 -0
  91. package/dist/lib/service/panorama/RoleRefiner.js +41 -0
  92. package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
  93. package/dist/lib/service/panorama/TechStackProfiler.js +191 -0
  94. package/dist/lib/service/skills/SignalCollector.d.ts +1 -0
  95. package/dist/lib/service/skills/SignalCollector.js +6 -5
  96. package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
  97. package/dist/lib/service/vector/ContextualEnricher.js +4 -0
  98. package/dist/lib/shared/LanguageService.js +3 -0
  99. package/dist/lib/shared/developer-identity.d.ts +18 -0
  100. package/dist/lib/shared/developer-identity.js +62 -0
  101. package/dist/lib/shared/schemas/http-requests.d.ts +8 -17
  102. package/dist/lib/shared/schemas/http-requests.js +9 -6
  103. package/dist/lib/types/knowledge-wire.d.ts +1 -0
  104. package/package.json +1 -1
  105. package/dashboard/dist/assets/icons-D1aVZYFW.js +0 -1
  106. package/dashboard/dist/assets/index-CxHOu8Hd.css +0 -1
  107. package/dashboard/dist/assets/index-DDdAOpYT.js +0 -128
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @module StarlarkParser
3
+ * @description Starlark (Python 子集) 轻量解析器 — 从 BUILD / BUILD.bazel / BUCK 文件提取构建目标信息
4
+ *
5
+ * 支持解析:
6
+ * - load() 语句 → 推断语言
7
+ * - 目标声明 (swift_library, cc_binary, java_library 等)
8
+ * - name / deps / srcs / visibility 字段
9
+ *
10
+ * 设计策略: 正则 + 逐行状态机(不做宏展开)
11
+ */
12
+ export interface StarlarkTarget {
13
+ rule: string;
14
+ name: string;
15
+ srcs: string[];
16
+ deps: string[];
17
+ visibility: string[];
18
+ testonly?: boolean;
19
+ }
20
+ export interface LoadStatement {
21
+ repository: string;
22
+ path: string;
23
+ symbols: string[];
24
+ }
25
+ export interface ParsedBuildFile {
26
+ targets: StarlarkTarget[];
27
+ loads: LoadStatement[];
28
+ }
29
+ export declare const RULE_TO_LANGUAGE: Record<string, string>;
30
+ /**
31
+ * 解析单个 BUILD/BUILD.bazel/BUCK 文件内容
32
+ */
33
+ export declare function parseStarlarkBuildFile(content: string): ParsedBuildFile;
@@ -0,0 +1,229 @@
1
+ /**
2
+ * @module StarlarkParser
3
+ * @description Starlark (Python 子集) 轻量解析器 — 从 BUILD / BUILD.bazel / BUCK 文件提取构建目标信息
4
+ *
5
+ * 支持解析:
6
+ * - load() 语句 → 推断语言
7
+ * - 目标声明 (swift_library, cc_binary, java_library 等)
8
+ * - name / deps / srcs / visibility 字段
9
+ *
10
+ * 设计策略: 正则 + 逐行状态机(不做宏展开)
11
+ */
12
+ // ── 规则名 → 语言映射 ────────────────────────────────
13
+ export const RULE_TO_LANGUAGE = {
14
+ // Bazel
15
+ swift_library: 'swift',
16
+ swift_binary: 'swift',
17
+ swift_test: 'swift',
18
+ cc_library: 'cpp',
19
+ cc_binary: 'cpp',
20
+ cc_test: 'cpp',
21
+ java_library: 'java',
22
+ java_binary: 'java',
23
+ java_test: 'java',
24
+ kt_jvm_library: 'kotlin',
25
+ kt_jvm_binary: 'kotlin',
26
+ py_library: 'python',
27
+ py_binary: 'python',
28
+ py_test: 'python',
29
+ go_library: 'go',
30
+ go_binary: 'go',
31
+ go_test: 'go',
32
+ rust_library: 'rust',
33
+ rust_binary: 'rust',
34
+ rust_test: 'rust',
35
+ ts_project: 'typescript',
36
+ proto_library: 'protobuf',
37
+ // Buck2
38
+ cxx_library: 'cpp',
39
+ cxx_binary: 'cpp',
40
+ cxx_test: 'cpp',
41
+ android_library: 'kotlin',
42
+ android_binary: 'kotlin',
43
+ apple_library: 'swift',
44
+ apple_binary: 'swift',
45
+ python_library: 'python',
46
+ python_binary: 'python',
47
+ // Pants
48
+ python_source: 'python',
49
+ python_sources: 'python',
50
+ docker_image: 'docker',
51
+ };
52
+ // ── 正则模式 ────────────────────────────────────────
53
+ const LOAD_RE = /^load\(\s*"([^"]+)"\s*,\s*((?:"[^"]+"(?:\s*,\s*)?)+)\s*\)/;
54
+ const RULE_CALL_RE = /^(\w+)\(\s*$/;
55
+ const NAME_RE = /name\s*=\s*"([^"]+)"/;
56
+ const TESTONLY_RE = /testonly\s*=\s*(True|1)/;
57
+ const STRING_LIST_RE = (field) => new RegExp(`${field}\\s*=\\s*(?:glob\\(\\s*)?\\[([^\\]]*)\\]`, 's');
58
+ const DEP_LABEL_RE = /"((?:\/\/[^"]*|:[^"]*))"/g;
59
+ const GLOB_RE = /glob\(\s*\[([^\]]*)\]\s*\)/;
60
+ // ── 公开 API ────────────────────────────────────────
61
+ /**
62
+ * 解析单个 BUILD/BUILD.bazel/BUCK 文件内容
63
+ */
64
+ export function parseStarlarkBuildFile(content) {
65
+ const result = { targets: [], loads: [] };
66
+ const lines = content.split('\n');
67
+ // Pass 1: 提取 load 语句
68
+ for (const line of lines) {
69
+ const trimmed = line.trim();
70
+ if (trimmed.startsWith('#')) {
71
+ continue;
72
+ }
73
+ const loadMatch = trimmed.match(LOAD_RE);
74
+ if (loadMatch) {
75
+ const fullLabel = loadMatch[1];
76
+ const symbolsPart = loadMatch[2];
77
+ // 解析 repository 和 path
78
+ let repository = '';
79
+ let path = fullLabel;
80
+ if (fullLabel.startsWith('@')) {
81
+ const slashIdx = fullLabel.indexOf('//');
82
+ if (slashIdx !== -1) {
83
+ repository = fullLabel.substring(0, slashIdx);
84
+ path = fullLabel.substring(slashIdx);
85
+ }
86
+ }
87
+ const symbols = [];
88
+ const symRe = /"([^"]+)"/g;
89
+ let symMatch;
90
+ while ((symMatch = symRe.exec(symbolsPart)) !== null) {
91
+ symbols.push(symMatch[1]);
92
+ }
93
+ result.loads.push({ repository, path, symbols });
94
+ }
95
+ }
96
+ // Pass 2: 提取目标声明(逐行状态机)
97
+ let i = 0;
98
+ while (i < lines.length) {
99
+ const trimmed = lines[i].trim();
100
+ // 跳过注释和空行
101
+ if (trimmed.startsWith('#') || trimmed === '') {
102
+ i++;
103
+ continue;
104
+ }
105
+ // 检测规则调用开始: rule_name(
106
+ const ruleMatch = trimmed.match(RULE_CALL_RE);
107
+ if (ruleMatch) {
108
+ const rule = ruleMatch[1];
109
+ // 收集整个调用块直到匹配的 )
110
+ const blockLines = [trimmed];
111
+ let depth = 1;
112
+ let j = i + 1;
113
+ while (j < lines.length && depth > 0) {
114
+ const bLine = lines[j];
115
+ for (const ch of bLine) {
116
+ if (ch === '(') {
117
+ depth++;
118
+ }
119
+ if (ch === ')') {
120
+ depth--;
121
+ }
122
+ }
123
+ blockLines.push(bLine);
124
+ j++;
125
+ }
126
+ const block = blockLines.join('\n');
127
+ const target = parseTargetBlock(rule, block);
128
+ if (target) {
129
+ result.targets.push(target);
130
+ }
131
+ i = j;
132
+ continue;
133
+ }
134
+ // 也检测单行调用: rule_name(name = "...")
135
+ const inlineMatch = trimmed.match(/^(\w+)\(/);
136
+ if (inlineMatch && RULE_TO_LANGUAGE[inlineMatch[1]]) {
137
+ // 同样收集到闭合 )
138
+ const rule = inlineMatch[1];
139
+ const blockLines = [trimmed];
140
+ let depth = 0;
141
+ for (const ch of trimmed) {
142
+ if (ch === '(') {
143
+ depth++;
144
+ }
145
+ if (ch === ')') {
146
+ depth--;
147
+ }
148
+ }
149
+ let j = i + 1;
150
+ while (j < lines.length && depth > 0) {
151
+ const bLine = lines[j];
152
+ for (const ch of bLine) {
153
+ if (ch === '(') {
154
+ depth++;
155
+ }
156
+ if (ch === ')') {
157
+ depth--;
158
+ }
159
+ }
160
+ blockLines.push(bLine);
161
+ j++;
162
+ }
163
+ const block = blockLines.join('\n');
164
+ const target = parseTargetBlock(rule, block);
165
+ if (target) {
166
+ result.targets.push(target);
167
+ }
168
+ i = j;
169
+ continue;
170
+ }
171
+ i++;
172
+ }
173
+ return result;
174
+ }
175
+ // ── 内部解析函数 ────────────────────────────────────
176
+ function parseTargetBlock(rule, block) {
177
+ const nameMatch = block.match(NAME_RE);
178
+ if (!nameMatch) {
179
+ return null;
180
+ }
181
+ return {
182
+ rule,
183
+ name: nameMatch[1],
184
+ srcs: extractStringList(block, 'srcs'),
185
+ deps: extractDepLabels(block),
186
+ visibility: extractStringList(block, 'visibility'),
187
+ testonly: TESTONLY_RE.test(block) ? true : undefined,
188
+ };
189
+ }
190
+ function extractStringList(block, field) {
191
+ const listMatch = block.match(STRING_LIST_RE(field));
192
+ if (!listMatch) {
193
+ return [];
194
+ }
195
+ let inner = listMatch[1];
196
+ // 处理 glob() 模式
197
+ const globMatch = inner.match(GLOB_RE);
198
+ if (globMatch) {
199
+ inner = globMatch[1];
200
+ }
201
+ const items = [];
202
+ const strRe = /"([^"]+)"/g;
203
+ let m;
204
+ while ((m = strRe.exec(inner)) !== null) {
205
+ items.push(m[1]);
206
+ }
207
+ return items;
208
+ }
209
+ function extractDepLabels(block) {
210
+ const depsMatch = block.match(STRING_LIST_RE('deps'));
211
+ if (!depsMatch) {
212
+ return [];
213
+ }
214
+ const inner = depsMatch[1];
215
+ const deps = [];
216
+ const re = new RegExp(DEP_LABEL_RE.source, 'g');
217
+ let m;
218
+ while ((m = re.exec(inner)) !== null) {
219
+ deps.push(m[1]);
220
+ }
221
+ // 也捕获非标签格式的字符串依赖
222
+ const strRe = /"([^"]+)"/g;
223
+ while ((m = strRe.exec(inner)) !== null) {
224
+ if (!m[1].startsWith('//') && !m[1].startsWith(':') && !deps.includes(m[1])) {
225
+ deps.push(m[1]);
226
+ }
227
+ }
228
+ return deps;
229
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @module YamlConfigParser
3
+ * @description YAML 配置解析器 — 从 XcodeGen project.yml 中提取项目结构信息
4
+ *
5
+ * 支持解析:
6
+ * - targets(构建目标)及其依赖关系
7
+ * - settings(项目级/目标级构建设置)
8
+ * - schemes(构建方案)
9
+ * - sources(源文件路径)
10
+ *
11
+ * 使用 js-yaml 进行安全解析(禁用危险的 YAML 特性)。
12
+ */
13
+ import type { ParsedModuleSpec, ParsedProjectConfig } from './RubyDslParser.js';
14
+ /**
15
+ * 解析 XcodeGen project.yml 内容
16
+ * 返回与 RubyDslParser 兼容的 ParsedProjectConfig
17
+ */
18
+ export declare function parseXcodeGenProject(content: string): ParsedProjectConfig;
19
+ /**
20
+ * 解析单个 target 为 ParsedModuleSpec 格式
21
+ */
22
+ export declare function parseXcodeGenTarget(targetName: string, content: string): ParsedModuleSpec | null;
23
+ /**
24
+ * 提取所有 target 的依赖图
25
+ * 返回 [from, to][] 形式的有向边列表
26
+ */
27
+ export declare function extractXcodeGenDependencyEdges(content: string): Array<[string, string]>;
28
+ export interface ParsedMelosProject {
29
+ name: string;
30
+ packageGlobs: string[];
31
+ scripts: string[];
32
+ }
33
+ /**
34
+ * 解析 melos.yaml 内容
35
+ * 提取项目名、包路径 glob 模式、scripts 列表
36
+ */
37
+ export declare function parseMelosProject(content: string): ParsedMelosProject;
@@ -0,0 +1,212 @@
1
+ /**
2
+ * @module YamlConfigParser
3
+ * @description YAML 配置解析器 — 从 XcodeGen project.yml 中提取项目结构信息
4
+ *
5
+ * 支持解析:
6
+ * - targets(构建目标)及其依赖关系
7
+ * - settings(项目级/目标级构建设置)
8
+ * - schemes(构建方案)
9
+ * - sources(源文件路径)
10
+ *
11
+ * 使用 js-yaml 进行安全解析(禁用危险的 YAML 特性)。
12
+ */
13
+ import yaml from 'js-yaml';
14
+ // ── 目标类型 → 层级推断 ────────────────────────────────
15
+ const TARGET_TYPE_LAYER = {
16
+ application: 'App',
17
+ 'app-extension': 'Extension',
18
+ framework: 'Framework',
19
+ 'static-library': 'Library',
20
+ 'dynamic-library': 'Library',
21
+ bundle: 'Resource',
22
+ 'unit-test': 'Test',
23
+ 'ui-test': 'Test',
24
+ tool: 'Tool',
25
+ };
26
+ // ── 公开 API ────────────────────────────────────────
27
+ /**
28
+ * 解析 XcodeGen project.yml 内容
29
+ * 返回与 RubyDslParser 兼容的 ParsedProjectConfig
30
+ */
31
+ export function parseXcodeGenProject(content) {
32
+ let doc;
33
+ try {
34
+ doc = yaml.load(content, { schema: yaml.CORE_SCHEMA });
35
+ }
36
+ catch {
37
+ return { layers: [], globalDependencies: [] };
38
+ }
39
+ if (!doc || typeof doc !== 'object') {
40
+ return { layers: [], globalDependencies: [] };
41
+ }
42
+ const result = {
43
+ layers: [],
44
+ globalDependencies: [],
45
+ };
46
+ // 提取宿主应用
47
+ if (doc.name) {
48
+ result.hostApp = { name: doc.name, version: '0.0.0' };
49
+ }
50
+ if (!doc.targets) {
51
+ return result;
52
+ }
53
+ // 将 targets 按类型分层
54
+ const layerMap = new Map();
55
+ for (const [targetName, target] of Object.entries(doc.targets)) {
56
+ const targetType = target.type ?? 'framework';
57
+ const layerName = TARGET_TYPE_LAYER[targetType] ?? 'Other';
58
+ const isLocal = !isExternalTarget(targetName, doc);
59
+ const mod = {
60
+ name: targetName,
61
+ version: '0.0.0',
62
+ isLocal,
63
+ localPath: extractSourcePath(target),
64
+ group: layerName,
65
+ };
66
+ if (!layerMap.has(layerName)) {
67
+ layerMap.set(layerName, []);
68
+ }
69
+ layerMap.get(layerName)?.push(mod);
70
+ }
71
+ // 转为 ParsedLayer[]
72
+ let order = 0;
73
+ for (const [name, modules] of layerMap.entries()) {
74
+ result.layers.push({
75
+ name,
76
+ order: order++,
77
+ accessibleLayers: [],
78
+ modules,
79
+ });
80
+ }
81
+ // 提取 SPM packages → globalDependencies
82
+ if (doc.packages) {
83
+ for (const [pkgName, pkg] of Object.entries(doc.packages)) {
84
+ result.globalDependencies.push({
85
+ name: pkgName,
86
+ version: pkg.from ?? pkg.version ?? pkg.branch ?? '0.0.0',
87
+ isLocal: !!pkg.path,
88
+ localPath: pkg.path,
89
+ });
90
+ }
91
+ }
92
+ return result;
93
+ }
94
+ /**
95
+ * 解析单个 target 为 ParsedModuleSpec 格式
96
+ */
97
+ export function parseXcodeGenTarget(targetName, content) {
98
+ let doc;
99
+ try {
100
+ doc = yaml.load(content, { schema: yaml.CORE_SCHEMA });
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ if (!doc?.targets?.[targetName]) {
106
+ return null;
107
+ }
108
+ const target = doc.targets[targetName];
109
+ return {
110
+ name: targetName,
111
+ version: '0.0.0',
112
+ sources: extractSourcePath(target) ?? targetName,
113
+ dependencies: extractTargetDependencies(target),
114
+ publicHeaders: [],
115
+ deploymentTarget: extractDeploymentTarget(target),
116
+ };
117
+ }
118
+ /**
119
+ * 提取所有 target 的依赖图
120
+ * 返回 [from, to][] 形式的有向边列表
121
+ */
122
+ export function extractXcodeGenDependencyEdges(content) {
123
+ let doc;
124
+ try {
125
+ doc = yaml.load(content, { schema: yaml.CORE_SCHEMA });
126
+ }
127
+ catch {
128
+ return [];
129
+ }
130
+ if (!doc?.targets) {
131
+ return [];
132
+ }
133
+ const edges = [];
134
+ for (const [targetName, target] of Object.entries(doc.targets)) {
135
+ if (!target.dependencies) {
136
+ continue;
137
+ }
138
+ for (const dep of target.dependencies) {
139
+ const depName = dep.target ?? dep.framework ?? dep.package ?? dep.carthage;
140
+ if (depName) {
141
+ edges.push([targetName, depName]);
142
+ }
143
+ }
144
+ }
145
+ return edges;
146
+ }
147
+ // ── 内部帮助函数 ────────────────────────────────────
148
+ function extractSourcePath(target) {
149
+ if (!target.sources || target.sources.length === 0) {
150
+ return undefined;
151
+ }
152
+ const first = target.sources[0];
153
+ if (typeof first === 'string') {
154
+ return first;
155
+ }
156
+ return first.path;
157
+ }
158
+ function extractTargetDependencies(target) {
159
+ if (!target.dependencies) {
160
+ return [];
161
+ }
162
+ return target.dependencies
163
+ .map((dep) => dep.target ?? dep.framework ?? dep.package ?? dep.carthage)
164
+ .filter((name) => !!name);
165
+ }
166
+ function extractDeploymentTarget(target) {
167
+ if (!target.deploymentTarget) {
168
+ return undefined;
169
+ }
170
+ if (typeof target.deploymentTarget === 'string') {
171
+ return target.deploymentTarget;
172
+ }
173
+ // Object form: { iOS: "15.0", macOS: "12.0" }
174
+ const values = Object.values(target.deploymentTarget);
175
+ return values[0];
176
+ }
177
+ function isExternalTarget(targetName, doc) {
178
+ // SPM packages referenced as targets are external
179
+ if (doc.packages?.[targetName]) {
180
+ return true;
181
+ }
182
+ // Targets defined in the project are local
183
+ return false;
184
+ }
185
+ /**
186
+ * 解析 melos.yaml 内容
187
+ * 提取项目名、包路径 glob 模式、scripts 列表
188
+ */
189
+ export function parseMelosProject(content) {
190
+ const result = {
191
+ name: '',
192
+ packageGlobs: [],
193
+ scripts: [],
194
+ };
195
+ try {
196
+ const doc = yaml.load(content, { schema: yaml.CORE_SCHEMA });
197
+ if (!doc) {
198
+ return result;
199
+ }
200
+ result.name = doc.name ?? '';
201
+ if (Array.isArray(doc.packages)) {
202
+ result.packageGlobs = doc.packages.filter((p) => typeof p === 'string');
203
+ }
204
+ if (doc.scripts && typeof doc.scripts === 'object') {
205
+ result.scripts = Object.keys(doc.scripts);
206
+ }
207
+ }
208
+ catch {
209
+ // YAML 解析失败时返回空结果
210
+ }
211
+ return result;
212
+ }
@@ -61,6 +61,7 @@ export declare class KnowledgeEntry {
61
61
  from: string;
62
62
  to: string;
63
63
  at: number;
64
+ by?: string;
64
65
  }>;
65
66
  autoApprovable: boolean;
66
67
  stagingDeadline: number | null;
@@ -152,6 +153,11 @@ export declare class KnowledgeEntry {
152
153
  success: boolean;
153
154
  error?: string;
154
155
  };
156
+ /**
157
+ * 将最后一条 lifecycleHistory 条目标记操作人。
158
+ * 由 KnowledgeService._lifecycleTransition() 在 entity method 执行后调用。
159
+ */
160
+ stampLastTransition(by: string): void;
155
161
  /** 是否处于候选阶段 */
156
162
  isCandidate(): boolean;
157
163
  /** 是否可被 Guard/Search/Export 消费 */
@@ -194,7 +200,7 @@ export declare class KnowledgeEntry {
194
200
  /** JSON → Domain (camelCase 直入) */
195
201
  static fromJSON(data: unknown): KnowledgeEntry;
196
202
  /** @returns } */
197
- _transition(to: string): {
203
+ _transition(to: string, by?: string): {
198
204
  success: boolean;
199
205
  error?: string;
200
206
  };
@@ -173,6 +173,16 @@ export class KnowledgeEntry {
173
173
  }
174
174
  return result;
175
175
  }
176
+ /**
177
+ * 将最后一条 lifecycleHistory 条目标记操作人。
178
+ * 由 KnowledgeService._lifecycleTransition() 在 entity method 执行后调用。
179
+ */
180
+ stampLastTransition(by) {
181
+ const last = this.lifecycleHistory[this.lifecycleHistory.length - 1];
182
+ if (last) {
183
+ last.by = by;
184
+ }
185
+ }
176
186
  /* ═══ 谓词 ═══════════════════════════════════════════ */
177
187
  /** 是否处于候选阶段 */
178
188
  isCandidate() {
@@ -289,18 +299,22 @@ export class KnowledgeEntry {
289
299
  }
290
300
  /* ═══ 私有 ═══════════════════════════════════════════ */
291
301
  /** @returns } */
292
- _transition(to) {
302
+ _transition(to, by) {
293
303
  if (!isValidTransition(this.lifecycle, to)) {
294
304
  return {
295
305
  success: false,
296
306
  error: `Invalid lifecycle transition: ${this.lifecycle} → ${to}`,
297
307
  };
298
308
  }
299
- this.lifecycleHistory.push({
309
+ const entry = {
300
310
  from: this.lifecycle,
301
311
  to,
302
312
  at: this._now(),
303
- });
313
+ };
314
+ if (by) {
315
+ entry.by = by;
316
+ }
317
+ this.lifecycleHistory.push(entry);
304
318
  this.lifecycle = to;
305
319
  this.updatedAt = this._now();
306
320
  return { success: true };
@@ -148,6 +148,13 @@ export declare class AiProvider {
148
148
  name: string;
149
149
  timeout: number;
150
150
  _fallbackFrom?: string;
151
+ /**
152
+ * Token 用量回调 — 每次 API 调用后触发(包括 chat / chatWithStructuredOutput / chatWithTools)
153
+ * 由外部(如 DI 容器)注入以实现全局 token 计量。
154
+ */
155
+ _onTokenUsage: ((usage: TokenUsage & {
156
+ source?: string;
157
+ }) => void) | null;
151
158
  constructor(config?: AiProviderConfig);
152
159
  _acquireRequestSlot(): Promise<void>;
153
160
  _releaseRequestSlot(): void;
@@ -158,6 +165,11 @@ export declare class AiProvider {
158
165
  * @param context {history: [], temperature, maxTokens}
159
166
  */
160
167
  chat(prompt: string, context?: ChatContext): Promise<string>;
168
+ /**
169
+ * 从 API 原始响应中提取 token 用量并触发回调。
170
+ * 子类在 chat() / chatWithStructuredOutput() 中调用。
171
+ */
172
+ _emitTokenUsage(usage: TokenUsage | null | undefined, source?: string): void;
161
173
  /** 摘要 - 对代码/文档生成结构化摘要 */
162
174
  summarize(code: string): Promise<unknown>;
163
175
  /** 向量嵌入 - 返回浮点数组 */
@@ -21,6 +21,11 @@ export class AiProvider {
21
21
  name;
22
22
  timeout;
23
23
  _fallbackFrom;
24
+ /**
25
+ * Token 用量回调 — 每次 API 调用后触发(包括 chat / chatWithStructuredOutput / chatWithTools)
26
+ * 由外部(如 DI 容器)注入以实现全局 token 计量。
27
+ */
28
+ _onTokenUsage = null;
24
29
  constructor(config = {}) {
25
30
  this.model = config.model || '';
26
31
  this.apiKey = config.apiKey || '';
@@ -79,6 +84,25 @@ export class AiProvider {
79
84
  async chat(prompt, context = {}) {
80
85
  throw new Error(`${this.name}.chat() not implemented`);
81
86
  }
87
+ /**
88
+ * 从 API 原始响应中提取 token 用量并触发回调。
89
+ * 子类在 chat() / chatWithStructuredOutput() 中调用。
90
+ */
91
+ _emitTokenUsage(usage, source) {
92
+ if (!usage || !this._onTokenUsage) {
93
+ return;
94
+ }
95
+ const total = (usage.inputTokens || 0) + (usage.outputTokens || 0);
96
+ if (total === 0) {
97
+ return;
98
+ }
99
+ try {
100
+ this._onTokenUsage({ ...usage, source });
101
+ }
102
+ catch {
103
+ /* token tracking should never break execution */
104
+ }
105
+ }
82
106
  /** 摘要 - 对代码/文档生成结构化摘要 */
83
107
  async summarize(code) {
84
108
  throw new Error(`${this.name}.summarize() not implemented`);