devflow-kit 1.0.0 → 1.2.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.
Files changed (134) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +35 -11
  3. package/dist/cli.js +5 -1
  4. package/dist/commands/ambient.d.ts +18 -0
  5. package/dist/commands/ambient.js +136 -0
  6. package/dist/commands/init.d.ts +2 -0
  7. package/dist/commands/init.js +97 -10
  8. package/dist/commands/memory.d.ts +22 -0
  9. package/dist/commands/memory.js +175 -0
  10. package/dist/commands/uninstall.js +72 -5
  11. package/dist/plugins.js +74 -3
  12. package/dist/utils/post-install.d.ts +12 -0
  13. package/dist/utils/post-install.js +82 -1
  14. package/dist/utils/safe-delete-install.d.ts +7 -0
  15. package/dist/utils/safe-delete-install.js +40 -5
  16. package/package.json +2 -1
  17. package/plugins/devflow-accessibility/.claude-plugin/plugin.json +15 -0
  18. package/plugins/devflow-ambient/.claude-plugin/plugin.json +7 -0
  19. package/plugins/devflow-ambient/README.md +49 -0
  20. package/plugins/devflow-ambient/commands/ambient.md +110 -0
  21. package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +89 -0
  22. package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +68 -0
  23. package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
  24. package/plugins/devflow-code-review/.claude-plugin/plugin.json +1 -4
  25. package/plugins/devflow-code-review/agents/reviewer.md +8 -0
  26. package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
  27. package/plugins/devflow-code-review/commands/code-review.md +12 -2
  28. package/plugins/devflow-core-skills/.claude-plugin/plugin.json +3 -6
  29. package/plugins/devflow-core-skills/skills/docs-framework/SKILL.md +10 -6
  30. package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +139 -0
  31. package/plugins/devflow-core-skills/skills/test-driven-development/references/rationalization-prevention.md +111 -0
  32. package/plugins/devflow-debug/.claude-plugin/plugin.json +1 -1
  33. package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +15 -0
  34. package/plugins/devflow-go/.claude-plugin/plugin.json +15 -0
  35. package/plugins/devflow-go/skills/go/SKILL.md +187 -0
  36. package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
  37. package/plugins/devflow-go/skills/go/references/detection.md +129 -0
  38. package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
  39. package/plugins/devflow-go/skills/go/references/violations.md +205 -0
  40. package/plugins/devflow-implement/.claude-plugin/plugin.json +1 -3
  41. package/plugins/devflow-implement/agents/coder.md +11 -6
  42. package/plugins/devflow-java/.claude-plugin/plugin.json +15 -0
  43. package/plugins/devflow-java/skills/java/SKILL.md +183 -0
  44. package/plugins/devflow-java/skills/java/references/detection.md +120 -0
  45. package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
  46. package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
  47. package/plugins/devflow-java/skills/java/references/violations.md +213 -0
  48. package/plugins/devflow-python/.claude-plugin/plugin.json +15 -0
  49. package/plugins/devflow-python/skills/python/SKILL.md +188 -0
  50. package/plugins/devflow-python/skills/python/references/async.md +220 -0
  51. package/plugins/devflow-python/skills/python/references/detection.md +128 -0
  52. package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
  53. package/plugins/devflow-python/skills/python/references/violations.md +204 -0
  54. package/plugins/devflow-react/.claude-plugin/plugin.json +15 -0
  55. package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
  56. package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
  57. package/plugins/devflow-resolve/.claude-plugin/plugin.json +1 -1
  58. package/plugins/devflow-rust/.claude-plugin/plugin.json +15 -0
  59. package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
  60. package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
  61. package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
  62. package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
  63. package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
  64. package/plugins/devflow-self-review/.claude-plugin/plugin.json +1 -1
  65. package/plugins/devflow-specify/.claude-plugin/plugin.json +1 -1
  66. package/plugins/devflow-typescript/.claude-plugin/plugin.json +15 -0
  67. package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
  68. package/scripts/hooks/ambient-prompt.sh +48 -0
  69. package/scripts/hooks/background-memory-update.sh +49 -8
  70. package/scripts/hooks/ensure-memory-gitignore.sh +17 -0
  71. package/scripts/hooks/pre-compact-memory.sh +12 -6
  72. package/scripts/hooks/session-start-memory.sh +50 -8
  73. package/scripts/hooks/stop-update-memory.sh +10 -6
  74. package/shared/agents/coder.md +11 -6
  75. package/shared/agents/reviewer.md +8 -0
  76. package/shared/skills/ambient-router/SKILL.md +89 -0
  77. package/shared/skills/ambient-router/references/skill-catalog.md +68 -0
  78. package/shared/skills/docs-framework/SKILL.md +10 -6
  79. package/shared/skills/go/SKILL.md +187 -0
  80. package/shared/skills/go/references/concurrency.md +312 -0
  81. package/shared/skills/go/references/detection.md +129 -0
  82. package/shared/skills/go/references/patterns.md +232 -0
  83. package/shared/skills/go/references/violations.md +205 -0
  84. package/shared/skills/java/SKILL.md +183 -0
  85. package/shared/skills/java/references/detection.md +120 -0
  86. package/shared/skills/java/references/modern-java.md +270 -0
  87. package/shared/skills/java/references/patterns.md +235 -0
  88. package/shared/skills/java/references/violations.md +213 -0
  89. package/shared/skills/python/SKILL.md +188 -0
  90. package/shared/skills/python/references/async.md +220 -0
  91. package/shared/skills/python/references/detection.md +128 -0
  92. package/shared/skills/python/references/patterns.md +226 -0
  93. package/shared/skills/python/references/violations.md +204 -0
  94. package/shared/skills/react/SKILL.md +1 -1
  95. package/shared/skills/react/references/patterns.md +3 -3
  96. package/shared/skills/rust/SKILL.md +193 -0
  97. package/shared/skills/rust/references/detection.md +131 -0
  98. package/shared/skills/rust/references/ownership.md +242 -0
  99. package/shared/skills/rust/references/patterns.md +210 -0
  100. package/shared/skills/rust/references/violations.md +191 -0
  101. package/shared/skills/test-driven-development/SKILL.md +139 -0
  102. package/shared/skills/test-driven-development/references/rationalization-prevention.md +111 -0
  103. package/shared/skills/typescript/references/patterns.md +3 -3
  104. package/src/templates/managed-settings.json +14 -0
  105. package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
  106. package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
  107. package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
  108. package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
  109. package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
  110. package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
  111. package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
  112. package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
  113. package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
  114. package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
  115. package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
  116. package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
  117. package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
  118. package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
  119. package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
  120. package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
  121. package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
  122. package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
  123. package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
  124. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
  125. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
  126. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
  127. /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
  128. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
  129. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
  130. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
  131. /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
  132. /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
  133. /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
  134. /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
@@ -0,0 +1,210 @@
1
+ # Rust Extended Patterns
2
+
3
+ Extended correct patterns for Rust. Reference from main SKILL.md.
4
+
5
+ ## Typestate Pattern
6
+
7
+ Encode valid state transitions in the type system so invalid sequences don't compile.
8
+
9
+ ```rust
10
+ // States are zero-sized types — no runtime cost
11
+ struct Draft;
12
+ struct Published;
13
+ struct Archived;
14
+
15
+ struct Article<State> {
16
+ title: String,
17
+ body: String,
18
+ _state: std::marker::PhantomData<State>,
19
+ }
20
+
21
+ impl Article<Draft> {
22
+ pub fn new(title: String, body: String) -> Self {
23
+ Article { title, body, _state: std::marker::PhantomData }
24
+ }
25
+
26
+ pub fn publish(self) -> Article<Published> {
27
+ Article { title: self.title, body: self.body, _state: std::marker::PhantomData }
28
+ }
29
+ }
30
+
31
+ impl Article<Published> {
32
+ pub fn archive(self) -> Article<Archived> {
33
+ Article { title: self.title, body: self.body, _state: std::marker::PhantomData }
34
+ }
35
+ }
36
+
37
+ // article.archive() on Draft won't compile — transition enforced at compile time
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Error Handling Hierarchy
43
+
44
+ Layer errors from specific to general using `thiserror` for libraries and `anyhow` for applications.
45
+
46
+ ```rust
47
+ // Library: precise, typed errors
48
+ #[derive(thiserror::Error, Debug)]
49
+ pub enum RepoError {
50
+ #[error("entity {entity} with id {id} not found")]
51
+ NotFound { entity: &'static str, id: String },
52
+ #[error("duplicate key: {0}")]
53
+ Duplicate(String),
54
+ #[error("connection failed")]
55
+ Connection(#[from] sqlx::Error),
56
+ }
57
+
58
+ // Application: ergonomic error propagation
59
+ use anyhow::{Context, Result};
60
+
61
+ fn run() -> Result<()> {
62
+ let config = load_config()
63
+ .context("failed to load configuration")?;
64
+ let db = connect_db(&config.database_url)
65
+ .context("failed to connect to database")?;
66
+ serve(db, config.port)
67
+ .context("server exited with error")
68
+ }
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Trait Objects vs Generics
74
+
75
+ ### Use Generics for Performance (Monomorphization)
76
+
77
+ ```rust
78
+ fn largest<T: PartialOrd>(list: &[T]) -> Option<&T> {
79
+ list.iter().reduce(|a, b| if a >= b { a } else { b })
80
+ }
81
+ ```
82
+
83
+ ### Use Trait Objects for Heterogeneous Collections
84
+
85
+ ```rust
86
+ trait Handler: Send + Sync {
87
+ fn handle(&self, request: &Request) -> Response;
88
+ }
89
+
90
+ struct Router {
91
+ routes: Vec<Box<dyn Handler>>, // Different concrete types in one Vec
92
+ }
93
+ ```
94
+
95
+ ### Decision Guide
96
+
97
+ | Criteria | Generics | Trait Objects |
98
+ |----------|----------|--------------|
99
+ | Known types at compile time | Yes | No |
100
+ | Heterogeneous collection | No | Yes |
101
+ | Performance-critical | Yes | Acceptable overhead |
102
+ | Binary size concern | Increases | Minimal |
103
+
104
+ ---
105
+
106
+ ## Smart Pointers
107
+
108
+ ### Box — Heap Allocation
109
+
110
+ ```rust
111
+ // Recursive types require indirection
112
+ enum List<T> {
113
+ Cons(T, Box<List<T>>),
114
+ Nil,
115
+ }
116
+ ```
117
+
118
+ ### Rc/Arc — Shared Ownership
119
+
120
+ ```rust
121
+ use std::sync::Arc;
122
+
123
+ // Shared read-only config across threads
124
+ let config = Arc::new(load_config()?);
125
+ let config_clone = Arc::clone(&config);
126
+ tokio::spawn(async move {
127
+ use_config(&config_clone).await;
128
+ });
129
+ ```
130
+
131
+ ### When to Use Each
132
+
133
+ | Pointer | Use Case |
134
+ |---------|----------|
135
+ | `Box<T>` | Single owner, heap allocation, recursive types |
136
+ | `Rc<T>` | Multiple owners, single-threaded |
137
+ | `Arc<T>` | Multiple owners, multi-threaded |
138
+ | `Cow<'a, T>` | Clone-on-write, flexible borrowing |
139
+
140
+ ---
141
+
142
+ ## From/Into Conversions
143
+
144
+ ```rust
145
+ // Implement From for automatic Into
146
+ impl From<CreateUserRequest> for User {
147
+ fn from(req: CreateUserRequest) -> Self {
148
+ User {
149
+ id: Uuid::new_v4(),
150
+ name: req.name,
151
+ email: req.email,
152
+ created_at: Utc::now(),
153
+ }
154
+ }
155
+ }
156
+
157
+ // Callers get Into for free
158
+ fn save_user(user: impl Into<User>) -> Result<(), DbError> {
159
+ let user: User = user.into();
160
+ // ...
161
+ Ok(())
162
+ }
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Derive and Trait Best Practices
168
+
169
+ ```rust
170
+ // Derive the standard set for data types
171
+ #[derive(Debug, Clone, PartialEq, Eq, Hash)]
172
+ pub struct UserId(String);
173
+
174
+ // Derive serde for serialization boundaries
175
+ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
176
+ pub struct ApiResponse<T> {
177
+ pub data: T,
178
+ pub metadata: Metadata,
179
+ }
180
+ ```
181
+
182
+ ### Trait Implementation Order Convention
183
+
184
+ 1. Standard library traits (`Debug`, `Display`, `Clone`, `PartialEq`)
185
+ 2. Conversion traits (`From`, `Into`, `TryFrom`)
186
+ 3. Iterator traits (`Iterator`, `IntoIterator`)
187
+ 4. Serde traits (`Serialize`, `Deserialize`)
188
+ 5. Custom traits (domain-specific)
189
+
190
+ ---
191
+
192
+ ## Module Organization
193
+
194
+ ```
195
+ src/
196
+ ├── lib.rs # Public API re-exports
197
+ ├── error.rs # Crate-level error types
198
+ ├── domain/
199
+ │ ├── mod.rs # Domain re-exports
200
+ │ ├── user.rs # User entity and logic
201
+ │ └── order.rs # Order entity and logic
202
+ ├── repo/
203
+ │ ├── mod.rs # Repository trait definitions
204
+ │ └── postgres.rs # Concrete implementation
205
+ └── api/
206
+ ├── mod.rs # Route registration
207
+ └── handlers.rs # HTTP handlers
208
+ ```
209
+
210
+ Keep `lib.rs` thin — re-export only the public API. Internal modules use `pub(crate)`.
@@ -0,0 +1,191 @@
1
+ # Rust Violation Examples
2
+
3
+ Extended violation patterns for Rust reviews. Reference from main SKILL.md.
4
+
5
+ ## Unwrap Abuse
6
+
7
+ ### Unwrap in Library Code
8
+
9
+ ```rust
10
+ // VIOLATION: Panics on None — caller has no way to handle failure
11
+ pub fn get_username(users: &HashMap<u64, String>, id: u64) -> &str {
12
+ users.get(&id).unwrap() // Panics if id not found
13
+ }
14
+
15
+ // VIOLATION: Unwrap on parse without context
16
+ let port: u16 = std::env::var("PORT").unwrap().parse().unwrap();
17
+ ```
18
+
19
+ ### Expect Without Useful Message
20
+
21
+ ```rust
22
+ // VIOLATION: Message doesn't help diagnose the problem
23
+ let config = load_config().expect("failed");
24
+
25
+ // CORRECT: Actionable message
26
+ let config = load_config().expect("failed to load config from config.toml — does file exist?");
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Unnecessary Cloning
32
+
33
+ ### Clone to Satisfy Borrow Checker
34
+
35
+ ```rust
36
+ // VIOLATION: Cloning to work around borrow issues
37
+ fn process_items(items: &Vec<Item>) {
38
+ let cloned = items.clone(); // Entire Vec cloned
39
+ for item in &cloned {
40
+ println!("{}", item.name);
41
+ }
42
+ }
43
+
44
+ // CORRECT: Just borrow
45
+ fn process_items(items: &[Item]) {
46
+ for item in items {
47
+ println!("{}", item.name);
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### Clone in Hot Loop
53
+
54
+ ```rust
55
+ // VIOLATION: Allocating on every iteration
56
+ for record in &records {
57
+ let key = record.id.clone(); // String allocation per iteration
58
+ map.insert(key, record);
59
+ }
60
+
61
+ // CORRECT: Borrow or use references
62
+ for record in &records {
63
+ map.insert(&record.id, record);
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Stringly-Typed APIs
70
+
71
+ ### String Where Enum Belongs
72
+
73
+ ```rust
74
+ // VIOLATION: Any typo compiles and fails at runtime
75
+ fn set_status(status: &str) {
76
+ match status {
77
+ "active" => { /* ... */ }
78
+ "inactive" => { /* ... */ }
79
+ _ => panic!("unknown status"), // Runtime failure
80
+ }
81
+ }
82
+
83
+ // CORRECT: Compiler enforces valid values
84
+ enum Status { Active, Inactive }
85
+
86
+ fn set_status(status: Status) {
87
+ match status {
88
+ Status::Active => { /* ... */ }
89
+ Status::Inactive => { /* ... */ }
90
+ } // Exhaustive — no default needed
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Unsafe Without Justification
97
+
98
+ ### Bare Unsafe Block
99
+
100
+ ```rust
101
+ // VIOLATION: No safety comment explaining invariants
102
+ unsafe {
103
+ let ptr = data.as_ptr();
104
+ std::ptr::copy_nonoverlapping(ptr, dest, len);
105
+ }
106
+
107
+ // CORRECT: Document why this is safe
108
+ // SAFETY: `data` is guaranteed to be valid for `len` bytes because
109
+ // it was allocated by `Vec::with_capacity(len)` and filled by `read_exact`.
110
+ // `dest` is a valid pointer from `alloc::alloc(layout)` with matching size.
111
+ unsafe {
112
+ std::ptr::copy_nonoverlapping(data.as_ptr(), dest, len);
113
+ }
114
+ ```
115
+
116
+ ### Unnecessary Unsafe
117
+
118
+ ```rust
119
+ // VIOLATION: Using unsafe when safe alternative exists
120
+ unsafe fn get_element(slice: &[u8], index: usize) -> u8 {
121
+ *slice.get_unchecked(index)
122
+ }
123
+
124
+ // CORRECT: Safe indexing with bounds check
125
+ fn get_element(slice: &[u8], index: usize) -> Option<u8> {
126
+ slice.get(index).copied()
127
+ }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Ignoring Results
133
+
134
+ ### Discarding Write Errors
135
+
136
+ ```rust
137
+ // VIOLATION: Write failure silently ignored
138
+ let _ = file.write_all(data);
139
+ let _ = file.flush();
140
+
141
+ // CORRECT: Propagate errors
142
+ file.write_all(data)?;
143
+ file.flush()?;
144
+ ```
145
+
146
+ ### Ignoring Lock Poisoning
147
+
148
+ ```rust
149
+ // VIOLATION: Silently ignoring poisoned mutex
150
+ let guard = mutex.lock().unwrap_or_else(|e| e.into_inner());
151
+
152
+ // CORRECT: Handle or propagate the poison
153
+ let guard = mutex.lock().map_err(|_| AppError::LockPoisoned)?;
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Concurrency Violations
159
+
160
+ ### Shared Mutable State Without Synchronization
161
+
162
+ ```rust
163
+ // VIOLATION: Data race potential — no synchronization
164
+ static mut COUNTER: u64 = 0;
165
+
166
+ fn increment() {
167
+ unsafe { COUNTER += 1; } // Undefined behavior under concurrency
168
+ }
169
+
170
+ // CORRECT: Use atomic or mutex
171
+ use std::sync::atomic::{AtomicU64, Ordering};
172
+ static COUNTER: AtomicU64 = AtomicU64::new(0);
173
+
174
+ fn increment() {
175
+ COUNTER.fetch_add(1, Ordering::Relaxed);
176
+ }
177
+ ```
178
+
179
+ ### Blocking in Async Context
180
+
181
+ ```rust
182
+ // VIOLATION: Blocks the async runtime thread
183
+ async fn read_file(path: &str) -> Result<String, io::Error> {
184
+ std::fs::read_to_string(path) // Blocking call in async fn
185
+ }
186
+
187
+ // CORRECT: Use async file I/O or spawn_blocking
188
+ async fn read_file(path: &str) -> Result<String, io::Error> {
189
+ tokio::fs::read_to_string(path).await
190
+ }
191
+ ```
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: test-driven-development
3
+ description: >-
4
+ Enforce RED-GREEN-REFACTOR cycle during implementation. Write failing tests before
5
+ production code. Distinct from test-patterns (which reviews test quality) — this
6
+ skill enforces the TDD workflow during code generation.
7
+ user-invocable: false
8
+ allowed-tools: Read, Grep, Glob
9
+ activation:
10
+ file-patterns:
11
+ - "**/*.ts"
12
+ - "**/*.tsx"
13
+ - "**/*.js"
14
+ - "**/*.jsx"
15
+ - "**/*.py"
16
+ exclude:
17
+ - "node_modules/**"
18
+ - "dist/**"
19
+ - "**/*.test.*"
20
+ - "**/*.spec.*"
21
+ ---
22
+
23
+ # Test-Driven Development
24
+
25
+ Enforce the RED-GREEN-REFACTOR cycle for all implementation work. Tests define the design. Code satisfies the tests. Refactoring improves the design without changing behavior.
26
+
27
+ ## Iron Law
28
+
29
+ > **TESTS FIRST, ALWAYS**
30
+ >
31
+ > Write the failing test before the production code. No exceptions. If you catch
32
+ > yourself writing production code without a failing test, stop immediately, delete
33
+ > the production code, write the test, watch it fail, then write the minimum code
34
+ > to make it pass. The test IS the specification.
35
+
36
+ ---
37
+
38
+ ## The Cycle
39
+
40
+ ### Step 1: RED — Write a Failing Test
41
+
42
+ Write a test that describes the behavior you want. Run it. Watch it fail. The failure message IS your specification.
43
+
44
+ ```
45
+ Describe what the code SHOULD do, not how it does it.
46
+ One behavior per test. One assertion per test (ideally).
47
+ Name tests as sentences: "returns error when email is invalid"
48
+ ```
49
+
50
+ **Checkpoint:** The test MUST fail before proceeding. A test that passes immediately proves nothing.
51
+
52
+ ### Step 2: GREEN — Write Minimum Code to Pass
53
+
54
+ Write the simplest production code that makes the failing test pass. No more, no less.
55
+
56
+ ```
57
+ Hardcode first if that's simplest. Generalize when the next test forces it.
58
+ Don't write code "you'll need later." Write code the test demands NOW.
59
+ Don't optimize. Don't refactor. Don't clean up. Just pass the test.
60
+ ```
61
+
62
+ **Checkpoint:** All tests pass. If any test fails, fix it before moving on.
63
+
64
+ ### Step 3: REFACTOR — Improve Without Changing Behavior
65
+
66
+ Now clean up. Extract helpers, rename variables, simplify logic. Tests stay green throughout.
67
+
68
+ ```
69
+ Run tests after every refactoring step.
70
+ If a test breaks during refactor, undo immediately — you changed behavior.
71
+ Apply DRY, extract patterns, improve readability.
72
+ ```
73
+
74
+ **Checkpoint:** All tests still pass. Code is clean. Repeat from Step 1 for next behavior.
75
+
76
+ ---
77
+
78
+ ## Rationalization Prevention
79
+
80
+ These are the excuses developers use to skip TDD. Recognize and reject them.
81
+
82
+ | Excuse | Why It Feels Right | Why It's Wrong | Correct Action |
83
+ |--------|-------------------|---------------|----------------|
84
+ | "I'll write tests after" | Need to see the shape first | Tests ARE the shape — they define the interface before implementation exists | Write the test first |
85
+ | "Too simple to test" | It's just a getter/setter | Getters break, defaults change, edge cases hide in "simple" code | Write it — takes 30 seconds |
86
+ | "I'll refactor later" | Just get it working now | "Later" never comes; technical debt compounds silently | Refactor now in Step 3 |
87
+ | "Test is too hard to write" | Setup is complex, mocking is painful | Hard-to-test code = bad design; the test is telling you the interface is wrong | Simplify the interface first |
88
+ | "Need to see the whole picture" | Can't test what I haven't designed yet | TDD IS design; each test reveals the next piece of the interface | Let the test guide the design |
89
+ | "Tests slow me down" | Faster to just write the code | Faster until the first regression; TDD is faster for anything > 50 lines | Trust the cycle |
90
+
91
+ See `references/rationalization-prevention.md` for extended examples with code.
92
+
93
+ ---
94
+
95
+ ## Process Enforcement
96
+
97
+ When implementing any feature under ambient BUILD/STANDARD:
98
+
99
+ 1. **Identify the first behavior** — What is the simplest thing this feature must do?
100
+ 2. **Write the test** — Describe that behavior as a failing test
101
+ 3. **Run the test** — Confirm it fails (RED)
102
+ 4. **Write minimum code** — Just enough to pass (GREEN)
103
+ 5. **Refactor** — Clean up while tests stay green (REFACTOR)
104
+ 6. **Repeat** — Next behavior, next test, next cycle
105
+
106
+ ### File Organization
107
+
108
+ - Test file lives next to production file: `user.ts` → `user.test.ts`
109
+ - Follow project's existing test conventions (Jest, Vitest, pytest, etc.)
110
+ - Import the module under test, not internal helpers
111
+
112
+ ### What to Test
113
+
114
+ | Test | Don't Test |
115
+ |------|-----------|
116
+ | Public API behavior | Private implementation details |
117
+ | Error conditions and edge cases | Framework internals |
118
+ | Integration points (boundaries) | Third-party library correctness |
119
+ | State transitions | Getter/setter plumbing (unless non-trivial) |
120
+
121
+ ---
122
+
123
+ ## When TDD Does Not Apply
124
+
125
+ - **QUICK depth** — Ambient classified as QUICK (chat, exploration, trivial edits)
126
+ - **Non-code tasks** — Documentation, configuration, CI changes
127
+ - **Exploratory prototyping** — User explicitly says "just spike this" or "prototype"
128
+ - **Existing test suite changes** — Modifying tests themselves (test-patterns skill applies instead)
129
+
130
+ When skipping TDD, never rationalize. State clearly: "Skipping TDD because: [specific reason from list above]."
131
+
132
+ ---
133
+
134
+ ## Integration with Ambient Mode
135
+
136
+ - **BUILD/STANDARD** → TDD enforced. Every new function/method gets test-first treatment.
137
+ - **BUILD/QUICK** → TDD skipped (trivial single-file edit).
138
+ - **BUILD/ESCALATE** → TDD mentioned in nudge toward `/implement`.
139
+ - **DEBUG/STANDARD** → TDD applies to the fix: write a test that reproduces the bug first, then fix.
@@ -0,0 +1,111 @@
1
+ # TDD Rationalization Prevention — Extended Examples
2
+
3
+ Detailed code examples showing how each rationalization leads to worse outcomes.
4
+
5
+ ## "I'll write tests after"
6
+
7
+ ### What happens:
8
+
9
+ ```typescript
10
+ // Developer writes production code first
11
+ function calculateDiscount(price: number, tier: string): number {
12
+ if (tier === 'gold') return price * 0.8;
13
+ if (tier === 'silver') return price * 0.9;
14
+ return price;
15
+ }
16
+
17
+ // Then "writes tests after" — but only for the happy path they remember
18
+ test('gold tier gets 20% off', () => {
19
+ expect(calculateDiscount(100, 'gold')).toBe(80);
20
+ });
21
+ // Missing: negative prices, unknown tiers, zero prices, NaN handling
22
+ ```
23
+
24
+ ### What TDD would have caught:
25
+
26
+ ```typescript
27
+ // Test first — forces you to think about the contract
28
+ test('returns error for negative price', () => {
29
+ expect(calculateDiscount(-100, 'gold')).toEqual({ ok: false, error: 'NEGATIVE_PRICE' });
30
+ });
31
+ // Now the interface includes error handling from the start
32
+ ```
33
+
34
+ ## "Too simple to test"
35
+
36
+ ### What happens:
37
+
38
+ ```typescript
39
+ // "It's just a config getter, no test needed"
40
+ function getMaxRetries(): number {
41
+ return parseInt(process.env.MAX_RETRIES || '3');
42
+ }
43
+ // 6 months later: someone sets MAX_RETRIES="three" and prod crashes with NaN retries
44
+ ```
45
+
46
+ ### What TDD would have caught:
47
+
48
+ ```typescript
49
+ test('returns default when env var is not a number', () => {
50
+ process.env.MAX_RETRIES = 'three';
51
+ expect(getMaxRetries()).toBe(3); // Forces validation logic
52
+ });
53
+ ```
54
+
55
+ ## "Test is too hard to write"
56
+
57
+ ### What happens:
58
+
59
+ ```typescript
60
+ // "I can't test this easily because it needs database + email + filesystem"
61
+ async function processOrder(orderId: string) {
62
+ const db = new Database();
63
+ const order = await db.find(orderId);
64
+ await sendEmail(order.customerEmail, 'Your order is processing');
65
+ await fs.writeFile(`/invoices/${orderId}.pdf`, generateInvoice(order));
66
+ await db.update(orderId, { status: 'processing' });
67
+ }
68
+ // Result: untestable monolith, test would need real DB + email + filesystem
69
+ ```
70
+
71
+ ### What TDD forces:
72
+
73
+ ```typescript
74
+ // Hard-to-test = bad design. TDD forces dependency injection:
75
+ async function processOrder(
76
+ orderId: string,
77
+ deps: { db: OrderRepository; emailer: Emailer; invoices: InvoiceStore }
78
+ ): Promise<Result<void, OrderError>> {
79
+ // Now trivially testable with mocks
80
+ }
81
+ ```
82
+
83
+ ## "I'll refactor later"
84
+
85
+ ### What happens:
86
+
87
+ ```typescript
88
+ // Sprint 1: "just get it working"
89
+ function handleRequest(req: any) {
90
+ if (req.type === 'create') { /* 50 lines */ }
91
+ else if (req.type === 'update') { /* 50 lines */ }
92
+ else if (req.type === 'delete') { /* 30 lines */ }
93
+ // Sprint 2-10: more conditions added, function grows to 500 lines
94
+ // "Refactor later" never comes because nobody wants to touch it
95
+ }
96
+ ```
97
+
98
+ ### What TDD enforces:
99
+
100
+ Step 3 (REFACTOR) happens every cycle. The function never grows beyond what's clean because you clean it every 5-10 minutes.
101
+
102
+ ## "Tests slow me down"
103
+
104
+ ### The math:
105
+
106
+ | Approach | Time to write | Time to first bug | Time to fix bug | Total (1 month) |
107
+ |----------|:---:|:---:|:---:|:---:|
108
+ | No TDD | 2h | 4h | 3h (no repro test) | 9h+ |
109
+ | TDD | 3h | Caught in test | 15min (test pinpoints) | 3h 15min |
110
+
111
+ TDD is slower for the first 30 minutes. It's faster for everything after that.
@@ -137,7 +137,7 @@ const isUndefined = (value: unknown): value is undefined =>
137
137
  const isNullish = (value: unknown): value is null | undefined =>
138
138
  value === null || value === undefined;
139
139
 
140
- const isFunction = (value: unknown): value is Function =>
140
+ const isFunction = (value: unknown): value is (...args: unknown[]) => unknown =>
141
141
  typeof value === 'function';
142
142
 
143
143
  const isObject = (value: unknown): value is object =>
@@ -760,7 +760,7 @@ function debounce<T extends (...args: any[]) => any>(
760
760
  fn: T,
761
761
  delayMs: number
762
762
  ): (...args: Parameters<T>) => void {
763
- let timeoutId: NodeJS.Timeout | null = null;
763
+ let timeoutId: ReturnType<typeof setTimeout> | null = null;
764
764
 
765
765
  return (...args: Parameters<T>) => {
766
766
  if (timeoutId) clearTimeout(timeoutId);
@@ -774,7 +774,7 @@ function throttle<T extends (...args: any[]) => any>(
774
774
  limitMs: number
775
775
  ): (...args: Parameters<T>) => void {
776
776
  let lastRun = 0;
777
- let timeoutId: NodeJS.Timeout | null = null;
777
+ let timeoutId: ReturnType<typeof setTimeout> | null = null;
778
778
 
779
779
  return (...args: Parameters<T>) => {
780
780
  const now = Date.now();
@@ -5,6 +5,15 @@
5
5
  "Bash(rm -rf ~*)",
6
6
  "Bash(rm -rf .*)",
7
7
  "Bash(* rm -rf /*)",
8
+ "Bash(rm -r /*)",
9
+ "Bash(rm -r ~*)",
10
+ "Bash(rm -r .*)",
11
+ "Bash(rm -fr /*)",
12
+ "Bash(rm -fr ~*)",
13
+ "Bash(rm -fr .*)",
14
+ "Bash(rm -f /*)",
15
+ "Bash(rm -f ~*)",
16
+ "Bash(rm -f .*)",
8
17
  "Bash(dd if=*)",
9
18
  "Bash(dd*of=/dev/*)",
10
19
  "Bash(mkfs*)",
@@ -85,12 +94,17 @@
85
94
  "Bash(crontab*)",
86
95
  "Bash(rm /var/log*)",
87
96
  "Bash(rm -rf /var/log*)",
97
+ "Bash(rm -r /var/log*)",
98
+ "Bash(rm -f /var/log*)",
99
+ "Bash(rm -fr /var/log*)",
88
100
  "Bash(> /var/log*)",
89
101
  "Bash(truncate /var/log*)",
90
102
  "Bash(history -c*)",
91
103
  "Bash(history -w*)",
92
104
  "Bash(rm ~/.bash_history*)",
105
+ "Bash(rm -f ~/.bash_history*)",
93
106
  "Bash(rm ~/.zsh_history*)",
107
+ "Bash(rm -f ~/.zsh_history*)",
94
108
  "Bash(unset HISTFILE*)",
95
109
  "Bash(curl 169.254.169.254*)",
96
110
  "Bash(wget 169.254.169.254*)",