autosnippet 3.0.13 → 3.1.0
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/bin/api-server.js +2 -0
- package/bin/cli.js +24 -19
- package/config/default.json +1 -1
- package/lib/bootstrap.js +4 -4
- package/lib/cli/SetupService.js +29 -29
- package/lib/cli/UpgradeService.js +3 -2
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/ast/ensure-grammars.js +1 -1
- package/lib/core/ast/index.js +62 -11
- package/lib/core/ast/lang-dart.js +27 -21
- package/lib/core/ast/lang-go.js +6 -20
- package/lib/core/ast/lang-rust.js +53 -28
- package/lib/core/ast/parser-init.js +9 -5
- package/lib/core/discovery/DartDiscoverer.js +4 -10
- package/lib/core/discovery/GoDiscoverer.js +45 -25
- package/lib/core/discovery/NodeDiscoverer.js +1 -3
- package/lib/core/discovery/PythonDiscoverer.js +7 -1
- package/lib/core/discovery/RustDiscoverer.js +111 -38
- package/lib/core/discovery/index.js +2 -2
- package/lib/core/enhancement/django-enhancement.js +10 -4
- package/lib/core/enhancement/fastapi-enhancement.js +16 -9
- package/lib/core/enhancement/go-grpc-enhancement.js +2 -1
- package/lib/core/enhancement/go-web-enhancement.js +3 -6
- package/lib/core/enhancement/ml-enhancement.js +6 -3
- package/lib/core/enhancement/nextjs-enhancement.js +17 -7
- package/lib/core/enhancement/node-server-enhancement.js +4 -2
- package/lib/core/enhancement/react-enhancement.js +6 -3
- package/lib/core/enhancement/rust-tokio-enhancement.js +6 -2
- package/lib/core/enhancement/rust-web-enhancement.js +13 -7
- package/lib/core/enhancement/vue-enhancement.js +10 -5
- package/lib/external/ai/AiFactory.js +3 -1
- package/lib/external/ai/AiProvider.js +3 -1
- package/lib/external/mcp/McpServer.js +2 -0
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +1 -2
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +7 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +55 -26
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/refine.js +3 -1
- package/lib/external/mcp/handlers/bootstrap.js +4 -10
- package/lib/external/mcp/handlers/browse.js +6 -2
- package/lib/external/mcp/handlers/guard.js +6 -2
- package/lib/external/mcp/handlers/skill.js +6 -2
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/routes/candidates.js +3 -1
- package/lib/http/routes/extract.js +4 -5
- package/lib/http/routes/guardRules.js +1 -1
- package/lib/http/routes/modules.js +9 -3
- package/lib/http/routes/skills.js +54 -6
- package/lib/http/routes/violations.js +4 -3
- package/lib/infrastructure/external/ClipboardManager.js +24 -7
- package/lib/infrastructure/external/NativeUi.js +3 -1
- package/lib/infrastructure/external/OpenBrowser.js +1 -0
- package/lib/infrastructure/external/XcodeAutomation.js +5 -5
- package/lib/infrastructure/vector/IndexingPipeline.js +14 -5
- package/lib/injection/ServiceContainer.js +34 -11
- package/lib/platform/ios/index.js +20 -25
- package/lib/platform/ios/routes/spm.js +6 -3
- package/lib/platform/ios/snippet/PlaceholderConverter.js +6 -2
- package/lib/platform/ios/snippet/XcodeCodec.js +4 -2
- package/lib/platform/ios/spm/SpmDiscoverer.js +1 -1
- package/lib/platform/ios/spm/SpmService.js +3 -1
- package/lib/platform/ios/xcode/XcodeIntegration.js +10 -12
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +6 -1
- package/lib/service/automation/FileWatcher.js +1 -3
- package/lib/service/automation/handlers/CreateHandler.js +3 -5
- package/lib/service/automation/handlers/GuardHandler.js +11 -32
- package/lib/service/automation/handlers/SearchHandler.js +9 -9
- package/lib/service/chat/CandidateGuardrail.js +11 -6
- package/lib/service/chat/ChatAgent.js +31 -22
- package/lib/service/chat/HandoffProtocol.js +5 -2
- package/lib/service/chat/tools/composite.js +3 -2
- package/lib/service/chat/tools/index.js +60 -71
- package/lib/service/chat/tools/infrastructure.js +9 -4
- package/lib/service/chat/tools/lifecycle.js +22 -5
- package/lib/service/chat/tools/project-access.js +5 -9
- package/lib/service/chat/tools.js +1 -2
- package/lib/service/cursor/AgentInstructionsGenerator.js +33 -15
- package/lib/service/cursor/CursorDeliveryPipeline.js +2 -1
- package/lib/service/cursor/KnowledgeCompressor.js +16 -7
- package/lib/service/guard/ComplianceReporter.js +5 -2
- package/lib/service/guard/GuardCheckEngine.js +53 -26
- package/lib/service/guard/GuardCodeChecks.js +217 -188
- package/lib/service/guard/GuardCrossFileChecks.js +203 -184
- package/lib/service/guard/GuardPatternUtils.js +17 -10
- package/lib/service/module/ModuleService.js +180 -56
- package/lib/service/recipe/RecipeCandidateValidator.js +11 -8
- package/lib/service/snippet/SnippetFactory.js +3 -3
- package/lib/service/snippet/SnippetInstaller.js +35 -11
- package/lib/service/snippet/codecs/VSCodeCodec.js +2 -2
- package/lib/service/wiki/WikiGenerator.js +54 -36
- package/lib/service/wiki/WikiRenderers.js +105 -80
- package/lib/service/wiki/WikiUtils.js +217 -80
- package/lib/shared/LanguageService.js +111 -53
- package/lib/shared/PathGuard.js +0 -8
- package/package.json +3 -9
- package/scripts/bench-real-projects.mjs +29 -29
- package/scripts/generate-recipe-drafts.js +17 -27
- package/scripts/init-snippets.js +43 -24
- package/scripts/install-vscode-copilot.js +3 -19
- package/scripts/setup-mcp-config.js +0 -4
|
@@ -25,7 +25,15 @@ router.get(
|
|
|
25
25
|
'/',
|
|
26
26
|
asyncHandler(async (_req, res) => {
|
|
27
27
|
const raw = listSkills();
|
|
28
|
-
|
|
28
|
+
let parsed;
|
|
29
|
+
try {
|
|
30
|
+
parsed = JSON.parse(raw);
|
|
31
|
+
} catch {
|
|
32
|
+
return res.status(500).json({
|
|
33
|
+
success: false,
|
|
34
|
+
error: { code: 'PARSE_ERROR', message: 'Invalid response from listSkills' },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
29
37
|
|
|
30
38
|
if (!parsed.success) {
|
|
31
39
|
return res.status(500).json(parsed);
|
|
@@ -68,7 +76,15 @@ router.get(
|
|
|
68
76
|
asyncHandler(async (req, res) => {
|
|
69
77
|
const ctx = { container: req.app.locals?.container || null };
|
|
70
78
|
const raw = await suggestSkills(ctx);
|
|
71
|
-
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = JSON.parse(raw);
|
|
82
|
+
} catch {
|
|
83
|
+
return res.status(500).json({
|
|
84
|
+
success: false,
|
|
85
|
+
error: { code: 'PARSE_ERROR', message: 'Invalid response from suggestSkills' },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
72
88
|
|
|
73
89
|
if (!parsed.success) {
|
|
74
90
|
return res.status(500).json(parsed);
|
|
@@ -90,7 +106,15 @@ router.get(
|
|
|
90
106
|
const { section } = req.query;
|
|
91
107
|
|
|
92
108
|
const raw = loadSkill(null, { skillName: name, section });
|
|
93
|
-
|
|
109
|
+
let parsed;
|
|
110
|
+
try {
|
|
111
|
+
parsed = JSON.parse(raw);
|
|
112
|
+
} catch {
|
|
113
|
+
return res.status(500).json({
|
|
114
|
+
success: false,
|
|
115
|
+
error: { code: 'PARSE_ERROR', message: 'Invalid response from loadSkill' },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
94
118
|
|
|
95
119
|
if (!parsed.success) {
|
|
96
120
|
const status = parsed.error?.code === 'SKILL_NOT_FOUND' ? 404 : 400;
|
|
@@ -122,7 +146,15 @@ router.post(
|
|
|
122
146
|
overwrite,
|
|
123
147
|
createdBy: createdBy || 'manual',
|
|
124
148
|
});
|
|
125
|
-
|
|
149
|
+
let parsed;
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(raw);
|
|
152
|
+
} catch {
|
|
153
|
+
return res.status(500).json({
|
|
154
|
+
success: false,
|
|
155
|
+
error: { code: 'PARSE_ERROR', message: 'Invalid response from createSkill' },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
126
158
|
|
|
127
159
|
if (!parsed.success) {
|
|
128
160
|
const status =
|
|
@@ -155,7 +187,15 @@ router.put(
|
|
|
155
187
|
}
|
|
156
188
|
|
|
157
189
|
const raw = updateSkill(null, { name, description, content });
|
|
158
|
-
|
|
190
|
+
let parsed;
|
|
191
|
+
try {
|
|
192
|
+
parsed = JSON.parse(raw);
|
|
193
|
+
} catch {
|
|
194
|
+
return res.status(500).json({
|
|
195
|
+
success: false,
|
|
196
|
+
error: { code: 'PARSE_ERROR', message: 'Invalid response from updateSkill' },
|
|
197
|
+
});
|
|
198
|
+
}
|
|
159
199
|
|
|
160
200
|
if (!parsed.success) {
|
|
161
201
|
const status =
|
|
@@ -181,7 +221,15 @@ router.delete(
|
|
|
181
221
|
const { name } = req.params;
|
|
182
222
|
|
|
183
223
|
const raw = deleteSkill(null, { name });
|
|
184
|
-
|
|
224
|
+
let parsed;
|
|
225
|
+
try {
|
|
226
|
+
parsed = JSON.parse(raw);
|
|
227
|
+
} catch {
|
|
228
|
+
return res.status(500).json({
|
|
229
|
+
success: false,
|
|
230
|
+
error: { code: 'PARSE_ERROR', message: 'Invalid response from deleteSkill' },
|
|
231
|
+
});
|
|
232
|
+
}
|
|
185
233
|
|
|
186
234
|
if (!parsed.success) {
|
|
187
235
|
const status =
|
|
@@ -126,9 +126,10 @@ router.post(
|
|
|
126
126
|
message: String(rule.description || rule.message || '').trim(),
|
|
127
127
|
severity: rule.severity === 'error' ? 'error' : 'warning',
|
|
128
128
|
pattern: String(rule.pattern || '').trim(),
|
|
129
|
-
languages:
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
languages:
|
|
130
|
+
Array.isArray(rule.languages) && rule.languages.length > 0
|
|
131
|
+
? rule.languages
|
|
132
|
+
: ['objc', 'swift'],
|
|
132
133
|
note: rule.note != null ? String(rule.note).trim() : rule.description_cn || '',
|
|
133
134
|
dimension: ['file', 'target', 'project'].includes(rule.dimension) ? rule.dimension : '',
|
|
134
135
|
};
|
|
@@ -26,17 +26,23 @@ function _linuxBackend() {
|
|
|
26
26
|
try {
|
|
27
27
|
execSync('which wl-copy', { stdio: 'ignore', timeout: TIMEOUT });
|
|
28
28
|
return 'wl';
|
|
29
|
-
} catch {
|
|
29
|
+
} catch {
|
|
30
|
+
/* fallthrough */
|
|
31
|
+
}
|
|
30
32
|
}
|
|
31
33
|
// X11
|
|
32
34
|
try {
|
|
33
35
|
execSync('which xclip', { stdio: 'ignore', timeout: TIMEOUT });
|
|
34
36
|
return 'xclip';
|
|
35
|
-
} catch {
|
|
37
|
+
} catch {
|
|
38
|
+
/* fallthrough */
|
|
39
|
+
}
|
|
36
40
|
try {
|
|
37
41
|
execSync('which xsel', { stdio: 'ignore', timeout: TIMEOUT });
|
|
38
42
|
return 'xsel';
|
|
39
|
-
} catch {
|
|
43
|
+
} catch {
|
|
44
|
+
/* fallthrough */
|
|
45
|
+
}
|
|
40
46
|
return null;
|
|
41
47
|
}
|
|
42
48
|
|
|
@@ -60,7 +66,8 @@ export function read() {
|
|
|
60
66
|
}
|
|
61
67
|
if (PLATFORM === 'win32') {
|
|
62
68
|
return execSync('powershell.exe -NoProfile -Command Get-Clipboard', {
|
|
63
|
-
encoding: 'utf8',
|
|
69
|
+
encoding: 'utf8',
|
|
70
|
+
timeout: TIMEOUT,
|
|
64
71
|
}).replace(/\r?\n$/, '');
|
|
65
72
|
}
|
|
66
73
|
// Linux
|
|
@@ -93,7 +100,9 @@ export function write(text) {
|
|
|
93
100
|
}
|
|
94
101
|
if (PLATFORM === 'win32') {
|
|
95
102
|
execSync('powershell.exe -NoProfile -Command "$input | Set-Clipboard"', {
|
|
96
|
-
input: text,
|
|
103
|
+
input: text,
|
|
104
|
+
timeout: TIMEOUT,
|
|
105
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
97
106
|
});
|
|
98
107
|
return true;
|
|
99
108
|
}
|
|
@@ -104,11 +113,19 @@ export function write(text) {
|
|
|
104
113
|
return true;
|
|
105
114
|
}
|
|
106
115
|
if (backend === 'xclip') {
|
|
107
|
-
execSync('xclip -selection clipboard', {
|
|
116
|
+
execSync('xclip -selection clipboard', {
|
|
117
|
+
input: text,
|
|
118
|
+
timeout: TIMEOUT,
|
|
119
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
120
|
+
});
|
|
108
121
|
return true;
|
|
109
122
|
}
|
|
110
123
|
if (backend === 'xsel') {
|
|
111
|
-
execSync('xsel --clipboard --input', {
|
|
124
|
+
execSync('xsel --clipboard --input', {
|
|
125
|
+
input: text,
|
|
126
|
+
timeout: TIMEOUT,
|
|
127
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
128
|
+
});
|
|
112
129
|
return true;
|
|
113
130
|
}
|
|
114
131
|
return false;
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* This re-export shim maintains backward compatibility.
|
|
4
4
|
*/
|
|
5
5
|
export {
|
|
6
|
-
isXcodeRunning,
|
|
7
|
-
isXcodeFrontmost,
|
|
8
|
-
jumpToLineInXcode,
|
|
9
6
|
cutLineInXcode,
|
|
10
7
|
deleteLineContentInXcode,
|
|
11
|
-
pasteInXcode,
|
|
12
|
-
selectAndPasteInXcode,
|
|
13
8
|
insertAtLineStartInXcode,
|
|
9
|
+
isXcodeFrontmost,
|
|
10
|
+
isXcodeRunning,
|
|
11
|
+
jumpToLineInXcode,
|
|
12
|
+
pasteInXcode,
|
|
14
13
|
saveActiveDocumentInXcode,
|
|
14
|
+
selectAndPasteInXcode,
|
|
15
15
|
} from '../../platform/ios/xcode/XcodeAutomation.js';
|
|
@@ -10,13 +10,22 @@ import { LanguageService } from '../../shared/LanguageService.js';
|
|
|
10
10
|
import { chunk } from './Chunker.js';
|
|
11
11
|
|
|
12
12
|
const SCANNABLE_EXTENSIONS = new Set([
|
|
13
|
-
'.md',
|
|
14
|
-
'.
|
|
15
|
-
'.
|
|
13
|
+
'.md',
|
|
14
|
+
'.markdown',
|
|
15
|
+
'.txt',
|
|
16
|
+
'.swift',
|
|
17
|
+
'.m',
|
|
18
|
+
'.h',
|
|
19
|
+
'.js',
|
|
20
|
+
'.ts',
|
|
21
|
+
'.jsx',
|
|
22
|
+
'.tsx',
|
|
16
23
|
'.py',
|
|
17
|
-
'.java',
|
|
24
|
+
'.java',
|
|
25
|
+
'.kt',
|
|
18
26
|
'.go',
|
|
19
|
-
'.rs',
|
|
27
|
+
'.rs',
|
|
28
|
+
'.rb',
|
|
20
29
|
]);
|
|
21
30
|
|
|
22
31
|
export class IndexingPipeline {
|
|
@@ -17,6 +17,8 @@ import { getRealtimeService as _getRealtimeService } from '../infrastructure/rea
|
|
|
17
17
|
import { IndexingPipeline } from '../infrastructure/vector/IndexingPipeline.js';
|
|
18
18
|
// ─── P0: Vector Storage ──────────────────────────────
|
|
19
19
|
import { JsonVectorAdapter } from '../infrastructure/vector/JsonVectorAdapter.js';
|
|
20
|
+
// ─── P2: SPM ──────────────────────────────────────────
|
|
21
|
+
import { SpmService } from '../platform/ios/spm/SpmService.js';
|
|
20
22
|
import { KnowledgeRepositoryImpl } from '../repository/knowledge/KnowledgeRepository.impl.js';
|
|
21
23
|
// ─── P1: Token Usage Tracking ─────────────────────────
|
|
22
24
|
import { TokenUsageStore } from '../repository/token/TokenUsageStore.js';
|
|
@@ -44,6 +46,8 @@ import { ConfidenceRouter } from '../service/knowledge/ConfidenceRouter.js';
|
|
|
44
46
|
import { KnowledgeFileWriter } from '../service/knowledge/KnowledgeFileWriter.js';
|
|
45
47
|
import { KnowledgeGraphService } from '../service/knowledge/KnowledgeGraphService.js';
|
|
46
48
|
import { KnowledgeService } from '../service/knowledge/KnowledgeService.js';
|
|
49
|
+
// ─── v3.2: ModuleService (统一多语言模块扫描) ──────────
|
|
50
|
+
import { ModuleService } from '../service/module/ModuleService.js';
|
|
47
51
|
import { FeedbackCollector } from '../service/quality/FeedbackCollector.js';
|
|
48
52
|
// ─── P2: Quality ──────────────────────────────────────
|
|
49
53
|
import { QualityScorer } from '../service/quality/QualityScorer.js';
|
|
@@ -54,14 +58,10 @@ import { RecipeParser } from '../service/recipe/RecipeParser.js';
|
|
|
54
58
|
import { RetrievalFunnel } from '../service/search/RetrievalFunnel.js';
|
|
55
59
|
import { SearchEngine } from '../service/search/SearchEngine.js';
|
|
56
60
|
import { SkillHooks } from '../service/skills/SkillHooks.js';
|
|
61
|
+
import { VSCodeCodec } from '../service/snippet/codecs/VSCodeCodec.js';
|
|
62
|
+
import { XcodeCodec } from '../service/snippet/codecs/XcodeCodec.js';
|
|
57
63
|
import { SnippetFactory } from '../service/snippet/SnippetFactory.js';
|
|
58
64
|
import { SnippetInstaller } from '../service/snippet/SnippetInstaller.js';
|
|
59
|
-
import { XcodeCodec } from '../service/snippet/codecs/XcodeCodec.js';
|
|
60
|
-
import { VSCodeCodec } from '../service/snippet/codecs/VSCodeCodec.js';
|
|
61
|
-
// ─── P2: SPM ──────────────────────────────────────────
|
|
62
|
-
import { SpmService } from '../platform/ios/spm/SpmService.js';
|
|
63
|
-
// ─── v3.2: ModuleService (统一多语言模块扫描) ──────────
|
|
64
|
-
import { ModuleService } from '../service/module/ModuleService.js';
|
|
65
65
|
import { DimensionCopy } from '../shared/DimensionCopyRegistry.js';
|
|
66
66
|
import { LanguageService } from '../shared/LanguageService.js';
|
|
67
67
|
|
|
@@ -107,6 +107,14 @@ export class ServiceContainer {
|
|
|
107
107
|
this.singletons._projectRoot = bootstrapComponents.projectRoot;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
if (bootstrapComponents.config) {
|
|
111
|
+
this.singletons._config = bootstrapComponents.config;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (bootstrapComponents.skillHooks) {
|
|
115
|
+
this.singletons.skillHooks = bootstrapComponents.skillHooks;
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
// AiFactory 模块引用(用于 SpmService AI 扫描)
|
|
111
119
|
try {
|
|
112
120
|
this.singletons._aiFactory = await import('../external/ai/AiFactory.js');
|
|
@@ -222,10 +230,14 @@ export class ServiceContainer {
|
|
|
222
230
|
this.logger.info('Embedding fallback provider re-created', { provider: fb });
|
|
223
231
|
break;
|
|
224
232
|
}
|
|
225
|
-
} catch {
|
|
233
|
+
} catch {
|
|
234
|
+
/* skip */
|
|
235
|
+
}
|
|
226
236
|
}
|
|
227
237
|
}
|
|
228
|
-
} catch {
|
|
238
|
+
} catch {
|
|
239
|
+
/* no embed fallback available */
|
|
240
|
+
}
|
|
229
241
|
}
|
|
230
242
|
|
|
231
243
|
// 清除持有旧 aiProvider 引用的 singleton 缓存
|
|
@@ -393,7 +405,9 @@ export class ServiceContainer {
|
|
|
393
405
|
let guardCheckEngine = null;
|
|
394
406
|
try {
|
|
395
407
|
guardCheckEngine = this.get('guardCheckEngine');
|
|
396
|
-
} catch {
|
|
408
|
+
} catch {
|
|
409
|
+
/* engine not yet available */
|
|
410
|
+
}
|
|
397
411
|
this.singletons.guardService = new GuardService(knowledgeRepository, auditLogger, gateway, {
|
|
398
412
|
guardCheckEngine,
|
|
399
413
|
});
|
|
@@ -538,7 +552,10 @@ export class ServiceContainer {
|
|
|
538
552
|
if (!this.singletons.vscodeSnippetInstaller) {
|
|
539
553
|
const factory = this.get('snippetFactory');
|
|
540
554
|
const codec = factory.getCodec('vscode');
|
|
541
|
-
this.singletons.vscodeSnippetInstaller = new SnippetInstaller({
|
|
555
|
+
this.singletons.vscodeSnippetInstaller = new SnippetInstaller({
|
|
556
|
+
codec,
|
|
557
|
+
snippetFactory: factory,
|
|
558
|
+
});
|
|
542
559
|
}
|
|
543
560
|
return this.singletons.vscodeSnippetInstaller;
|
|
544
561
|
});
|
|
@@ -716,9 +733,15 @@ export class ServiceContainer {
|
|
|
716
733
|
});
|
|
717
734
|
|
|
718
735
|
// SkillHooks (Skill 生命周期钩子 — 加载 skills/*/hooks.js)
|
|
736
|
+
// 注意: 优先使用 bootstrap 注入的已加载实例,避免创建未 .load() 的空实例
|
|
719
737
|
this.register('skillHooks', () => {
|
|
720
738
|
if (!this.singletons.skillHooks) {
|
|
721
|
-
|
|
739
|
+
const hooks = new SkillHooks();
|
|
740
|
+
// 同步返回空实例;异步 load 在后台执行
|
|
741
|
+
hooks.load().catch(() => {
|
|
742
|
+
/* skill hooks load is best-effort */
|
|
743
|
+
});
|
|
744
|
+
this.singletons.skillHooks = hooks;
|
|
722
745
|
}
|
|
723
746
|
return this.singletons.skillHooks;
|
|
724
747
|
});
|
|
@@ -26,38 +26,33 @@
|
|
|
26
26
|
* 旧路径保留了 re-export shim 确保向后兼容。
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
+
// ── SPM Legacy Routes ──
|
|
30
|
+
export { default as spmRouter } from './routes/spm.js';
|
|
31
|
+
export { PlaceholderConverter } from './snippet/PlaceholderConverter.js';
|
|
32
|
+
// ── Xcode Snippet 编解码 ──
|
|
33
|
+
export { XcodeCodec } from './snippet/XcodeCodec.js';
|
|
34
|
+
export { DependencyGraph } from './spm/DependencyGraph.js';
|
|
35
|
+
export { PackageSwiftParser } from './spm/PackageSwiftParser.js';
|
|
36
|
+
export { PolicyEngine } from './spm/PolicyEngine.js';
|
|
37
|
+
export { SpmDiscoverer } from './spm/SpmDiscoverer.js';
|
|
38
|
+
// ── Swift Package Manager ──
|
|
39
|
+
export { SpmService } from './spm/SpmService.js';
|
|
40
|
+
export { saveEventFilter } from './xcode/SaveEventFilter.js';
|
|
29
41
|
// ── Xcode IDE 自动化 ──
|
|
30
42
|
export {
|
|
31
|
-
isXcodeRunning,
|
|
32
|
-
isXcodeFrontmost,
|
|
33
|
-
jumpToLineInXcode,
|
|
34
43
|
cutLineInXcode,
|
|
35
44
|
deleteLineContentInXcode,
|
|
36
|
-
pasteInXcode,
|
|
37
|
-
selectAndPasteInXcode,
|
|
38
45
|
insertAtLineStartInXcode,
|
|
46
|
+
isXcodeFrontmost,
|
|
47
|
+
isXcodeRunning,
|
|
48
|
+
jumpToLineInXcode,
|
|
49
|
+
pasteInXcode,
|
|
39
50
|
saveActiveDocumentInXcode,
|
|
51
|
+
selectAndPasteInXcode,
|
|
40
52
|
} from './xcode/XcodeAutomation.js';
|
|
41
|
-
|
|
42
53
|
export {
|
|
43
|
-
insertHeaders,
|
|
44
|
-
insertCodeToXcode,
|
|
45
|
-
findTriggerLineNumber,
|
|
46
54
|
findImportInsertLine,
|
|
55
|
+
findTriggerLineNumber,
|
|
56
|
+
insertCodeToXcode,
|
|
57
|
+
insertHeaders,
|
|
47
58
|
} from './xcode/XcodeIntegration.js';
|
|
48
|
-
|
|
49
|
-
export { saveEventFilter } from './xcode/SaveEventFilter.js';
|
|
50
|
-
|
|
51
|
-
// ── Xcode Snippet 编解码 ──
|
|
52
|
-
export { XcodeCodec } from './snippet/XcodeCodec.js';
|
|
53
|
-
export { PlaceholderConverter } from './snippet/PlaceholderConverter.js';
|
|
54
|
-
|
|
55
|
-
// ── Swift Package Manager ──
|
|
56
|
-
export { SpmService } from './spm/SpmService.js';
|
|
57
|
-
export { SpmDiscoverer } from './spm/SpmDiscoverer.js';
|
|
58
|
-
export { PackageSwiftParser } from './spm/PackageSwiftParser.js';
|
|
59
|
-
export { DependencyGraph } from './spm/DependencyGraph.js';
|
|
60
|
-
export { PolicyEngine } from './spm/PolicyEngine.js';
|
|
61
|
-
|
|
62
|
-
// ── SPM Legacy Routes ──
|
|
63
|
-
export { default as spmRouter } from './routes/spm.js';
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import express from 'express';
|
|
10
|
+
import { asyncHandler } from '../../../http/middleware/errorHandler.js';
|
|
11
|
+
import { createStreamSession, getStreamSession } from '../../../http/utils/sse-sessions.js';
|
|
10
12
|
import Logger from '../../../infrastructure/logging/Logger.js';
|
|
11
13
|
import { getServiceContainer } from '../../../injection/ServiceContainer.js';
|
|
12
14
|
import { ValidationError } from '../../../shared/errors/index.js';
|
|
13
|
-
import { asyncHandler } from '../../../http/middleware/errorHandler.js';
|
|
14
|
-
import { createStreamSession, getStreamSession } from '../../../http/utils/sse-sessions.js';
|
|
15
15
|
|
|
16
16
|
const router = express.Router();
|
|
17
17
|
const logger = Logger.getInstance();
|
|
@@ -241,7 +241,10 @@ router.post(
|
|
|
241
241
|
// 异步执行扫描,通过 session 推送进度事件
|
|
242
242
|
setImmediate(async () => {
|
|
243
243
|
try {
|
|
244
|
-
logger.info('Module stream scan started via /spm/', {
|
|
244
|
+
logger.info('Module stream scan started via /spm/', {
|
|
245
|
+
target: tName,
|
|
246
|
+
sessionId: session.sessionId,
|
|
247
|
+
});
|
|
245
248
|
const result = await moduleService.scanTarget(resolvedTarget, {
|
|
246
249
|
...options,
|
|
247
250
|
onProgress(event) {
|
|
@@ -20,7 +20,9 @@ export class PlaceholderConverter {
|
|
|
20
20
|
* @returns {string}
|
|
21
21
|
*/
|
|
22
22
|
static xcodeToVSCode(code) {
|
|
23
|
-
if (!code)
|
|
23
|
+
if (!code) {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
24
26
|
let index = 0;
|
|
25
27
|
return code.replace(/<#(.*?)#>/g, (_match, inner) => {
|
|
26
28
|
index++;
|
|
@@ -39,7 +41,9 @@ export class PlaceholderConverter {
|
|
|
39
41
|
* @returns {string}
|
|
40
42
|
*/
|
|
41
43
|
static vscodeToXcode(code) {
|
|
42
|
-
if (!code)
|
|
44
|
+
if (!code) {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
43
47
|
return (
|
|
44
48
|
code
|
|
45
49
|
// ${1:name} → <#name#>
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* - XML 转义
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { join } from 'node:path';
|
|
11
10
|
import { homedir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
12
|
import { SnippetCodec } from '../../../service/snippet/codecs/SnippetCodec.js';
|
|
13
13
|
|
|
14
14
|
const XCODE_LANGUAGE_MAP = {
|
|
@@ -102,7 +102,9 @@ export class XcodeCodec extends SnippetCodec {
|
|
|
102
102
|
|
|
103
103
|
/** XML 特殊字符转义 */
|
|
104
104
|
function escapeXml(str) {
|
|
105
|
-
if (!str)
|
|
105
|
+
if (!str) {
|
|
106
|
+
return '';
|
|
107
|
+
}
|
|
106
108
|
return String(str)
|
|
107
109
|
.replace(/&/g, '&')
|
|
108
110
|
.replace(/</g, '<')
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import { existsSync, readdirSync } from 'node:fs';
|
|
9
9
|
import { basename, join, relative } from 'node:path';
|
|
10
|
-
import { LanguageService } from '../../../shared/LanguageService.js';
|
|
11
10
|
import { ProjectDiscoverer } from '../../../core/discovery/ProjectDiscoverer.js';
|
|
11
|
+
import { LanguageService } from '../../../shared/LanguageService.js';
|
|
12
12
|
|
|
13
13
|
export class SpmDiscoverer extends ProjectDiscoverer {
|
|
14
14
|
/** @type {import('./SpmService.js').SpmService|null} */
|
|
@@ -1002,7 +1002,9 @@ export class SpmService {
|
|
|
1002
1002
|
// 加载语言参考 Skill 注入 AI 提取 prompt
|
|
1003
1003
|
let extractOpts = {};
|
|
1004
1004
|
try {
|
|
1005
|
-
const { loadBootstrapSkills } = await import(
|
|
1005
|
+
const { loadBootstrapSkills } = await import(
|
|
1006
|
+
'../../../external/mcp/handlers/bootstrap.js'
|
|
1007
|
+
);
|
|
1006
1008
|
const langProfile = ai._detectLanguageProfile?.(files);
|
|
1007
1009
|
const primaryLang = langProfile?.primaryLanguage;
|
|
1008
1010
|
if (primaryLang) {
|
|
@@ -17,21 +17,21 @@ import { readFileSync, writeFileSync } from 'node:fs';
|
|
|
17
17
|
import { saveEventFilter } from './SaveEventFilter.js';
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
|
-
|
|
20
|
+
checkImportStatus,
|
|
21
21
|
collectImportsFromFile,
|
|
22
22
|
collectImportsFromHeaderFile,
|
|
23
|
-
checkImportStatus,
|
|
24
23
|
inferModulesFromHeaders,
|
|
24
|
+
resolveHeaderFormat,
|
|
25
25
|
} from './XcodeImportResolver.js';
|
|
26
26
|
|
|
27
27
|
import {
|
|
28
|
-
|
|
29
|
-
withAutoSnippetNote,
|
|
28
|
+
computePasteLineNumber,
|
|
30
29
|
evaluateDepResult,
|
|
31
30
|
handleDepReview,
|
|
32
|
-
|
|
31
|
+
sleep,
|
|
32
|
+
withAutoSnippetNote,
|
|
33
33
|
writeImportLineFile,
|
|
34
|
-
|
|
34
|
+
writeImportLineXcode,
|
|
35
35
|
} from './XcodeWriteUtils.js';
|
|
36
36
|
|
|
37
37
|
/** 常见 Apple 系统框架(无需 SPM 依赖检查) */
|
|
@@ -401,12 +401,10 @@ export async function insertCodeToXcode(watcher, fullPath, selected, triggerLine
|
|
|
401
401
|
// ── Step 6: 计算偏移后的粘贴行号 ──
|
|
402
402
|
// 使用实际插入的 header 数量计算偏移,而非期望数量
|
|
403
403
|
// 当 headers 全部重复被跳过时,headerInsertCount = 0,不应偏移
|
|
404
|
-
const pasteLineNumber = computePasteLineNumber(
|
|
405
|
-
|
|
406
|
-
headerInsertCount,
|
|
407
|
-
|
|
408
|
-
{ forceOffset: headerInsertCount > 0, expectedHeaderCount: headerInsertCount }
|
|
409
|
-
);
|
|
404
|
+
const pasteLineNumber = computePasteLineNumber(triggerLineNumber, headerInsertCount, fullPath, {
|
|
405
|
+
forceOffset: headerInsertCount > 0,
|
|
406
|
+
expectedHeaderCount: headerInsertCount,
|
|
407
|
+
});
|
|
410
408
|
|
|
411
409
|
// 如果 headers 通过文件写入,等待 Xcode reload
|
|
412
410
|
if (headerInsertCount > 0) {
|
|
@@ -203,7 +203,12 @@ export function getLastImportLine(filePath) {
|
|
|
203
203
|
*
|
|
204
204
|
* 如果 headers 插入在 trigger 行之前(import 区),trigger 行号需要向下偏移。
|
|
205
205
|
*/
|
|
206
|
-
export function computePasteLineNumber(
|
|
206
|
+
export function computePasteLineNumber(
|
|
207
|
+
triggerLineNumber,
|
|
208
|
+
headerInsertCount,
|
|
209
|
+
filePath,
|
|
210
|
+
options = {}
|
|
211
|
+
) {
|
|
207
212
|
const expectedCount = Number.isFinite(options.expectedHeaderCount)
|
|
208
213
|
? options.expectedHeaderCount
|
|
209
214
|
: headerInsertCount;
|
|
@@ -12,17 +12,16 @@
|
|
|
12
12
|
import { accessSync, readFileSync, statSync } from 'node:fs';
|
|
13
13
|
import { basename, join, normalize } from 'node:path';
|
|
14
14
|
import { watch as chokidarWatch } from 'chokidar';
|
|
15
|
+
import { saveEventFilter } from '../../platform/ios/xcode/SaveEventFilter.js';
|
|
15
16
|
import { FILE_WATCHER } from '../../shared/constants.js';
|
|
16
17
|
import { detectTriggers, REGEX } from './DirectiveDetector.js';
|
|
17
18
|
import { handleAlink } from './handlers/AlinkHandler.js';
|
|
18
|
-
|
|
19
19
|
/* ── Handler imports ── */
|
|
20
20
|
import { handleCreate } from './handlers/CreateHandler.js';
|
|
21
21
|
import { handleDraft } from './handlers/DraftHandler.js';
|
|
22
22
|
import { handleGuard } from './handlers/GuardHandler.js';
|
|
23
23
|
import { handleHeader } from './handlers/HeaderHandler.js';
|
|
24
24
|
import { handleSearch } from './handlers/SearchHandler.js';
|
|
25
|
-
import { saveEventFilter } from '../../platform/ios/xcode/SaveEventFilter.js';
|
|
26
25
|
|
|
27
26
|
/* ────────── 配置 ────────── */
|
|
28
27
|
|
|
@@ -118,7 +117,6 @@ export class FileWatcher {
|
|
|
118
117
|
: DEFAULT_FILE_PATTERN;
|
|
119
118
|
|
|
120
119
|
if (!this.quiet) {
|
|
121
|
-
console.log('\n👁️ 文件监听已启动 — Xcode 模式');
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
this._watcher = chokidarWatch(filePattern, {
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { saveEventFilter } from '../../../platform/ios/xcode/SaveEventFilter.js';
|
|
7
8
|
import { LanguageService } from '../../../shared/LanguageService.js';
|
|
8
9
|
import { REGEX } from '../DirectiveDetector.js';
|
|
9
|
-
import { saveEventFilter } from '../../../platform/ios/xcode/SaveEventFilter.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* 处理 // as:c 指令
|
|
@@ -121,11 +121,9 @@ async function silentCreateCandidate(watcher, text, relativePath) {
|
|
|
121
121
|
|
|
122
122
|
// -c 模式:剪贴板内容整体作为一条候选(不拆分)
|
|
123
123
|
// 先用 AI 生成标题和摘要,code 保持剪贴板原文
|
|
124
|
-
const lang = relativePath ?
|
|
124
|
+
const lang = relativePath ? LanguageService.inferLang(relativePath) || 'unknown' : 'unknown';
|
|
125
125
|
const ext = LanguageService.extForLang(lang) || '.txt';
|
|
126
|
-
const fileName = relativePath
|
|
127
|
-
? relativePath.split('/').pop()
|
|
128
|
-
: `clipboard${ext}`;
|
|
126
|
+
const fileName = relativePath ? relativePath.split('/').pop() : `clipboard${ext}`;
|
|
129
127
|
|
|
130
128
|
let title = fileName.replace(/\.\w+$/, '') || 'Clipboard Snippet';
|
|
131
129
|
let summary = '';
|