@moon791017/neo-skills 1.0.33 → 1.0.35
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/GEMINI.md +2 -0
- package/README.md +6 -3
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/skills/neo-rust/SKILL.md +44 -0
- package/skills/neo-rust/reference/anti-patterns.md +354 -0
- package/skills/neo-rust/reference/coding-style.md +467 -0
- package/skills/neo-rust/reference/patterns.md +784 -0
package/GEMINI.md
CHANGED
|
@@ -113,3 +113,5 @@ When interacting with this codebase or using the extension, the agent follows th
|
|
|
113
113
|
* **JavaScript 現代語法專家:** 跨版本 JavaScript 專家 (ES6-ES2025+),專注於現代化語法、模組系統與高效能開發模式。
|
|
114
114
|
|
|
115
115
|
* **Vue 現代開發專家:** 專注於 Vue 3 Composition API (`<script setup>`)、Pinia 狀態管理與 Vue Router 4,嚴格遵循官方 Style Guide 與最佳實踐。
|
|
116
|
+
|
|
117
|
+
* **Rust 專家 (`skills/neo-rust`)**: 用於開發、重構或審查 Rust 應用程式的專家技能,涵蓋 ownership、borrowing、Result/Option 與現代 Rust 設計模式。
|
package/README.md
CHANGED
|
@@ -60,13 +60,16 @@
|
|
|
60
60
|
### 9. Vue 開發專家
|
|
61
61
|
* **Vue 3 現代開發專家 (`skills/neo-vue`)**:專注於 Vue 3 Composition API (`<script setup>`)、Pinia 狀態管理與 Vue Router 4。嚴格遵循官方 Style Guide 與最佳實踐,並提供反模式 (Anti-Patterns) 的避坑指引。
|
|
62
62
|
|
|
63
|
-
### 10.
|
|
63
|
+
### 10. Rust 開發專家
|
|
64
|
+
* **Rust 專家 (`skills/neo-rust`)**:用於開發、重構或審查 Rust 應用程式的專家技能,涵蓋 ownership、borrowing、Result/Option 與現代 Rust 設計模式。
|
|
65
|
+
|
|
66
|
+
### 11. 需求釐清助手
|
|
64
67
|
* **需求釐清**:系統化引導用戶釐清模糊需求,並將其轉化為結構化的規格文件(背景、功能、約束、驗收標準)。
|
|
65
68
|
|
|
66
|
-
###
|
|
69
|
+
### 12. AI 開發流程健檢
|
|
67
70
|
* **AI 助手開發治理 (`skills/neo-agent-harness`)**:檢查專案規則、測試、CI、審查流程與安全防護是否足夠清楚,協助 AI 助手更穩定、更安全地參與開發。
|
|
68
71
|
|
|
69
|
-
###
|
|
72
|
+
### 13. 安全守衛 (Security Guard)
|
|
70
73
|
* **主動防護 (`secret-guard.ts`)**:作為 CLI 的中介軟體 (Hook),自動攔截並掃描所有工具執行的參數。若偵測到敏感資訊(如環境設定檔、私鑰、雲端憑證等)將強制阻擋執行,防止機密外洩。
|
|
71
74
|
|
|
72
75
|
## 📂 系統架構
|
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: neo-rust
|
|
3
|
+
description: 用於開發、重構或審查 Rust 應用程式的專家技能。涵蓋 ownership、borrowing、Result/Option 與現代 Rust 設計模式。當使用者要求撰寫 Rust 程式碼、進行 Code Review 或討論 Rust 架構時使用。
|
|
4
|
+
metadata:
|
|
5
|
+
pattern: tool-wrapper, reviewer
|
|
6
|
+
domain: rust
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Neo Rust Expert
|
|
10
|
+
|
|
11
|
+
You are an expert Rust developer. Your goal is to write safe, maintainable, and idiomatic Rust code by strictly following the official design patterns and avoiding anti-patterns.
|
|
12
|
+
|
|
13
|
+
## Core Directives
|
|
14
|
+
|
|
15
|
+
Before writing or reviewing any Rust code, you MUST follow the "Perceive-Reason-Act" loop:
|
|
16
|
+
|
|
17
|
+
1. **Perceive**: Understand the user's intent. Is it a new feature, a refactor, or a code review?
|
|
18
|
+
2. **Reason**: Load the appropriate reference files based on the task:
|
|
19
|
+
- For writing new code or architecture design, load `reference/patterns.md` and `reference/coding-style.md`.
|
|
20
|
+
- For code review or debugging, load `reference/anti-patterns.md` to spot common mistakes.
|
|
21
|
+
3. **Act**: Execute the task adhering strictly to the loaded guidelines.
|
|
22
|
+
|
|
23
|
+
## When Writing Code
|
|
24
|
+
- Prioritize Borrowing over Ownership where applicable (`&str` instead of `String`).
|
|
25
|
+
- Handle errors gracefully using `Result<T, E>`. Never use `.unwrap()` or `panic!()` in library or production code.
|
|
26
|
+
- Represent "no value" with `Option<T>`, never with magic values like empty strings or `-1`.
|
|
27
|
+
- Use the Type System to prevent invalid states (e.g., Typestate, Newtype, Smart Constructors).
|
|
28
|
+
- Ensure resource cleanup using RAII/Drop.
|
|
29
|
+
- Prefer explicit composition over shared mutable state. Default to single ownership.
|
|
30
|
+
|
|
31
|
+
## When Reviewing Code
|
|
32
|
+
1. Load `reference/anti-patterns.md`.
|
|
33
|
+
2. Check for unnecessary `.clone()`, excessive use of `String`, and improper error handling.
|
|
34
|
+
3. Verify that `Arc<Mutex<T>>` is not overused where message passing or simple ownership would suffice.
|
|
35
|
+
4. Check naming conventions against `reference/coding-style.md`.
|
|
36
|
+
5. Provide actionable feedback with code examples demonstrating the "Good" approach.
|
|
37
|
+
|
|
38
|
+
## Tools & Formatting
|
|
39
|
+
Always recommend standard Rust tooling: `cargo fmt`, `cargo clippy`, and `cargo check`.
|
|
40
|
+
|
|
41
|
+
## Official Resources
|
|
42
|
+
- **Rust Official Website**: https://www.rust-lang.org/
|
|
43
|
+
- **Rust Documentation**: https://doc.rust-lang.org/
|
|
44
|
+
- **The Rust Programming Language (The Book)**: https://doc.rust-lang.org/book/
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# 3. Anti-patterns
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Anti-pattern 1: Using `.clone()` everywhere to avoid the borrow checker
|
|
6
|
+
|
|
7
|
+
### Bad
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
fn process(name: String) {
|
|
11
|
+
println!("{name}");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fn main() {
|
|
15
|
+
let name = String::from("Alice");
|
|
16
|
+
|
|
17
|
+
process(name.clone());
|
|
18
|
+
process(name.clone());
|
|
19
|
+
process(name.clone());
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Good
|
|
24
|
+
|
|
25
|
+
```rust
|
|
26
|
+
fn process(name: &str) {
|
|
27
|
+
println!("{name}");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn main() {
|
|
31
|
+
let name = String::from("Alice");
|
|
32
|
+
|
|
33
|
+
process(&name);
|
|
34
|
+
process(&name);
|
|
35
|
+
process(&name);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Principle
|
|
40
|
+
|
|
41
|
+
`.clone()` is not forbidden, but you should know why you are cloning.
|
|
42
|
+
If you only need to read the data, use a borrow instead.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Anti-pattern 2: Excessive use of `String`
|
|
47
|
+
|
|
48
|
+
### Bad
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
fn is_admin(role: String) -> bool {
|
|
52
|
+
role == "admin"
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Good
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
fn is_admin(role: &str) -> bool {
|
|
60
|
+
role == "admin"
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Principle
|
|
65
|
+
|
|
66
|
+
Use `String` when you need ownership; prefer `&str` for reading text.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Anti-pattern 3: Using `bool` to represent complex states
|
|
71
|
+
|
|
72
|
+
### Bad
|
|
73
|
+
|
|
74
|
+
```rust
|
|
75
|
+
struct User {
|
|
76
|
+
is_active: bool,
|
|
77
|
+
is_deleted: bool,
|
|
78
|
+
is_locked: bool,
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Problem: Contradictory states can occur, such as being both `active` and `deleted`.
|
|
83
|
+
|
|
84
|
+
### Good
|
|
85
|
+
|
|
86
|
+
```rust
|
|
87
|
+
enum UserStatus {
|
|
88
|
+
Active,
|
|
89
|
+
Deleted,
|
|
90
|
+
Locked,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
struct User {
|
|
94
|
+
status: UserStatus,
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Anti-pattern 4: Using `String` for errors
|
|
101
|
+
|
|
102
|
+
### Bad
|
|
103
|
+
|
|
104
|
+
```rust
|
|
105
|
+
fn parse_age(input: &str) -> Result<u8, String> {
|
|
106
|
+
input.parse::<u8>().map_err(|_| "invalid age".to_string())
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Acceptable for short scripts, but not recommended for libraries or large systems.
|
|
111
|
+
|
|
112
|
+
### Good
|
|
113
|
+
|
|
114
|
+
```rust
|
|
115
|
+
#[derive(Debug)]
|
|
116
|
+
enum ParseAgeError {
|
|
117
|
+
InvalidNumber,
|
|
118
|
+
TooLarge,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn parse_age(input: &str) -> Result<u8, ParseAgeError> {
|
|
122
|
+
let age = input.parse::<u8>().map_err(|_| ParseAgeError::InvalidNumber)?;
|
|
123
|
+
|
|
124
|
+
if age > 120 {
|
|
125
|
+
return Err(ParseAgeError::TooLarge);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Ok(age)
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Principle
|
|
133
|
+
|
|
134
|
+
Errors should be categorizable, testable, and handleable.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Anti-pattern 5: Random `panic!` inside a library
|
|
139
|
+
|
|
140
|
+
### Bad
|
|
141
|
+
|
|
142
|
+
```rust
|
|
143
|
+
pub fn divide(a: i32, b: i32) -> i32 {
|
|
144
|
+
if b == 0 {
|
|
145
|
+
panic!("divide by zero");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
a / b
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Good
|
|
153
|
+
|
|
154
|
+
```rust
|
|
155
|
+
#[derive(Debug)]
|
|
156
|
+
pub enum DivideError {
|
|
157
|
+
DivideByZero,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
pub fn divide(a: i32, b: i32) -> Result<i32, DivideError> {
|
|
161
|
+
if b == 0 {
|
|
162
|
+
return Err(DivideError::DivideByZero);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
Ok(a / b)
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Principle
|
|
170
|
+
|
|
171
|
+
Return `Result` unless it is an unrecoverable programming error.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Anti-pattern 6: Over-abstraction with Traits
|
|
176
|
+
|
|
177
|
+
### Bad
|
|
178
|
+
|
|
179
|
+
```rust
|
|
180
|
+
trait UserNameProvider {
|
|
181
|
+
fn user_name(&self) -> &str;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
struct User {
|
|
185
|
+
name: String,
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
impl UserNameProvider for User {
|
|
189
|
+
fn user_name(&self) -> &str {
|
|
190
|
+
&self.name
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Problem: When there's only one struct, one method, and no need for substitution, a trait is just noise.
|
|
196
|
+
|
|
197
|
+
### Good
|
|
198
|
+
|
|
199
|
+
```rust
|
|
200
|
+
struct User {
|
|
201
|
+
name: String,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
impl User {
|
|
205
|
+
fn name(&self) -> &str {
|
|
206
|
+
&self.name
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Principle
|
|
212
|
+
|
|
213
|
+
Use concrete types first. Abstract into traits only when actually needed.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Anti-pattern 7: Public fields breaking invariants
|
|
218
|
+
|
|
219
|
+
### Bad
|
|
220
|
+
|
|
221
|
+
```rust
|
|
222
|
+
pub struct Account {
|
|
223
|
+
pub balance: i64,
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
fn main() {
|
|
227
|
+
let mut account = Account { balance: 100 };
|
|
228
|
+
account.balance = -999;
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Good
|
|
233
|
+
|
|
234
|
+
```rust
|
|
235
|
+
pub struct Account {
|
|
236
|
+
balance: i64,
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
impl Account {
|
|
240
|
+
pub fn new(balance: i64) -> Result<Self, String> {
|
|
241
|
+
if balance < 0 {
|
|
242
|
+
return Err("balance cannot be negative".to_string());
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Ok(Self { balance })
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
pub fn balance(&self) -> i64 {
|
|
249
|
+
self.balance
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
pub fn deposit(&mut self, amount: i64) -> Result<(), String> {
|
|
253
|
+
if amount <= 0 {
|
|
254
|
+
return Err("amount must be positive".to_string());
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
self.balance += amount;
|
|
258
|
+
Ok(())
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Anti-pattern 8: Passing `Arc<Mutex<T>>` everywhere
|
|
266
|
+
|
|
267
|
+
### Bad
|
|
268
|
+
|
|
269
|
+
```rust
|
|
270
|
+
use std::sync::{Arc, Mutex};
|
|
271
|
+
|
|
272
|
+
struct AppState {
|
|
273
|
+
users: Arc<Mutex<Vec<String>>>,
|
|
274
|
+
logs: Arc<Mutex<Vec<String>>>,
|
|
275
|
+
configs: Arc<Mutex<Vec<String>>>,
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Problem: Too many locks, unclear responsibilities, prone to deadlocks, and difficult to test.
|
|
280
|
+
|
|
281
|
+
### Good
|
|
282
|
+
|
|
283
|
+
```rust
|
|
284
|
+
struct UserService {
|
|
285
|
+
users: Vec<String>,
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
impl UserService {
|
|
289
|
+
fn create_user(&mut self, name: String) {
|
|
290
|
+
self.users.push(name);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Principle
|
|
296
|
+
|
|
297
|
+
Prefer single ownership over shared state. Consider `Arc<Mutex<T>>` only when cross-thread access is required.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Anti-pattern 9: Holding a lock across `.await` in async code
|
|
302
|
+
|
|
303
|
+
### Bad
|
|
304
|
+
|
|
305
|
+
```rust
|
|
306
|
+
// Conceptual illustration
|
|
307
|
+
let mut guard = state.lock().await;
|
|
308
|
+
do_async_work().await;
|
|
309
|
+
guard.push(1);
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Problem: Holding a lock for too long can cause performance issues or deadlock-like behavior.
|
|
313
|
+
|
|
314
|
+
### Good
|
|
315
|
+
|
|
316
|
+
```rust
|
|
317
|
+
let data = {
|
|
318
|
+
let mut guard = state.lock().await;
|
|
319
|
+
guard.push(1);
|
|
320
|
+
guard.clone()
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
do_async_work(data).await;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Principle
|
|
327
|
+
|
|
328
|
+
Keep the code inside a lock as short, synchronous, and necessary as possible.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Anti-pattern 10: Abuse of `unsafe`
|
|
333
|
+
|
|
334
|
+
### Bad
|
|
335
|
+
|
|
336
|
+
```rust
|
|
337
|
+
unsafe {
|
|
338
|
+
// Using raw pointers haphazardly to bypass the borrow checker
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Good
|
|
343
|
+
|
|
344
|
+
Ask yourself first:
|
|
345
|
+
|
|
346
|
+
1. Can the design be changed using ownership?
|
|
347
|
+
2. Can `Rc` / `Arc` be used?
|
|
348
|
+
3. Can `RefCell` / `Mutex` be used?
|
|
349
|
+
4. Can the data structure be split?
|
|
350
|
+
5. Do you really need FFI, SIMD, or low-level memory operations?
|
|
351
|
+
|
|
352
|
+
### Principle
|
|
353
|
+
|
|
354
|
+
`unsafe` is not a performance switch; it is taking over the safety responsibilities that the compiler can no longer guarantee.
|