autosnippet 2.6.0 → 2.7.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.
- package/README.md +137 -65
- package/bin/api-server.js +5 -0
- package/bin/cli.js +6 -1
- package/dashboard/dist/assets/{icons-rnn04CvH.js → icons-B_Xg4B-s.js} +148 -88
- package/dashboard/dist/assets/index-BjfUm8p9.js +197 -0
- package/dashboard/dist/assets/index-CkIih2CC.css +1 -0
- package/dashboard/dist/assets/{react-markdown-CWxUbOf4.js → react-markdown-BA6FB2NP.js} +1 -1
- package/dashboard/dist/assets/{syntax-highlighter-CJ2drQQb.js → syntax-highlighter-CVLHn9O5.js} +1 -1
- package/dashboard/dist/assets/{vendor-f83ah6cm.js → vendor-BotF760a.js} +61 -61
- package/dashboard/dist/index.html +6 -6
- package/lib/bootstrap.js +18 -1
- package/lib/cli/SetupService.js +86 -8
- package/lib/cli/UpgradeService.js +139 -2
- package/lib/core/ast/ProjectGraph.js +599 -0
- package/lib/core/gateway/GatewayActionRegistry.js +2 -2
- package/lib/domain/recipe/Recipe.js +3 -0
- package/lib/external/ai/AiProvider.js +83 -20
- package/lib/external/ai/providers/ClaudeProvider.js +208 -0
- package/lib/external/ai/providers/GoogleGeminiProvider.js +247 -1
- package/lib/external/ai/providers/OpenAiProvider.js +141 -0
- package/lib/external/mcp/McpServer.js +6 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +216 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +657 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +160 -0
- package/lib/external/mcp/handlers/bootstrap/skills.js +225 -0
- package/lib/external/mcp/handlers/bootstrap.js +159 -1634
- package/lib/external/mcp/handlers/browse.js +1 -1
- package/lib/external/mcp/handlers/candidate.js +1 -33
- package/lib/external/mcp/handlers/skill.js +58 -17
- package/lib/external/mcp/tools.js +4 -3
- package/lib/http/middleware/requestLogger.js +23 -4
- package/lib/http/routes/ai.js +158 -2
- package/lib/http/routes/auth.js +3 -2
- package/lib/http/routes/candidates.js +49 -25
- package/lib/http/routes/commands.js +0 -8
- package/lib/http/routes/guardRules.js +1 -16
- package/lib/http/routes/recipes.js +4 -17
- package/lib/http/routes/search.js +11 -19
- package/lib/http/routes/skills.js +2 -0
- package/lib/http/routes/snippets.js +0 -33
- package/lib/http/routes/spm.js +37 -63
- package/lib/http/utils/routeHelpers.js +31 -0
- package/lib/infrastructure/config/Paths.js +12 -0
- package/lib/infrastructure/database/DatabaseConnection.js +6 -1
- package/lib/infrastructure/logging/Logger.js +86 -3
- package/lib/infrastructure/realtime/RealtimeService.js +2 -5
- package/lib/infrastructure/vector/JsonVectorAdapter.js +26 -1
- package/lib/injection/ServiceContainer.js +55 -2
- package/lib/service/bootstrap/BootstrapTaskManager.js +400 -0
- package/lib/service/candidate/CandidateFileWriter.js +72 -27
- package/lib/service/candidate/CandidateService.js +156 -10
- package/lib/service/chat/AnalystAgent.js +245 -0
- package/lib/service/chat/CandidateGuardrail.js +134 -0
- package/lib/service/chat/ChatAgent.js +1055 -167
- package/lib/service/chat/ContextWindow.js +730 -0
- package/lib/service/chat/ConversationStore.js +3 -0
- package/lib/service/chat/HandoffProtocol.js +181 -0
- package/lib/service/chat/Memory.js +3 -0
- package/lib/service/chat/ProducerAgent.js +293 -0
- package/lib/service/chat/ToolRegistry.js +149 -5
- package/lib/service/chat/tools.js +1404 -61
- package/lib/service/guard/ExclusionManager.js +2 -0
- package/lib/service/guard/RuleLearner.js +2 -0
- package/lib/service/quality/FeedbackCollector.js +2 -0
- package/lib/service/recipe/RecipeFileWriter.js +16 -1
- package/lib/service/recipe/RecipeStatsTracker.js +2 -0
- package/lib/service/skills/SignalCollector.js +33 -6
- package/lib/service/skills/SkillAdvisor.js +2 -1
- package/lib/service/skills/SkillHooks.js +13 -5
- package/lib/service/spm/SpmService.js +2 -2
- package/lib/shared/PathGuard.js +314 -0
- package/package.json +1 -1
- package/resources/native-ui/combined-window.swift +494 -0
- package/templates/copilot-instructions.md +20 -3
- package/templates/cursor-rules/autosnippet-conventions.mdc +21 -4
- package/templates/cursor-rules/autosnippet-skills.mdc +45 -0
- package/dashboard/dist/assets/index-BBKa3Dgi.js +0 -195
- package/dashboard/dist/assets/index-DLsECfzW.css +0 -1
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* AutoSnippet 组合窗口 - 列表 + 预览一体化
|
|
6
|
+
* 左侧:搜索结果列表
|
|
7
|
+
* 右侧:代码预览(实时更新)
|
|
8
|
+
* 底部:操作按钮
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// MARK: - 语法高亮
|
|
12
|
+
|
|
13
|
+
struct SyntaxHighlighter {
|
|
14
|
+
static let keywords = ["func", "var", "let", "if", "else", "guard", "return", "import", "class", "struct", "enum", "protocol", "extension", "private", "public", "internal", "fileprivate", "static", "override", "init", "deinit", "self", "super", "nil", "true", "false", "for", "while", "repeat", "switch", "case", "default", "break", "continue", "fallthrough", "where", "in", "throws", "throw", "try", "catch", "async", "await", "typealias", "associatedtype", "weak", "unowned", "lazy", "final", "required", "convenience", "mutating", "nonmutating", "open", "inout", "some", "any",
|
|
15
|
+
// Objective-C
|
|
16
|
+
"@interface", "@implementation", "@end", "@property", "@synthesize", "@dynamic", "@class", "@protocol", "@optional", "@required", "@public", "@private", "@protected", "@package", "@selector", "@encode", "@synchronized", "@autoreleasepool", "@try", "@catch", "@finally", "@throw", "YES", "NO", "NULL", "nil", "self", "super", "id", "Class", "SEL", "IMP", "BOOL", "instancetype", "void", "char", "short", "int", "long", "float", "double", "signed", "unsigned", "const", "static", "extern", "auto", "register", "volatile", "inline", "restrict", "typedef", "sizeof", "typeof", "return", "if", "else", "for", "while", "do", "switch", "case", "default", "break", "continue", "goto", "struct", "union", "enum",
|
|
17
|
+
// TypeScript/JavaScript
|
|
18
|
+
"function", "const", "async", "await", "new", "this", "typeof", "instanceof", "export", "import", "from", "as", "default", "extends", "implements", "interface", "type", "namespace", "module", "declare", "abstract", "readonly", "keyof", "infer", "never", "unknown", "any", "void", "null", "undefined", "number", "string", "boolean", "symbol", "bigint", "object"
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
static let typeKeywords = ["String", "Int", "Double", "Float", "Bool", "Array", "Dictionary", "Set", "Optional", "Any", "AnyObject", "Void", "Never", "Error", "Result", "URL", "Data", "Date", "UUID", "NSObject", "NSString", "NSNumber", "NSArray", "NSDictionary", "NSSet", "NSData", "NSDate", "NSURL", "CGFloat", "CGPoint", "CGSize", "CGRect", "UIView", "UIViewController", "UIButton", "UILabel", "UIImage", "UIColor", "NSView", "NSViewController", "NSButton", "NSTextField", "NSImage", "NSColor", "Promise", "Observable", "Subject"]
|
|
22
|
+
|
|
23
|
+
static func highlight(_ code: String) -> NSAttributedString {
|
|
24
|
+
let attributed = NSMutableAttributedString(string: code)
|
|
25
|
+
let fullRange = NSRange(location: 0, length: code.utf16.count)
|
|
26
|
+
|
|
27
|
+
// 基础样式 - SF Mono 字体 (Xcode 默认) - 增大到 14
|
|
28
|
+
let baseFont = NSFont(name: "SFMono-Regular", size: 14) ?? NSFont.monospacedSystemFont(ofSize: 14, weight: .regular)
|
|
29
|
+
// 普通文本 - Xcode 默认浅灰白色
|
|
30
|
+
let baseColor = NSColor(calibratedRed: 0.83, green: 0.84, blue: 0.85, alpha: 1.0) // #D4D4D6
|
|
31
|
+
attributed.addAttribute(.font, value: baseFont, range: fullRange)
|
|
32
|
+
attributed.addAttribute(.foregroundColor, value: baseColor, range: fullRange)
|
|
33
|
+
|
|
34
|
+
// 高亮注释 - Xcode 默认绿色 (系统 Green)
|
|
35
|
+
let commentColor = NSColor(calibratedRed: 0.42, green: 0.75, blue: 0.31, alpha: 1.0) // #6BBF4F
|
|
36
|
+
highlightPattern(attributed, pattern: "//.*$", color: commentColor, options: .anchorsMatchLines)
|
|
37
|
+
highlightPattern(attributed, pattern: "/\\*[\\s\\S]*?\\*/", color: commentColor)
|
|
38
|
+
|
|
39
|
+
// 高亮字符串 - Xcode 默认橙红色 (系统 Red/Orange 混合)
|
|
40
|
+
let stringColor = NSColor(calibratedRed: 0.98, green: 0.42, blue: 0.33, alpha: 1.0) // #FA6B54
|
|
41
|
+
highlightPattern(attributed, pattern: "\"(?:[^\"\\\\]|\\\\.)*\"", color: stringColor)
|
|
42
|
+
highlightPattern(attributed, pattern: "'(?:[^'\\\\]|\\\\.)*'", color: stringColor)
|
|
43
|
+
highlightPattern(attributed, pattern: "@\"(?:[^\"\\\\]|\\\\.)*\"", color: stringColor) // ObjC string
|
|
44
|
+
|
|
45
|
+
// 高亮数字 - Xcode 默认紫色 (Light Purple)
|
|
46
|
+
let numberColor = NSColor(calibratedRed: 0.69, green: 0.54, blue: 0.89, alpha: 1.0) // #B08AE3
|
|
47
|
+
highlightPattern(attributed, pattern: "\\b\\d+\\.?\\d*\\b", color: numberColor)
|
|
48
|
+
|
|
49
|
+
// 高亮类型关键字 - Xcode 默认青绿色 (Teal/Cyan)
|
|
50
|
+
let typeColor = NSColor(calibratedRed: 0.40, green: 0.84, blue: 0.89, alpha: 1.0) // #66D7E3
|
|
51
|
+
for keyword in typeKeywords {
|
|
52
|
+
highlightWord(attributed, word: keyword, color: typeColor)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 高亮关键字 - Xcode 默认粉紫色 (Magenta/Pink)
|
|
56
|
+
let keywordColor = NSColor(calibratedRed: 0.98, green: 0.42, blue: 0.69, alpha: 1.0) // #FA6BB0
|
|
57
|
+
// Objective-C @ 前缀关键字 - 使用浅棕色
|
|
58
|
+
let atKeywordColor = NSColor(calibratedRed: 0.83, green: 0.60, blue: 0.45, alpha: 1.0) // #D49973
|
|
59
|
+
for keyword in keywords {
|
|
60
|
+
if keyword.hasPrefix("@") {
|
|
61
|
+
highlightPattern(attributed, pattern: "\(keyword)\\b", color: atKeywordColor)
|
|
62
|
+
} else {
|
|
63
|
+
highlightWord(attributed, word: keyword, color: keywordColor)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return attributed
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private static func highlightPattern(_ attributed: NSMutableAttributedString, pattern: String, color: NSColor, options: NSRegularExpression.Options = []) {
|
|
71
|
+
guard let regex = try? NSRegularExpression(pattern: pattern, options: options) else { return }
|
|
72
|
+
let string = attributed.string
|
|
73
|
+
let range = NSRange(location: 0, length: string.utf16.count)
|
|
74
|
+
|
|
75
|
+
for match in regex.matches(in: string, options: [], range: range) {
|
|
76
|
+
attributed.addAttribute(.foregroundColor, value: color, range: match.range)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private static func highlightWord(_ attributed: NSMutableAttributedString, word: String, color: NSColor) {
|
|
81
|
+
let pattern = "\\b\(NSRegularExpression.escapedPattern(for: word))\\b"
|
|
82
|
+
highlightPattern(attributed, pattern: pattern, color: color)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// MARK: - 数据结构
|
|
87
|
+
|
|
88
|
+
struct SearchItem {
|
|
89
|
+
let title: String
|
|
90
|
+
let code: String
|
|
91
|
+
let explanation: String
|
|
92
|
+
let groupSize: Int
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// MARK: - Combined Window Controller
|
|
96
|
+
|
|
97
|
+
class CombinedSearchWindowController: NSObject, NSTableViewDataSource, NSTableViewDelegate, NSSearchFieldDelegate {
|
|
98
|
+
private var panel: NSPanel!
|
|
99
|
+
private var tableView: NSTableView!
|
|
100
|
+
private var searchField: NSSearchField!
|
|
101
|
+
private var codeTextView: NSTextView!
|
|
102
|
+
private var allItems: [SearchItem] = []
|
|
103
|
+
private var filteredItems: [SearchItem] = []
|
|
104
|
+
private var selectedIndex: Int = -1
|
|
105
|
+
private var confirmed: Bool = false
|
|
106
|
+
|
|
107
|
+
enum Result {
|
|
108
|
+
case confirmed(Int) // 用户确认插入,返回选中的索引
|
|
109
|
+
case cancelled // 用户取消
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func show(items: [SearchItem], keyword: String) -> Result {
|
|
113
|
+
self.allItems = items
|
|
114
|
+
self.filteredItems = items
|
|
115
|
+
|
|
116
|
+
let panelWidth: CGFloat = 1400
|
|
117
|
+
let panelHeight: CGFloat = 680
|
|
118
|
+
|
|
119
|
+
// 创建面板
|
|
120
|
+
panel = NSPanel(
|
|
121
|
+
contentRect: NSRect(x: 0, y: 0, width: panelWidth, height: panelHeight),
|
|
122
|
+
styleMask: [.titled, .closable, .resizable, .fullSizeContentView],
|
|
123
|
+
backing: .buffered,
|
|
124
|
+
defer: false
|
|
125
|
+
)
|
|
126
|
+
panel.title = "AutoSnippet 搜索结果"
|
|
127
|
+
panel.level = .floating
|
|
128
|
+
panel.center()
|
|
129
|
+
panel.minSize = NSSize(width: 1000, height: 500)
|
|
130
|
+
panel.titlebarAppearsTransparent = true
|
|
131
|
+
panel.isMovableByWindowBackground = true
|
|
132
|
+
|
|
133
|
+
// 模糊背景
|
|
134
|
+
let visualEffect = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: panelWidth, height: panelHeight))
|
|
135
|
+
visualEffect.material = .hudWindow
|
|
136
|
+
visualEffect.blendingMode = .behindWindow
|
|
137
|
+
visualEffect.state = .active
|
|
138
|
+
visualEffect.autoresizingMask = [.width, .height]
|
|
139
|
+
panel.contentView = visualEffect
|
|
140
|
+
|
|
141
|
+
let contentView = NSView(frame: NSRect(x: 0, y: 0, width: panelWidth, height: panelHeight))
|
|
142
|
+
contentView.autoresizingMask = [.width, .height]
|
|
143
|
+
visualEffect.addSubview(contentView)
|
|
144
|
+
|
|
145
|
+
// 顶部标题和搜索框
|
|
146
|
+
let titleLabel = NSTextField(labelWithString: "找到 \(items.count) 个匹配")
|
|
147
|
+
titleLabel.font = NSFont.systemFont(ofSize: 13, weight: .medium)
|
|
148
|
+
titleLabel.textColor = .secondaryLabelColor
|
|
149
|
+
titleLabel.frame = NSRect(x: 20, y: panelHeight - 50, width: 200, height: 20)
|
|
150
|
+
titleLabel.autoresizingMask = [.minYMargin]
|
|
151
|
+
contentView.addSubview(titleLabel)
|
|
152
|
+
|
|
153
|
+
// 搜索框
|
|
154
|
+
searchField = NSSearchField(frame: NSRect(x: 20, y: panelHeight - 85, width: 350, height: 28))
|
|
155
|
+
searchField.placeholderString = "过滤结果..."
|
|
156
|
+
searchField.stringValue = keyword
|
|
157
|
+
searchField.autoresizingMask = [.width, .minYMargin]
|
|
158
|
+
searchField.target = self
|
|
159
|
+
searchField.action = #selector(searchFieldDidChange)
|
|
160
|
+
searchField.font = NSFont.systemFont(ofSize: 13)
|
|
161
|
+
contentView.addSubview(searchField)
|
|
162
|
+
|
|
163
|
+
// 分隔线
|
|
164
|
+
let separator = NSBox(frame: NSRect(x: 0, y: panelHeight - 95, width: panelWidth, height: 1))
|
|
165
|
+
separator.boxType = .separator
|
|
166
|
+
separator.autoresizingMask = [.width, .minYMargin]
|
|
167
|
+
contentView.addSubview(separator)
|
|
168
|
+
|
|
169
|
+
// 左侧:列表区域
|
|
170
|
+
let listWidth: CGFloat = 360
|
|
171
|
+
let listScrollView = NSScrollView(frame: NSRect(x: 20, y: 70, width: listWidth, height: panelHeight - 175))
|
|
172
|
+
listScrollView.hasVerticalScroller = true
|
|
173
|
+
listScrollView.borderType = .noBorder
|
|
174
|
+
listScrollView.autoresizingMask = [.height, .minYMargin]
|
|
175
|
+
listScrollView.wantsLayer = true
|
|
176
|
+
listScrollView.layer?.cornerRadius = 8
|
|
177
|
+
listScrollView.layer?.masksToBounds = true
|
|
178
|
+
listScrollView.backgroundColor = NSColor(calibratedRed: 0.12, green: 0.12, blue: 0.14, alpha: 1.0)
|
|
179
|
+
// 使用现代 overlay 滚动条样式(悬浮自动隐藏)
|
|
180
|
+
listScrollView.scrollerStyle = .overlay
|
|
181
|
+
listScrollView.autohidesScrollers = true
|
|
182
|
+
listScrollView.scrollerKnobStyle = .light
|
|
183
|
+
// 强制刷新滚动条样式
|
|
184
|
+
listScrollView.flashScrollers()
|
|
185
|
+
|
|
186
|
+
tableView = NSTableView()
|
|
187
|
+
tableView.headerView = nil
|
|
188
|
+
tableView.rowHeight = 60
|
|
189
|
+
tableView.intercellSpacing = NSSize(width: 0, height: 2)
|
|
190
|
+
tableView.backgroundColor = NSColor(calibratedRed: 0.12, green: 0.12, blue: 0.14, alpha: 1.0)
|
|
191
|
+
tableView.usesAlternatingRowBackgroundColors = false
|
|
192
|
+
tableView.selectionHighlightStyle = .regular
|
|
193
|
+
tableView.target = self
|
|
194
|
+
tableView.action = #selector(tableViewClicked)
|
|
195
|
+
tableView.doubleAction = #selector(tableViewDoubleClicked)
|
|
196
|
+
|
|
197
|
+
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("item"))
|
|
198
|
+
column.width = listWidth - 25
|
|
199
|
+
tableView.addTableColumn(column)
|
|
200
|
+
|
|
201
|
+
tableView.dataSource = self
|
|
202
|
+
tableView.delegate = self
|
|
203
|
+
|
|
204
|
+
listScrollView.documentView = tableView
|
|
205
|
+
contentView.addSubview(listScrollView)
|
|
206
|
+
|
|
207
|
+
// 右侧:代码预览区域
|
|
208
|
+
let codeX = 20 + listWidth + 15
|
|
209
|
+
let codeWidth = panelWidth - codeX - 20
|
|
210
|
+
|
|
211
|
+
let previewLabel = NSTextField(labelWithString: "代码预览")
|
|
212
|
+
previewLabel.font = NSFont.systemFont(ofSize: 11, weight: .semibold)
|
|
213
|
+
previewLabel.textColor = .secondaryLabelColor
|
|
214
|
+
previewLabel.frame = NSRect(x: codeX, y: panelHeight - 115, width: codeWidth, height: 16)
|
|
215
|
+
previewLabel.autoresizingMask = [.width, .minYMargin]
|
|
216
|
+
contentView.addSubview(previewLabel)
|
|
217
|
+
|
|
218
|
+
let codeScrollView = NSScrollView(frame: NSRect(x: codeX, y: 70, width: codeWidth, height: panelHeight - 195))
|
|
219
|
+
codeScrollView.hasVerticalScroller = true
|
|
220
|
+
codeScrollView.hasHorizontalScroller = true
|
|
221
|
+
codeScrollView.borderType = .noBorder
|
|
222
|
+
codeScrollView.autoresizingMask = [.width, .height]
|
|
223
|
+
codeScrollView.wantsLayer = true
|
|
224
|
+
codeScrollView.layer?.cornerRadius = 8
|
|
225
|
+
codeScrollView.layer?.masksToBounds = true
|
|
226
|
+
codeScrollView.backgroundColor = NSColor(calibratedRed: 0.12, green: 0.12, blue: 0.14, alpha: 1.0)
|
|
227
|
+
// 使用现代 overlay 滚动条样式(悬浮自动隐藏)
|
|
228
|
+
codeScrollView.scrollerStyle = .overlay
|
|
229
|
+
codeScrollView.autohidesScrollers = true
|
|
230
|
+
codeScrollView.scrollerKnobStyle = .light
|
|
231
|
+
// 启用平滑滚动
|
|
232
|
+
codeScrollView.usesPredominantAxisScrolling = false
|
|
233
|
+
codeScrollView.horizontalScrollElasticity = .automatic
|
|
234
|
+
codeScrollView.verticalScrollElasticity = .automatic
|
|
235
|
+
// 强制刷新滚动条样式
|
|
236
|
+
codeScrollView.flashScrollers()
|
|
237
|
+
|
|
238
|
+
codeTextView = NSTextView(frame: codeScrollView.bounds)
|
|
239
|
+
codeTextView.isEditable = false
|
|
240
|
+
codeTextView.isSelectable = true
|
|
241
|
+
codeTextView.backgroundColor = NSColor(calibratedRed: 0.12, green: 0.12, blue: 0.14, alpha: 1.0)
|
|
242
|
+
codeTextView.textContainerInset = NSSize(width: 16, height: 16)
|
|
243
|
+
codeTextView.isHorizontallyResizable = true
|
|
244
|
+
codeTextView.isVerticallyResizable = true
|
|
245
|
+
codeTextView.textContainer?.widthTracksTextView = false
|
|
246
|
+
codeTextView.textContainer?.heightTracksTextView = false
|
|
247
|
+
codeTextView.textContainer?.containerSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
|
|
248
|
+
codeTextView.autoresizingMask = []
|
|
249
|
+
codeTextView.textContainer?.lineFragmentPadding = 10
|
|
250
|
+
// 设置默认字体大小
|
|
251
|
+
codeTextView.font = NSFont(name: "SFMono-Regular", size: 14) ?? NSFont.monospacedSystemFont(ofSize: 14, weight: .regular)
|
|
252
|
+
|
|
253
|
+
if let textContainer = codeTextView.textContainer {
|
|
254
|
+
textContainer.size = NSSize(width: codeScrollView.bounds.width - 40, height: CGFloat.greatestFiniteMagnitude)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
codeScrollView.documentView = codeTextView
|
|
258
|
+
contentView.addSubview(codeScrollView)
|
|
259
|
+
|
|
260
|
+
// 底部按钮
|
|
261
|
+
let buttonY: CGFloat = 20
|
|
262
|
+
|
|
263
|
+
let cancelButton = NSButton(title: "取消", target: self, action: #selector(cancelClicked))
|
|
264
|
+
cancelButton.bezelStyle = .rounded
|
|
265
|
+
cancelButton.frame = NSRect(x: panelWidth - 310, y: buttonY, width: 90, height: 32)
|
|
266
|
+
cancelButton.keyEquivalent = "\u{1b}"
|
|
267
|
+
cancelButton.autoresizingMask = [.minXMargin, .maxYMargin]
|
|
268
|
+
cancelButton.wantsLayer = true
|
|
269
|
+
cancelButton.layer?.cornerRadius = 6
|
|
270
|
+
contentView.addSubview(cancelButton)
|
|
271
|
+
|
|
272
|
+
let copyButton = NSButton(title: "复制代码", target: self, action: #selector(copyClicked))
|
|
273
|
+
copyButton.bezelStyle = .rounded
|
|
274
|
+
copyButton.frame = NSRect(x: panelWidth - 210, y: buttonY, width: 90, height: 32)
|
|
275
|
+
copyButton.autoresizingMask = [.minXMargin, .maxYMargin]
|
|
276
|
+
copyButton.wantsLayer = true
|
|
277
|
+
copyButton.layer?.cornerRadius = 6
|
|
278
|
+
contentView.addSubview(copyButton)
|
|
279
|
+
|
|
280
|
+
let okButton = NSButton(title: "立即插入", target: self, action: #selector(okClicked))
|
|
281
|
+
okButton.bezelStyle = .rounded
|
|
282
|
+
okButton.frame = NSRect(x: panelWidth - 110, y: buttonY, width: 90, height: 32)
|
|
283
|
+
okButton.keyEquivalent = "\r"
|
|
284
|
+
okButton.autoresizingMask = [.minXMargin, .maxYMargin]
|
|
285
|
+
okButton.wantsLayer = true
|
|
286
|
+
okButton.layer?.cornerRadius = 6
|
|
287
|
+
contentView.addSubview(okButton)
|
|
288
|
+
|
|
289
|
+
// 初始化显示
|
|
290
|
+
tableView.reloadData()
|
|
291
|
+
if !filteredItems.isEmpty {
|
|
292
|
+
tableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false)
|
|
293
|
+
updatePreview(index: 0)
|
|
294
|
+
} else {
|
|
295
|
+
showEmptyPreview()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 显示窗口
|
|
299
|
+
panel.alphaValue = 0
|
|
300
|
+
NSApp.activate(ignoringOtherApps: true)
|
|
301
|
+
panel.makeKeyAndOrderFront(nil)
|
|
302
|
+
panel.makeFirstResponder(searchField)
|
|
303
|
+
|
|
304
|
+
NSAnimationContext.runAnimationGroup({ context in
|
|
305
|
+
context.duration = 0.2
|
|
306
|
+
panel.animator().alphaValue = 1.0
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
NSApp.runModal(for: panel)
|
|
310
|
+
|
|
311
|
+
// 返回结果
|
|
312
|
+
if confirmed && selectedIndex >= 0 {
|
|
313
|
+
// 找到在原始数组中的索引
|
|
314
|
+
if let selected = filteredItems[safe: selectedIndex] {
|
|
315
|
+
if let originalIndex = allItems.firstIndex(where: { $0.title == selected.title }) {
|
|
316
|
+
return .confirmed(originalIndex)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return .confirmed(selectedIndex)
|
|
320
|
+
}
|
|
321
|
+
return .cancelled
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// MARK: - Table View Data Source
|
|
325
|
+
|
|
326
|
+
func numberOfRows(in tableView: NSTableView) -> Int {
|
|
327
|
+
return filteredItems.count
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
|
331
|
+
let item = filteredItems[row]
|
|
332
|
+
|
|
333
|
+
let cellView = NSTableCellView()
|
|
334
|
+
cellView.wantsLayer = true
|
|
335
|
+
|
|
336
|
+
// 标题 - 支持两行显示
|
|
337
|
+
let textField = NSTextField(labelWithString: item.title)
|
|
338
|
+
textField.font = NSFont.systemFont(ofSize: 14, weight: .medium)
|
|
339
|
+
textField.textColor = .labelColor
|
|
340
|
+
textField.lineBreakMode = .byWordWrapping // 改为自动换行
|
|
341
|
+
textField.maximumNumberOfLines = 2 // 最多显示两行
|
|
342
|
+
textField.frame = NSRect(x: 12, y: 24, width: tableView.bounds.width - 24, height: 36) // 增加高度到36,调整y位置
|
|
343
|
+
cellView.addSubview(textField)
|
|
344
|
+
|
|
345
|
+
// 说明
|
|
346
|
+
var subtitleParts: [String] = []
|
|
347
|
+
if item.groupSize > 1 {
|
|
348
|
+
subtitleParts.append("同类\(item.groupSize)")
|
|
349
|
+
}
|
|
350
|
+
if !item.explanation.isEmpty {
|
|
351
|
+
subtitleParts.append(item.explanation)
|
|
352
|
+
}
|
|
353
|
+
let subtitle = subtitleParts.joined(separator: " · ")
|
|
354
|
+
if !subtitle.isEmpty {
|
|
355
|
+
let subtitleField = NSTextField(labelWithString: subtitle)
|
|
356
|
+
subtitleField.font = NSFont.systemFont(ofSize: 11, weight: .regular)
|
|
357
|
+
subtitleField.textColor = .secondaryLabelColor
|
|
358
|
+
subtitleField.lineBreakMode = .byTruncatingTail
|
|
359
|
+
subtitleField.frame = NSRect(x: 12, y: 8, width: tableView.bounds.width - 24, height: 16) // 调整y位置为8
|
|
360
|
+
cellView.addSubview(subtitleField)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return cellView
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
func tableViewSelectionDidChange(_ notification: Notification) {
|
|
367
|
+
let row = tableView.selectedRow
|
|
368
|
+
if row >= 0 {
|
|
369
|
+
selectedIndex = row
|
|
370
|
+
updatePreview(index: row)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// MARK: - Actions
|
|
375
|
+
|
|
376
|
+
@objc func tableViewClicked() {
|
|
377
|
+
let row = tableView.clickedRow
|
|
378
|
+
if row >= 0 {
|
|
379
|
+
selectedIndex = row
|
|
380
|
+
updatePreview(index: row)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
@objc func tableViewDoubleClicked() {
|
|
385
|
+
let row = tableView.clickedRow
|
|
386
|
+
if row >= 0 {
|
|
387
|
+
selectedIndex = row
|
|
388
|
+
confirmed = true
|
|
389
|
+
panel.close()
|
|
390
|
+
NSApp.stopModal()
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
@objc func searchFieldDidChange() {
|
|
395
|
+
let query = searchField.stringValue.lowercased().trimmingCharacters(in: .whitespaces)
|
|
396
|
+
|
|
397
|
+
if query.isEmpty {
|
|
398
|
+
filteredItems = allItems
|
|
399
|
+
} else {
|
|
400
|
+
filteredItems = allItems.filter { $0.title.lowercased().contains(query) }
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
tableView.reloadData()
|
|
404
|
+
|
|
405
|
+
if !filteredItems.isEmpty {
|
|
406
|
+
tableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false)
|
|
407
|
+
selectedIndex = 0
|
|
408
|
+
updatePreview(index: 0)
|
|
409
|
+
} else {
|
|
410
|
+
showEmptyPreview()
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
@objc func cancelClicked() {
|
|
415
|
+
confirmed = false
|
|
416
|
+
panel.close()
|
|
417
|
+
NSApp.stopModal()
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
@objc func copyClicked() {
|
|
421
|
+
if selectedIndex >= 0, let item = filteredItems[safe: selectedIndex] {
|
|
422
|
+
let pureCode = extractPureCode(item.code)
|
|
423
|
+
let pasteboard = NSPasteboard.general
|
|
424
|
+
pasteboard.clearContents()
|
|
425
|
+
pasteboard.setString(pureCode, forType: .string)
|
|
426
|
+
print("✅ 代码已复制到剪贴板")
|
|
427
|
+
// 复制后也关闭窗口
|
|
428
|
+
confirmed = true
|
|
429
|
+
panel.close()
|
|
430
|
+
NSApp.stopModal()
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
@objc func okClicked() {
|
|
435
|
+
if selectedIndex >= 0 {
|
|
436
|
+
confirmed = true
|
|
437
|
+
// 复制代码到剪贴板
|
|
438
|
+
if let item = filteredItems[safe: selectedIndex] {
|
|
439
|
+
let pureCode = extractPureCode(item.code)
|
|
440
|
+
let pasteboard = NSPasteboard.general
|
|
441
|
+
pasteboard.clearContents()
|
|
442
|
+
pasteboard.setString(pureCode, forType: .string)
|
|
443
|
+
}
|
|
444
|
+
panel.close()
|
|
445
|
+
NSApp.stopModal()
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// MARK: - Helper Methods
|
|
450
|
+
|
|
451
|
+
private func updatePreview(index: Int) {
|
|
452
|
+
guard let item = filteredItems[safe: index] else {
|
|
453
|
+
showEmptyPreview()
|
|
454
|
+
return
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
let processedCode = item.code.replacingOccurrences(of: "\\n", with: "\n")
|
|
458
|
+
let highlightedCode = SyntaxHighlighter.highlight(processedCode)
|
|
459
|
+
codeTextView.textStorage?.setAttributedString(highlightedCode)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private func showEmptyPreview() {
|
|
463
|
+
let emptyText = NSAttributedString(
|
|
464
|
+
string: "无内容",
|
|
465
|
+
attributes: [
|
|
466
|
+
.font: NSFont.systemFont(ofSize: 14),
|
|
467
|
+
.foregroundColor: NSColor.tertiaryLabelColor
|
|
468
|
+
]
|
|
469
|
+
)
|
|
470
|
+
codeTextView.textStorage?.setAttributedString(emptyText)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
private func extractPureCode(_ code: String) -> String {
|
|
474
|
+
var lines = code.split(separator: "\n", omittingEmptySubsequences: false).map(String.init)
|
|
475
|
+
|
|
476
|
+
// 移除首尾的代码块标记(```swift、```等)
|
|
477
|
+
while !lines.isEmpty && (lines.first?.trimmingCharacters(in: .whitespaces).starts(with: "```") ?? false) {
|
|
478
|
+
lines.removeFirst()
|
|
479
|
+
}
|
|
480
|
+
while !lines.isEmpty && (lines.last?.trimmingCharacters(in: .whitespaces).starts(with: "```") ?? false) {
|
|
481
|
+
lines.removeLast()
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return lines.joined(separator: "\n")
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// MARK: - Array Safe Subscript
|
|
489
|
+
|
|
490
|
+
extension Array {
|
|
491
|
+
subscript(safe index: Int) -> Element? {
|
|
492
|
+
return indices.contains(index) ? self[index] : nil
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
## 知识库与结构
|
|
10
10
|
- 知识库根目录:`AutoSnippet/`(用户项目可通过 boxspec `knowledgeBase.dir` 自定义)
|
|
11
11
|
- Recipe:`AutoSnippet/recipes/*.md`(Markdown + Frontmatter + Snippet + Usage Guide)
|
|
12
|
+
- **Project Skills**:`AutoSnippet/skills/<name>/SKILL.md`(项目级 AI 知识增强文档,跟随 Git)
|
|
12
13
|
- constitution.yaml:`AutoSnippet/constitution.yaml`(权限宪法:角色 + 能力 + 治理规则)
|
|
13
14
|
- 运行时 DB:`.autosnippet/autosnippet.db`(SQLite,recipes/candidates/snippets 索引缓存)
|
|
14
15
|
- 向量索引:`.autosnippet/context/`(`asd embed` 生成)
|
|
@@ -51,14 +52,21 @@
|
|
|
51
52
|
- `autosnippet_submit_draft_recipes` — 解析草稿 Markdown 文件
|
|
52
53
|
- `autosnippet_enrich_candidates` — AI 补全缺失语义字段
|
|
53
54
|
|
|
54
|
-
### 项目扫描
|
|
55
|
+
### 项目扫描 & 冷启动
|
|
55
56
|
- `autosnippet_get_targets` / `autosnippet_get_target_files` / `autosnippet_get_target_metadata`
|
|
56
57
|
- `autosnippet_scan_project` — 轻量探查(文件清单 + Guard 审计)
|
|
57
|
-
- `autosnippet_bootstrap_knowledge` — 冷启动知识库初始化(9
|
|
58
|
+
- `autosnippet_bootstrap_knowledge` — 冷启动知识库初始化(9 维度 + 自动生成 Project Skills)
|
|
59
|
+
- `autosnippet_bootstrap_refine` — Bootstrap 候选 AI 润色(summary/insight/relations)
|
|
58
60
|
|
|
59
61
|
### Guard & 治理
|
|
60
62
|
- `autosnippet_guard_check` / `autosnippet_guard_audit_files` / `autosnippet_compliance_report`
|
|
61
63
|
|
|
64
|
+
### Skills
|
|
65
|
+
- `autosnippet_list_skills` — 列出所有可用 Skills(内置 + 项目级)
|
|
66
|
+
- `autosnippet_load_skill` — 加载 Skill 完整文档
|
|
67
|
+
- `autosnippet_create_skill` — 创建项目级 Skill(写入 `AutoSnippet/skills/`)
|
|
68
|
+
- `autosnippet_suggest_skills` — 基于使用模式推荐创建 Skill
|
|
69
|
+
|
|
62
70
|
### 其它
|
|
63
71
|
- `autosnippet_health` / `autosnippet_capabilities` / `autosnippet_confirm_usage`
|
|
64
72
|
|
|
@@ -67,10 +75,19 @@
|
|
|
67
75
|
- Frontmatter 必填字段(7):`title`、`trigger`(@开头)、`category`(8 选 1)、`language`、`summary_cn`、`summary_en`、`headers`。
|
|
68
76
|
- Usage Guide 必须用 `###` 三级标题分段,列表式书写,禁止一行文字墙。
|
|
69
77
|
|
|
78
|
+
## Project Skills
|
|
79
|
+
- **发现**:`autosnippet_list_skills` — 列出所有可用 Skills(内置 + 项目级)
|
|
80
|
+
- **加载**:`autosnippet_load_skill(skillName)` — 获取 Skill 完整操作指南
|
|
81
|
+
- **创建**:`autosnippet_create_skill(name, description, content)` — 创建项目级 Skill
|
|
82
|
+
- **推荐**:`autosnippet_suggest_skills` — 基于使用模式推荐创建 Skill
|
|
83
|
+
- **Bootstrap 自动生成**:冷启动 Phase 5.5 自动生成 4 个 Project Skills(code-standard, architecture, project-profile, agent-guidelines)
|
|
84
|
+
- **优先级**:项目级 Skill 同名覆盖内置;宏观知识查 Skill,微观代码模式查 Recipe
|
|
85
|
+
|
|
70
86
|
## 推荐工作流
|
|
71
87
|
- **查找**:`autosnippet_search`(推荐)或 `autosnippet_context_search`(上下文感知)。
|
|
72
88
|
- **产出候选**:`autosnippet_validate_candidate` 预校验 → `autosnippet_submit_candidate` 提交。
|
|
73
|
-
-
|
|
89
|
+
- **冷启动**:`autosnippet_bootstrap_knowledge` → `autosnippet_enrich_candidates` → `autosnippet_bootstrap_refine` → 逐 Target 深入 → `autosnippet_submit_candidates`。
|
|
90
|
+
- **Skills 创建**:`autosnippet_suggest_skills` 分析 → `autosnippet_create_skill` 固化知识。
|
|
74
91
|
- **采纳反馈**:`autosnippet_confirm_usage`(记录使用量影响排序权重)。
|
|
75
92
|
|
|
76
93
|
## 与 Cursor 规则联动
|
|
@@ -41,9 +41,19 @@ Recipes are classified into three kinds:
|
|
|
41
41
|
- **Retry policy**: If an MCP call fails, do not retry within the same agent turn; fall back to static docs or already-read context.
|
|
42
42
|
- **Adoption tracking**: Tool `autosnippet_confirm_usage` records that a Recipe was adopted or applied (affects usage stats and authority ranking). Show it when the user explicitly adopts a recipe, not immediately after presenting one.
|
|
43
43
|
- **Batch scan**: Ask Cursor in natural language (e.g. "scan all targets in this project", "batch extract candidates") to trigger the batch-scan workflow. The workflow calls `autosnippet_get_targets` → `autosnippet_get_target_files` → extract per file → `autosnippet_submit_candidates` automatically.
|
|
44
|
-
- **Cold start**: Use `autosnippet_bootstrap_knowledge` for full project knowledge initialization covering 9 dimensions
|
|
44
|
+
- **Cold start**: Use `autosnippet_bootstrap_knowledge` for full project knowledge initialization covering 9 dimensions. Phase 5.5 auto-generates 4 Project Skills (code-standard, architecture, project-profile, agent-guidelines) to `AutoSnippet/skills/`.
|
|
45
45
|
|
|
46
|
-
##
|
|
46
|
+
## Project Skills
|
|
47
|
+
|
|
48
|
+
- **Skills 目录**: `AutoSnippet/skills/<name>/SKILL.md` — 跟随项目 Git 管理
|
|
49
|
+
- **发现**: `autosnippet_list_skills` — 列出所有可用 Skills(内置 + 项目级)
|
|
50
|
+
- **加载**: `autosnippet_load_skill(skillName)` — 获取 Skill 完整文档
|
|
51
|
+
- **创建**: `autosnippet_create_skill(name, description, content)` — 创建项目级 Skill
|
|
52
|
+
- **推荐**: `autosnippet_suggest_skills` — 基于使用模式推荐创建 Skill
|
|
53
|
+
- **优先级**: 项目级 Skill 同名覆盖内置 Skill;Skill > Recipe(宏观知识优先查 Skill)
|
|
54
|
+
- **Bootstrap 自动生成**: 冷启动会自动生成 `project-code-standard`、`project-architecture`、`project-profile`、`project-agent-guidelines` 四个 Project Skills
|
|
55
|
+
|
|
56
|
+
## MCP tools (36 total)
|
|
47
57
|
|
|
48
58
|
### Search (prefer search / context_search)
|
|
49
59
|
- `autosnippet_search` — Unified search (auto: BM25 + semantic fusion)
|
|
@@ -66,14 +76,21 @@ Recipes are classified into three kinds:
|
|
|
66
76
|
- `autosnippet_submit_draft_recipes` — Parse draft Markdown files
|
|
67
77
|
- `autosnippet_enrich_candidates` — AI-fill missing semantic fields
|
|
68
78
|
|
|
69
|
-
### Project scanning
|
|
79
|
+
### Project scanning & bootstrap
|
|
70
80
|
- `autosnippet_get_targets` / `autosnippet_get_target_files` / `autosnippet_get_target_metadata`
|
|
71
81
|
- `autosnippet_scan_project` — Lightweight probe (file list + Guard audit)
|
|
72
|
-
- `autosnippet_bootstrap_knowledge` — Cold-start knowledge initialization
|
|
82
|
+
- `autosnippet_bootstrap_knowledge` — Cold-start knowledge initialization (9 dimensions + auto Project Skills)
|
|
83
|
+
- `autosnippet_bootstrap_refine` — AI refinement for bootstrap candidates (summary/insight/relations)
|
|
73
84
|
|
|
74
85
|
### Guard & governance
|
|
75
86
|
- `autosnippet_guard_check` / `autosnippet_guard_audit_files` / `autosnippet_compliance_report`
|
|
76
87
|
|
|
88
|
+
### Skills
|
|
89
|
+
- `autosnippet_list_skills` — List all available Skills (builtin + project)
|
|
90
|
+
- `autosnippet_load_skill` — Load a Skill's full document
|
|
91
|
+
- `autosnippet_create_skill` — Create a project-level Skill in `AutoSnippet/skills/`
|
|
92
|
+
- `autosnippet_suggest_skills` — Recommend Skills based on usage patterns
|
|
93
|
+
|
|
77
94
|
### Other
|
|
78
95
|
- `autosnippet_health` / `autosnippet_capabilities` / `autosnippet_confirm_usage`
|
|
79
96
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: AutoSnippet Project Skills — 项目级知识增强文档索引与使用指引
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AutoSnippet Project Skills
|
|
7
|
+
|
|
8
|
+
Project Skills 是针对当前项目自动生成或手动创建的 AI Agent 知识增强文档。
|
|
9
|
+
它们存放在 `AutoSnippet/skills/` 目录下,跟随项目 Git 管理。
|
|
10
|
+
|
|
11
|
+
## 如何发现与加载
|
|
12
|
+
|
|
13
|
+
1. **列出所有 Skills**:调用 `autosnippet_list_skills` 查看当前可用的 Skills(含内置 + 项目级)
|
|
14
|
+
2. **加载完整内容**:调用 `autosnippet_load_skill(skillName)` 获取具体 Skill 的完整文档
|
|
15
|
+
3. **按需加载**:不确定该用哪个 Skill 时,先加载 `autosnippet-intent` 做意图路由
|
|
16
|
+
4. **创建 Skill**:调用 `autosnippet_create_skill(name, description, content)` 创建项目级 Skill
|
|
17
|
+
5. **Skill 推荐**:调用 `autosnippet_suggest_skills` 获取基于使用模式的 Skill 创建建议
|
|
18
|
+
|
|
19
|
+
## Bootstrap 自动生成的 Project Skills
|
|
20
|
+
|
|
21
|
+
冷启动(`autosnippet_bootstrap_knowledge`)Phase 5.5 会自动为 4 个宏观维度生成 Project Skills:
|
|
22
|
+
|
|
23
|
+
| Skill | 维度 | 说明 |
|
|
24
|
+
|-------|------|------|
|
|
25
|
+
| `project-code-standard` | 代码规范 | 命名约定、文件组织规范 |
|
|
26
|
+
| `project-architecture` | 架构模式 | 分层架构、模块边界、依赖图 |
|
|
27
|
+
| `project-profile` | 项目特征 | 技术栈、模块结构、代码指标 |
|
|
28
|
+
| `project-agent-guidelines` | Agent 约束 | 强制规则、TODO/FIXME、WARNING |
|
|
29
|
+
|
|
30
|
+
生成后的 Skill 文件位于 `AutoSnippet/skills/<name>/SKILL.md`,自动跟随 Git。
|
|
31
|
+
调用 `autosnippet_load_skill` 可加载完整内容。
|
|
32
|
+
|
|
33
|
+
## 推荐工作流
|
|
34
|
+
|
|
35
|
+
冷启动完整流程:
|
|
36
|
+
1. `autosnippet_bootstrap_knowledge` — 9 维度分析 + 自动生成 Project Skills
|
|
37
|
+
2. `autosnippet_enrich_candidates` — 补全缺失语义字段
|
|
38
|
+
3. `autosnippet_bootstrap_refine` — AI 润色 summary/insight/relations
|
|
39
|
+
4. 逐 Target 深入分析 → `autosnippet_submit_candidates`
|
|
40
|
+
|
|
41
|
+
## 使用优先级
|
|
42
|
+
|
|
43
|
+
- **Project Skills > 内置 Skills**:同名时项目级覆盖内置
|
|
44
|
+
- **Skill > Recipe**:宏观架构/规范知识优先查 Skill,微观代码模式查 Recipe
|
|
45
|
+
- **Skills 是只读参考**:不要直接修改 SKILL.md,应通过 `autosnippet_create_skill` 工具创建或更新
|