safeword 0.6.3 → 0.6.5

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 (55) hide show
  1. package/dist/{check-PECCGHEA.js → check-OYYSYHFP.js} +41 -23
  2. package/dist/check-OYYSYHFP.js.map +1 -0
  3. package/dist/chunk-LNSEDZIW.js +454 -0
  4. package/dist/chunk-LNSEDZIW.js.map +1 -0
  5. package/dist/chunk-ZS3Z3Q37.js +729 -0
  6. package/dist/chunk-ZS3Z3Q37.js.map +1 -0
  7. package/dist/cli.js +7 -7
  8. package/dist/cli.js.map +1 -1
  9. package/dist/diff-325TIZ63.js +168 -0
  10. package/dist/diff-325TIZ63.js.map +1 -0
  11. package/dist/reset-ZGJIKMUW.js +74 -0
  12. package/dist/reset-ZGJIKMUW.js.map +1 -0
  13. package/dist/setup-GAMXTFM2.js +103 -0
  14. package/dist/setup-GAMXTFM2.js.map +1 -0
  15. package/dist/{sync-4XBMKLXS.js → sync-BFMXZEHM.js} +33 -32
  16. package/dist/sync-BFMXZEHM.js.map +1 -0
  17. package/dist/upgrade-X4GREJXN.js +73 -0
  18. package/dist/upgrade-X4GREJXN.js.map +1 -0
  19. package/package.json +15 -14
  20. package/templates/SAFEWORD.md +101 -689
  21. package/templates/guides/architecture-guide.md +1 -1
  22. package/templates/guides/cli-reference.md +35 -0
  23. package/templates/guides/code-philosophy.md +22 -19
  24. package/templates/guides/context-files-guide.md +2 -2
  25. package/templates/guides/data-architecture-guide.md +1 -1
  26. package/templates/guides/design-doc-guide.md +1 -1
  27. package/templates/guides/{testing-methodology.md → development-workflow.md} +1 -1
  28. package/templates/guides/learning-extraction.md +1 -1
  29. package/templates/guides/{llm-instruction-design.md → llm-guide.md} +93 -29
  30. package/templates/guides/tdd-best-practices.md +2 -2
  31. package/templates/guides/test-definitions-guide.md +1 -1
  32. package/templates/guides/user-story-guide.md +1 -1
  33. package/templates/guides/zombie-process-cleanup.md +1 -1
  34. package/dist/check-PECCGHEA.js.map +0 -1
  35. package/dist/chunk-6CVTH67L.js +0 -43
  36. package/dist/chunk-6CVTH67L.js.map +0 -1
  37. package/dist/chunk-75FKNZUM.js +0 -15
  38. package/dist/chunk-75FKNZUM.js.map +0 -1
  39. package/dist/chunk-ARIAOK2F.js +0 -110
  40. package/dist/chunk-ARIAOK2F.js.map +0 -1
  41. package/dist/chunk-FRPJITGG.js +0 -35
  42. package/dist/chunk-FRPJITGG.js.map +0 -1
  43. package/dist/chunk-IWWBZVHT.js +0 -274
  44. package/dist/chunk-IWWBZVHT.js.map +0 -1
  45. package/dist/diff-ZACVJKOU.js +0 -171
  46. package/dist/diff-ZACVJKOU.js.map +0 -1
  47. package/dist/reset-5SRM3P6J.js +0 -145
  48. package/dist/reset-5SRM3P6J.js.map +0 -1
  49. package/dist/setup-65EVU5OT.js +0 -437
  50. package/dist/setup-65EVU5OT.js.map +0 -1
  51. package/dist/sync-4XBMKLXS.js.map +0 -1
  52. package/dist/upgrade-P3WX3ODU.js +0 -153
  53. package/dist/upgrade-P3WX3ODU.js.map +0 -1
  54. package/templates/guides/llm-prompting.md +0 -102
  55. /package/templates/prompts/{review.md → quality-review.md} +0 -0
@@ -1,6 +1,6 @@
1
1
  # Architecture & Design Documentation Guide
2
2
 
3
- **See:** `@.safeword/guides/llm-instruction-design.md` for LLM-consumable documentation principles.
3
+ **See:** `@.safeword/guides/llm-guide.md` for LLM-consumable documentation principles.
4
4
 
5
5
  ---
6
6
 
@@ -0,0 +1,35 @@
1
+ # Safeword CLI Reference
2
+
3
+ Commands for managing safeword in projects.
4
+
5
+ ## Commands
6
+
7
+ | Command | Purpose |
8
+ | ----------------------------- | ---------------------------------------------- |
9
+ | `npx safeword@latest setup` | Install safeword in current project |
10
+ | `npx safeword@latest check` | Check project health and versions |
11
+ | `npx safeword@latest upgrade` | Upgrade to latest version |
12
+ | `npx safeword@latest diff` | Preview changes before upgrading |
13
+ | `npx safeword sync` | Sync linting plugins with project dependencies |
14
+ | `npx safeword reset` | Remove safeword from project |
15
+
16
+ ## When to Use
17
+
18
+ | Situation | Command |
19
+ | -------------------------- | ----------------------------- |
20
+ | New project setup | `npx safeword@latest setup` |
21
+ | Check if update available | `npx safeword@latest check` |
22
+ | Update after CLI release | `npx safeword@latest upgrade` |
23
+ | See what upgrade changes | `npx safeword@latest diff` |
24
+ | Added/removed framework | `npx safeword sync` |
25
+ | Remove safeword completely | `npx safeword reset --full` |
26
+
27
+ ## Options
28
+
29
+ Run `npx safeword <command> --help` for command-specific options.
30
+
31
+ Common flags:
32
+
33
+ - `-y, --yes` - Skip confirmations (setup, reset)
34
+ - `-v, --verbose` - Show detailed output (diff)
35
+ - `-q, --quiet` - Suppress output (sync)
@@ -1,6 +1,6 @@
1
1
  # Code Philosophy & Practices
2
2
 
3
- **Note:** This file provides instructions for LLM-based coding agents. For comprehensive framework on writing clear, actionable LLM-consumable instructions, see `@.safeword/guides/llm-instruction-design.md`.
3
+ **Note:** This file provides instructions for LLM-based coding agents. For comprehensive framework on writing clear, actionable LLM-consumable instructions, see `@.safeword/guides/llm-guide.md`.
4
4
 
5
5
  ---
6
6
 
@@ -41,19 +41,21 @@ Examples:
41
41
  - **Explicit error handling** - NEVER suppress or swallow errors silently
42
42
 
43
43
  **Error handling examples:**
44
- | ❌ Bad | ✅ Good |
45
- |--------|---------|
46
- | `catch (e) {}` (swallowed) | `catch (e) { throw new Error(\`Failed to read ${filePath}: ${e.message}\`) }`|
47
- |`catch (e) { console.log(e) }`|`catch (e) { logger.error('Payment failed', { userId, amount, error: e }) }` |
48
- | Generic "Something went wrong" | "Failed to save user profile: database connection timeout" |
44
+
45
+ | ❌ Bad | ✅ Good |
46
+ | ------------------------------ | ----------------------------------------------------------------------------- |
47
+ | `catch (e) {}` (swallowed) | `catch (e) { throw new Error(\`Failed to read ${filePath}: ${e.message}\`) }` |
48
+ | `catch (e) { console.log(e) }` | `catch (e) { logger.error('Payment failed', { userId, amount, error: e }) }` |
49
+ | Generic "Something went wrong" | "Failed to save user profile: database connection timeout" |
49
50
 
50
51
  **Naming examples:**
51
- | ❌ Bad | ✅ Good |
52
- |--------|---------|
53
- | `calcTot` | `calculateTotalWithTax` |
52
+
53
+ | ❌ Bad | ✅ Good |
54
+ | ------------------ | ------------------------------ |
55
+ | `calcTot` | `calculateTotalWithTax` |
54
56
  | `d`, `tmp`, `data` | `userProfile`, `pendingOrders` |
55
- | `handleClick` | `submitPaymentForm` |
56
- | `process()` | `validateAndSaveUser()` |
57
+ | `handleClick` | `submitPaymentForm` |
58
+ | `process()` | `validateAndSaveUser()` |
57
59
 
58
60
  **When to comment:**
59
61
 
@@ -63,13 +65,14 @@ Examples:
63
65
  - ❌ Restating the function name
64
66
 
65
67
  **Bloat examples (avoid these):**
66
- | ❌ Bloat | ✅ Instead |
67
- |----------|-----------|
68
- | Utility class for one function | Single function |
69
- | Factory pattern for simple object | Direct construction |
70
- | Abstract base class with one implementation | Concrete class |
71
- | Config file for 2 options | Hardcode or simple params |
72
- | "Future-proofing" unused code paths | Delete, add when needed |
68
+
69
+ | ❌ Bloat | ✅ Instead |
70
+ | ------------------------------------------- | ------------------------- |
71
+ | Utility class for one function | Single function |
72
+ | Factory pattern for simple object | Direct construction |
73
+ | Abstract base class with one implementation | Concrete class |
74
+ | Config file for 2 options | Hardcode or simple params |
75
+ | "Future-proofing" unused code paths | Delete, add when needed |
73
76
 
74
77
  **When to push back:** If a feature request would add >50 lines for a "nice to have", ask: "Is this essential now, or can we add it later?"
75
78
 
@@ -106,7 +109,7 @@ Examples:
106
109
  - ✅ If a test fails, fix the implementation—not the test
107
110
  - ✅ If a test seems wrong or requirements changed, explain why and ask before changing it
108
111
 
109
- **Workflow:** See `@.safeword/guides/testing-methodology.md` for comprehensive TDD workflow (RED → GREEN → REFACTOR phases)
112
+ **Workflow:** See `@.safeword/guides/development-workflow.md` for comprehensive TDD workflow (RED → GREEN → REFACTOR phases)
110
113
 
111
114
  ## Debugging & Troubleshooting
112
115
 
@@ -224,7 +224,7 @@ See @README for project overview and @package.json for available npm commands.
224
224
 
225
225
  ## Coding Standards
226
226
 
227
- @.safeword/guides/llm-prompting.md
227
+ @.safeword/guides/llm-guide.md
228
228
 
229
229
  ## Git Workflow
230
230
 
@@ -415,7 +415,7 @@ Brief description. Current status.
415
415
 
416
416
  **Critical:** These files are instructions consumed by LLMs.
417
417
 
418
- **See:** `@.safeword/guides/llm-instruction-design.md` for 13 core principles for writing LLM-consumable documentation.
418
+ **See:** `@.safeword/guides/llm-guide.md` for 13 core principles for writing LLM-consumable documentation.
419
419
 
420
420
  ### Quality Checklist
421
421
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Context:** How to document data architecture decisions, models, and flows for software projects. Applies LLM instruction design principles for clarity and reliability.
4
4
 
5
- **See:** `@.safeword/guides/llm-instruction-design.md` for comprehensive framework on writing LLM-consumable documentation.
5
+ **See:** `@.safeword/guides/llm-guide.md` for comprehensive framework on writing LLM-consumable documentation.
6
6
 
7
7
  ---
8
8
 
@@ -168,4 +168,4 @@ Before saving, verify:
168
168
 
169
169
  **Important:** Design docs are instructions that LLMs read and follow.
170
170
 
171
- **See:** `@.safeword/guides/llm-instruction-design.md` for comprehensive framework on writing clear, actionable documentation that LLMs can reliably follow.
171
+ **See:** `@.safeword/guides/llm-guide.md` for comprehensive framework on writing clear, actionable documentation that LLMs can reliably follow.
@@ -614,5 +614,5 @@ npm run test:e2e # E2E tests only
614
614
 
615
615
  **Cascading precedence:**
616
616
 
617
- 1. **Global** (`~/.claude/testing-methodology.md`) - Universal methodology (test type selection, TDD workflow)
617
+ 1. **Global** (`~/.claude/development-workflow.md`) - Universal methodology (test type selection, TDD workflow)
618
618
  2. **Project** (`tests/SAFEWORD.md`) - Specific stack, commands, patterns
@@ -2,7 +2,7 @@
2
2
 
3
3
  Extract reusable knowledge from debugging sessions and implementation discoveries. Ensures insights compound across sessions.
4
4
 
5
- **LLM Instruction Design:** Learnings are documentation that LLMs read and follow. Apply best practices from `@.safeword/guides/llm-instruction-design.md` when writing learning files (concrete examples, explicit definitions, MECE principles).
5
+ **LLM Instruction Design:** Learnings are documentation that LLMs read and follow. Apply best practices from `@.safeword/guides/llm-guide.md` when writing learning files (concrete examples, explicit definitions, MECE principles).
6
6
 
7
7
  ---
8
8
 
@@ -1,12 +1,76 @@
1
- # Writing Instructions for LLMs
1
+ # LLM Guide
2
2
 
3
- **Context:** When creating documentation that LLMs will read and follow (like AGENTS.md, CLAUDE.md, testing guides, coding standards), different best practices apply than when prompting an LLM directly.
3
+ This guide covers two related topics:
4
4
 
5
- ## Core Principles
5
+ **Part 1: Integration** - How to call LLMs effectively (API calls, structured outputs, caching, testing)
6
6
 
7
- **1. MECE Principle (Mutually Exclusive, Collectively Exhaustive)**
7
+ **Part 2: Writing for LLMs** - How to write documentation that LLMs will read and follow (SAFEWORD.md, CLAUDE.md, guides)
8
8
 
9
- Decision trees and categorization must have no overlap and cover all cases. Research shows LLMs struggle with overlapping categories—McKinsey/BCG MECE framework ensures clear decision paths.
9
+ ---
10
+
11
+ ## Part 1: Integration
12
+
13
+ ### Structured Outputs
14
+
15
+ Use JSON mode for predictable LLM responses. Define explicit schemas with validation. Return structured data, not prose.
16
+
17
+ ```typescript
18
+ // ❌ BAD - Prose output
19
+ "The user wants to create a campaign named 'Shadows' with 4 players"
20
+
21
+ // ✅ GOOD - Structured JSON
22
+ { "intent": "create_campaign", "name": "Shadows", "playerCount": 4 }
23
+ ```
24
+
25
+ ### Cost Optimization
26
+
27
+ **Prompt Caching (Critical for AI Agents):**
28
+
29
+ - Static rules → System prompt with cache_control: ephemeral (caches for ~5 min, auto-expires)
30
+ - Dynamic data (character state, user input) → User message (no caching)
31
+ - Example: 468-line prompt costs $0.10 without caching, $0.01 with (90% reduction)
32
+ - Cache invalidation: ANY change to cached blocks breaks ALL caches
33
+ - Rule: Change system prompts sparingly; accept one-time cache rebuild cost
34
+
35
+ **Message Architecture:**
36
+
37
+ ```typescript
38
+ // ✅ GOOD - Cacheable system prompt
39
+ systemPrompt: [
40
+ { text: STATIC_RULES, cache_control: { type: 'ephemeral' } },
41
+ { text: STATIC_EXAMPLES, cache_control: { type: 'ephemeral' } },
42
+ ];
43
+ userMessage: `Character: ${dynamicState}\nAction: ${userInput}`;
44
+
45
+ // ❌ BAD - Uncacheable (character state in system prompt)
46
+ systemPrompt: `Rules + Character: ${dynamicState}`;
47
+ ```
48
+
49
+ ### Testing AI Outputs
50
+
51
+ **LLM-as-Judge Pattern:**
52
+
53
+ - Use LLM to evaluate nuanced qualities (narrative tone, reasoning quality)
54
+ - Avoid brittle keyword matching for creative outputs
55
+ - Define rubrics: EXCELLENT / ACCEPTABLE / POOR with criteria
56
+ - Example: "Does the GM's response show collaborative tone?" vs checking for specific words
57
+
58
+ **Evaluation Framework:**
59
+
60
+ - Unit tests: Pure functions (parsing, validation)
61
+ - Integration tests: Agent + real LLM calls (schema compliance)
62
+ - LLM Evals: Judgment quality (position/effect reasoning, atmosphere)
63
+ - Cost awareness: 30 scenarios ≈ $0.15-0.30 per run with caching
64
+
65
+ ---
66
+
67
+ ## Part 2: Writing for LLMs
68
+
69
+ When creating documentation that LLMs will read and follow (AGENTS.md, CLAUDE.md, testing guides, coding standards), apply these principles:
70
+
71
+ ### 1. MECE Principle (Mutually Exclusive, Collectively Exhaustive)
72
+
73
+ Decision trees must have no overlap and cover all cases. LLMs struggle with overlapping categories.
10
74
 
11
75
  ```markdown
12
76
  ❌ BAD - Not mutually exclusive:
@@ -26,7 +90,7 @@ Problem: A function with database calls could match both
26
90
  Stops at first match, no ambiguity.
27
91
  ```
28
92
 
29
- **2. Explicit Over Implicit**
93
+ ### 2. Explicit Over Implicit
30
94
 
31
95
  Never assume LLMs know what you mean. Define all terms, even "obvious" ones.
32
96
 
@@ -41,7 +105,7 @@ Examples needing definition:
41
105
  - "Pure function" → Input → output, no I/O (define edge cases like Date.now())
42
106
  ```
43
107
 
44
- **3. No Contradictions**
108
+ ### 3. No Contradictions
45
109
 
46
110
  Different sections must align. LLMs don't reconcile conflicting guidance. When updating, grep for related terms and update all references.
47
111
 
@@ -57,7 +121,7 @@ Section B: "All critical multi-page user flows have at least one E2E test"
57
121
  - Definition of "critical" with examples
58
122
  ```
59
123
 
60
- **4. Concrete Examples Over Abstract Rules**
124
+ ### 4. Concrete Examples Over Abstract Rules
61
125
 
62
126
  Show, don't just tell. LLMs learn patterns from examples. For every rule, include 2-3 concrete examples showing good vs bad.
63
127
 
@@ -78,9 +142,9 @@ expect(calculateDiscount(100, 0.20)).toBe(80)
78
142
  })
79
143
  ```
80
144
 
81
- **5. Edge Cases Must Be Explicit**
145
+ ### 5. Edge Cases Must Be Explicit
82
146
 
83
- What seems obvious to humans often isn't to LLMs. After stating a rule, add "Edge cases:" section with common confusing scenarios.
147
+ What seems obvious to humans often isn't to LLMs. After stating a rule, add "Edge cases:" section.
84
148
 
85
149
  ```markdown
86
150
  ❌ BAD: "Unit test pure functions"
@@ -94,9 +158,9 @@ Edge cases:
94
158
  - Mixed pure + I/O → Extract pure part, unit test separately
95
159
  ```
96
160
 
97
- **6. Actionable Over Vague**
161
+ ### 6. Actionable Over Vague
98
162
 
99
- Give LLMs concrete actions, not subjective guidance. Replace subjective terms (most/some/few) with optimization rules + red flags.
163
+ Replace subjective terms with optimization rules + red flags.
100
164
 
101
165
  ```markdown
102
166
  ❌ BAD: "Most tests: Fast, Some tests: Slow"
@@ -108,9 +172,9 @@ Give LLMs concrete actions, not subjective guidance. Replace subjective terms (m
108
172
  - Red flag: If you have more E2E tests than integration tests, suite is too slow
109
173
  ```
110
174
 
111
- **7. Decision Trees: Sequential Over Parallel**
175
+ ### 7. Decision Trees: Sequential Over Parallel
112
176
 
113
- Structure decisions as ordered steps, not simultaneous checks. Sequential questions force the LLM through a deterministic decision path.
177
+ Structure decisions as ordered steps, not simultaneous checks.
114
178
 
115
179
  ```markdown
116
180
  ❌ BAD - Parallel branches:
@@ -118,11 +182,11 @@ Structure decisions as ordered steps, not simultaneous checks. Sequential questi
118
182
  ├─ Multiple components?
119
183
  └─ Full user flow?
120
184
 
121
- ✅ GOOD - Sequential (see Principle 1 example above)
185
+ ✅ GOOD - Sequential:
122
186
  Answer questions IN ORDER. Stop at the first match.
123
187
  ```
124
188
 
125
- **8. Tie-Breaking Rules**
189
+ ### 8. Tie-Breaking Rules
126
190
 
127
191
  When multiple options could apply, tell LLMs how to choose.
128
192
 
@@ -134,9 +198,9 @@ Reference in decision trees:
134
198
  "If multiple seem to apply, use the tie-breaking rule stated above: choose the faster one."
135
199
  ```
136
200
 
137
- **9. Lookup Tables for Complex Decisions**
201
+ ### 9. Lookup Tables for Complex Decisions
138
202
 
139
- When decision logic has 3+ branches, nested conditions, or multiple variables to consider, provide a reference table.
203
+ When decision logic has 3+ branches, provide a reference table.
140
204
 
141
205
  ```markdown
142
206
  | Bug Type | Unit? | Integration? | E2E? | Best Choice |
@@ -146,16 +210,16 @@ When decision logic has 3+ branches, nested conditions, or multiple variables to
146
210
  | CSS layout broken | ❌ | ❌ | ✅ | E2E (only option) |
147
211
  ```
148
212
 
149
- **10. Avoid Caveats in Tables**
213
+ ### 10. Avoid Caveats in Tables
150
214
 
151
- Keep patterns clean. Parentheticals break LLM pattern matching. Add separate rows for caveat cases.
215
+ Keep patterns clean. Parentheticals break LLM pattern matching.
152
216
 
153
217
  ```markdown
154
218
  ❌ BAD: | State management bug | ❌ NO (if mocked) | ✅ YES |
155
219
  ✅ GOOD: | State management bug (Zustand, Redux) | ❌ NO | ✅ YES |
156
220
  ```
157
221
 
158
- **11. Percentages: Context or None**
222
+ ### 11. Percentages: Context or None
159
223
 
160
224
  Don't use percentages without adjustment guidance.
161
225
 
@@ -167,7 +231,7 @@ Don't use percentages without adjustment guidance.
167
231
  ✅ BEST: "Write as many fast tests as possible. Red flag: More E2E than integration = too slow."
168
232
  ```
169
233
 
170
- **12. Specificity in Questions**
234
+ ### 12. Specificity in Questions
171
235
 
172
236
  Use precise technical terms, not general descriptions.
173
237
 
@@ -178,7 +242,7 @@ Use precise technical terms, not general descriptions.
178
242
  Note: React Testing Library does NOT require a browser - that's integration testing.
179
243
  ```
180
244
 
181
- **13. Re-evaluation Paths**
245
+ ### 13. Re-evaluation Paths
182
246
 
183
247
  When LLMs hit dead ends, provide concrete next steps.
184
248
 
@@ -195,13 +259,17 @@ When LLMs hit dead ends, provide concrete next steps.
195
259
  - Login form → Dashboard → E2E test (multi-page)"
196
260
  ```
197
261
 
198
- ## Anti-Patterns to Avoid
262
+ ---
263
+
264
+ ## Anti-Patterns
199
265
 
200
266
  ❌ **Visual metaphors** - Pyramids, icebergs—LLMs don't process visual information well
201
267
  ❌ **Undefined jargon** - "Technical debt", "code smell" need definitions
202
268
  ❌ **Competing guidance** - Multiple decision frameworks that contradict each other
203
269
  ❌ **Outdated references** - Remove concepts, but forget to update all mentions
204
270
 
271
+ ---
272
+
205
273
  ## Quality Checklist
206
274
 
207
275
  Before saving/committing LLM-consumable documentation:
@@ -216,11 +284,7 @@ Before saving/committing LLM-consumable documentation:
216
284
  - [ ] Complex decisions (3+ branches) have lookup tables
217
285
  - [ ] Dead-end paths have re-evaluation steps with examples
218
286
 
219
- ## Research-Backed Principles
220
-
221
- - **MECE (McKinsey):** Mutually exclusive, collectively exhaustive decision trees for reliable LLM decisions
222
- - **Prompt ambiguity (2025):** "Ambiguity is one of the most common causes of poor LLM output" (Zero-Shot Decision Tree Construction)
223
- - **Concrete examples (2025):** Structured approaches with concrete examples consistently improve performance over "act as" or "###" techniques
287
+ ---
224
288
 
225
289
  ## Example: Before and After
226
290
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Patterns and examples for user stories and test definitions following TDD best practices.
4
4
 
5
- **LLM Instruction Design:** These templates create documentation that LLMs read and follow. For comprehensive framework on writing clear, actionable LLM-consumable documentation, see `@.safeword/guides/llm-instruction-design.md`.
5
+ **LLM Instruction Design:** These templates create documentation that LLMs read and follow. For comprehensive framework on writing clear, actionable LLM-consumable documentation, see `@.safeword/guides/llm-guide.md`.
6
6
 
7
7
  ---
8
8
 
@@ -64,7 +64,7 @@ Patterns and examples for user stories and test definitions following TDD best p
64
64
  - "Create a design doc for [feature]" → Uses design doc template (2-3 pages)
65
65
  - "Update the project architecture doc" → Adds to existing ARCHITECTURE.md
66
66
 
67
- **TDD Workflow:** See `@.safeword/guides/testing-methodology.md` for comprehensive RED → GREEN → REFACTOR workflow with latest best practices
67
+ **TDD Workflow:** See `@.safeword/guides/development-workflow.md` for comprehensive RED → GREEN → REFACTOR workflow with latest best practices
68
68
 
69
69
  ---
70
70
 
@@ -325,7 +325,7 @@ npm run test:e2e -- tests/feature-name.spec.ts --grep "specific test name"
325
325
 
326
326
  **Important:** Test definitions are instructions that LLMs read and follow. Apply best practices for clarity.
327
327
 
328
- **See:** `@.safeword/guides/llm-instruction-design.md` for comprehensive framework including:
328
+ **See:** `@.safeword/guides/llm-guide.md` for comprehensive framework including:
329
329
 
330
330
  - MECE decision trees (mutually exclusive, collectively exhaustive)
331
331
  - Explicit definitions (never assume LLMs know what you mean)
@@ -221,7 +221,7 @@ After filling out story, mentally check:
221
221
 
222
222
  **Core principle:** User stories are instructions that LLMs read and follow. Apply LLM instruction design best practices.
223
223
 
224
- **See:** `@.safeword/guides/llm-instruction-design.md` for comprehensive framework on writing LLM-consumable documentation.
224
+ **See:** `@.safeword/guides/llm-guide.md` for comprehensive framework on writing LLM-consumable documentation.
225
225
 
226
226
  **When filling templates:**
227
227
 
@@ -26,7 +26,7 @@ When running dev servers and E2E tests across multiple projects, zombie processe
26
26
  - **Dev port**: Project's configured port (e.g., 3000, 5173, 8080) - manual testing
27
27
  - **Test port**: Dev port + 1000 (e.g., 4000, 6173, 9080) - Playwright managed
28
28
 
29
- See `testing-methodology.md` → "E2E Testing with Persistent Dev Servers" for full port isolation strategy.
29
+ See `development-workflow.md` → "E2E Testing with Persistent Dev Servers" for full port isolation strategy.
30
30
 
31
31
  **Decision rule:** If unsure which cleanup method to use → port-based first (safest), then project script, then tmux.
32
32
 
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/check.ts"],"sourcesContent":["/**\n * Check command - Verify project health and configuration\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, readFile, readFileSafe } from '../utils/fs.js';\nimport { info, success, warn, header, keyValue } from '../utils/output.js';\nimport { isNewerVersion } from '../utils/version.js';\n\nexport interface CheckOptions {\n offline?: boolean;\n}\n\ninterface HealthStatus {\n configured: boolean;\n projectVersion: string | null;\n cliVersion: string;\n updateAvailable: boolean;\n latestVersion: string | null;\n issues: string[];\n}\n\n/**\n * Check for latest version from npm (with timeout)\n */\nasync function checkLatestVersion(timeout = 3000): Promise<string | null> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const response = await fetch('https://registry.npmjs.org/safeword/latest', {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as { version?: string };\n return data.version ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check project configuration health\n */\nfunction checkHealth(cwd: string): HealthStatus {\n const safewordDir = join(cwd, '.safeword');\n const issues: string[] = [];\n\n // Check if configured\n if (!exists(safewordDir)) {\n return {\n configured: false,\n projectVersion: null,\n cliVersion: VERSION,\n updateAvailable: false,\n latestVersion: null,\n issues: [],\n };\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? null;\n\n // Check for required files\n const requiredFiles = ['SAFEWORD.md', 'version', 'hooks/session-verify-agents.sh'];\n\n for (const file of requiredFiles) {\n if (!exists(join(safewordDir, file))) {\n issues.push(`Missing: .safeword/${file}`);\n }\n }\n\n // Check AGENTS.md link\n const agentsMdPath = join(cwd, 'AGENTS.md');\n if (exists(agentsMdPath)) {\n const content = readFile(agentsMdPath);\n if (!content.includes('@./.safeword/SAFEWORD.md')) {\n issues.push('AGENTS.md missing safeword link');\n }\n } else {\n issues.push('AGENTS.md file missing');\n }\n\n // Check .claude/settings.json\n const settingsPath = join(cwd, '.claude', 'settings.json');\n if (!exists(settingsPath)) {\n issues.push('Missing: .claude/settings.json');\n }\n\n return {\n configured: true,\n projectVersion,\n cliVersion: VERSION,\n updateAvailable: false,\n latestVersion: null,\n issues,\n };\n}\n\nexport async function check(options: CheckOptions): Promise<void> {\n const cwd = process.cwd();\n\n header('Safeword Health Check');\n\n const health = checkHealth(cwd);\n\n // Not configured\n if (!health.configured) {\n info('Not configured. Run `safeword setup` to initialize.');\n return;\n }\n\n // Show versions\n keyValue('Safeword CLI', `v${health.cliVersion}`);\n keyValue('Project config', health.projectVersion ? `v${health.projectVersion}` : 'unknown');\n\n // Check for updates (unless offline)\n if (!options.offline) {\n info('\\nChecking for updates...');\n const latestVersion = await checkLatestVersion();\n\n if (latestVersion) {\n health.latestVersion = latestVersion;\n health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);\n\n if (health.updateAvailable) {\n warn(`Update available: v${latestVersion}`);\n info('Run `npm install -g safeword` to upgrade');\n } else {\n success('CLI is up to date');\n }\n } else {\n warn(\"Couldn't check for updates (offline?)\");\n }\n } else {\n info('\\nSkipped update check (offline mode)');\n }\n\n // Check project version vs CLI version\n if (health.projectVersion && isNewerVersion(health.cliVersion, health.projectVersion)) {\n warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);\n info('Consider upgrading the CLI');\n } else if (health.projectVersion && isNewerVersion(health.projectVersion, health.cliVersion)) {\n info(`\\nUpgrade available for project config`);\n info(\n `Run \\`safeword upgrade\\` to update from v${health.projectVersion} to v${health.cliVersion}`,\n );\n }\n\n // Show issues\n if (health.issues.length > 0) {\n header('Issues Found');\n for (const issue of health.issues) {\n warn(issue);\n }\n info('\\nRun `safeword upgrade` to repair configuration');\n } else {\n success('\\nConfiguration is healthy');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAIA,SAAS,YAAY;AAsBrB,eAAe,mBAAmB,UAAU,KAA8B;AACxE,MAAI;AACF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,UAAM,WAAW,MAAM,MAAM,8CAA8C;AAAA,MACzE,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,iBAAa,SAAS;AAEtB,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,WAAW;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,SAAS,YAAY,KAA2B;AAC9C,QAAM,cAAc,KAAK,KAAK,WAAW;AACzC,QAAM,SAAmB,CAAC;AAG1B,MAAI,CAAC,OAAO,WAAW,GAAG;AACxB,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,eAAe;AAAA,MACf,QAAQ,CAAC;AAAA,IACX;AAAA,EACF;AAGA,QAAM,cAAc,KAAK,aAAa,SAAS;AAC/C,QAAM,iBAAiB,aAAa,WAAW,GAAG,KAAK,KAAK;AAG5D,QAAM,gBAAgB,CAAC,eAAe,WAAW,gCAAgC;AAEjF,aAAW,QAAQ,eAAe;AAChC,QAAI,CAAC,OAAO,KAAK,aAAa,IAAI,CAAC,GAAG;AACpC,aAAO,KAAK,sBAAsB,IAAI,EAAE;AAAA,IAC1C;AAAA,EACF;AAGA,QAAM,eAAe,KAAK,KAAK,WAAW;AAC1C,MAAI,OAAO,YAAY,GAAG;AACxB,UAAM,UAAU,SAAS,YAAY;AACrC,QAAI,CAAC,QAAQ,SAAS,0BAA0B,GAAG;AACjD,aAAO,KAAK,iCAAiC;AAAA,IAC/C;AAAA,EACF,OAAO;AACL,WAAO,KAAK,wBAAwB;AAAA,EACtC;AAGA,QAAM,eAAe,KAAK,KAAK,WAAW,eAAe;AACzD,MAAI,CAAC,OAAO,YAAY,GAAG;AACzB,WAAO,KAAK,gCAAgC;AAAA,EAC9C;AAEA,SAAO;AAAA,IACL,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf;AAAA,EACF;AACF;AAEA,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AAExB,SAAO,uBAAuB;AAE9B,QAAM,SAAS,YAAY,GAAG;AAG9B,MAAI,CAAC,OAAO,YAAY;AACtB,SAAK,qDAAqD;AAC1D;AAAA,EACF;AAGA,WAAS,gBAAgB,IAAI,OAAO,UAAU,EAAE;AAChD,WAAS,kBAAkB,OAAO,iBAAiB,IAAI,OAAO,cAAc,KAAK,SAAS;AAG1F,MAAI,CAAC,QAAQ,SAAS;AACpB,SAAK,2BAA2B;AAChC,UAAM,gBAAgB,MAAM,mBAAmB;AAE/C,QAAI,eAAe;AACjB,aAAO,gBAAgB;AACvB,aAAO,kBAAkB,eAAe,OAAO,YAAY,aAAa;AAExE,UAAI,OAAO,iBAAiB;AAC1B,aAAK,sBAAsB,aAAa,EAAE;AAC1C,aAAK,0CAA0C;AAAA,MACjD,OAAO;AACL,gBAAQ,mBAAmB;AAAA,MAC7B;AAAA,IACF,OAAO;AACL,WAAK,uCAAuC;AAAA,IAC9C;AAAA,EACF,OAAO;AACL,SAAK,uCAAuC;AAAA,EAC9C;AAGA,MAAI,OAAO,kBAAkB,eAAe,OAAO,YAAY,OAAO,cAAc,GAAG;AACrF,SAAK,oBAAoB,OAAO,cAAc,yBAAyB,OAAO,UAAU,GAAG;AAC3F,SAAK,4BAA4B;AAAA,EACnC,WAAW,OAAO,kBAAkB,eAAe,OAAO,gBAAgB,OAAO,UAAU,GAAG;AAC5F,SAAK;AAAA,qCAAwC;AAC7C;AAAA,MACE,4CAA4C,OAAO,cAAc,QAAQ,OAAO,UAAU;AAAA,IAC5F;AAAA,EACF;AAGA,MAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,WAAO,cAAc;AACrB,eAAW,SAAS,OAAO,QAAQ;AACjC,WAAK,KAAK;AAAA,IACZ;AACA,SAAK,kDAAkD;AAAA,EACzD,OAAO;AACL,YAAQ,4BAA4B;AAAA,EACtC;AACF;","names":[]}
@@ -1,43 +0,0 @@
1
- // src/utils/project-detector.ts
2
- function detectProjectType(packageJson) {
3
- const deps = packageJson.dependencies || {};
4
- const devDeps = packageJson.devDependencies || {};
5
- const allDeps = { ...deps, ...devDeps };
6
- const hasTypescript = "typescript" in allDeps;
7
- const hasReact = "react" in deps || "react" in devDeps;
8
- const hasNextJs = "next" in deps;
9
- const hasAstro = "astro" in deps || "astro" in devDeps;
10
- const hasVue = "vue" in deps || "vue" in devDeps;
11
- const hasNuxt = "nuxt" in deps;
12
- const hasSvelte = "svelte" in deps || "svelte" in devDeps;
13
- const hasSvelteKit = "@sveltejs/kit" in deps || "@sveltejs/kit" in devDeps;
14
- const hasElectron = "electron" in deps || "electron" in devDeps;
15
- const hasVitest = "vitest" in devDeps;
16
- const hasPlaywright = "@playwright/test" in devDeps;
17
- const hasTailwind = "tailwindcss" in allDeps;
18
- const hasEntryPoints = !!(packageJson.main || packageJson.module || packageJson.exports);
19
- const isPublishable = hasEntryPoints && packageJson.private !== true;
20
- return {
21
- typescript: hasTypescript,
22
- react: hasReact || hasNextJs,
23
- // Next.js implies React
24
- nextjs: hasNextJs,
25
- astro: hasAstro,
26
- vue: hasVue || hasNuxt,
27
- // Nuxt implies Vue
28
- nuxt: hasNuxt,
29
- svelte: hasSvelte || hasSvelteKit,
30
- // SvelteKit implies Svelte
31
- sveltekit: hasSvelteKit,
32
- electron: hasElectron,
33
- vitest: hasVitest,
34
- playwright: hasPlaywright,
35
- tailwind: hasTailwind,
36
- publishableLibrary: isPublishable
37
- };
38
- }
39
-
40
- export {
41
- detectProjectType
42
- };
43
- //# sourceMappingURL=chunk-6CVTH67L.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/project-detector.ts"],"sourcesContent":["/**\n * Project type detection from package.json\n *\n * Detects frameworks and tools used in the project to configure\n * appropriate linting rules.\n */\n\nexport interface PackageJson {\n name?: string;\n version?: string;\n private?: boolean;\n main?: string;\n module?: string;\n exports?: unknown;\n types?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport interface ProjectType {\n typescript: boolean;\n react: boolean;\n nextjs: boolean;\n astro: boolean;\n vue: boolean;\n nuxt: boolean;\n svelte: boolean;\n sveltekit: boolean;\n electron: boolean;\n vitest: boolean;\n playwright: boolean;\n tailwind: boolean;\n publishableLibrary: boolean;\n}\n\n/**\n * Detects project type from package.json contents\n */\nexport function detectProjectType(packageJson: PackageJson): ProjectType {\n const deps = packageJson.dependencies || {};\n const devDeps = packageJson.devDependencies || {};\n const allDeps = { ...deps, ...devDeps };\n\n const hasTypescript = 'typescript' in allDeps;\n const hasReact = 'react' in deps || 'react' in devDeps;\n const hasNextJs = 'next' in deps;\n const hasAstro = 'astro' in deps || 'astro' in devDeps;\n const hasVue = 'vue' in deps || 'vue' in devDeps;\n const hasNuxt = 'nuxt' in deps;\n const hasSvelte = 'svelte' in deps || 'svelte' in devDeps;\n const hasSvelteKit = '@sveltejs/kit' in deps || '@sveltejs/kit' in devDeps;\n const hasElectron = 'electron' in deps || 'electron' in devDeps;\n const hasVitest = 'vitest' in devDeps;\n const hasPlaywright = '@playwright/test' in devDeps;\n const hasTailwind = 'tailwindcss' in allDeps;\n\n // Publishable library: has entry points and is not marked private\n const hasEntryPoints = !!(packageJson.main || packageJson.module || packageJson.exports);\n const isPublishable = hasEntryPoints && packageJson.private !== true;\n\n return {\n typescript: hasTypescript,\n react: hasReact || hasNextJs, // Next.js implies React\n nextjs: hasNextJs,\n astro: hasAstro,\n vue: hasVue || hasNuxt, // Nuxt implies Vue\n nuxt: hasNuxt,\n svelte: hasSvelte || hasSvelteKit, // SvelteKit implies Svelte\n sveltekit: hasSvelteKit,\n electron: hasElectron,\n vitest: hasVitest,\n playwright: hasPlaywright,\n tailwind: hasTailwind,\n publishableLibrary: isPublishable,\n };\n}\n"],"mappings":";AAsCO,SAAS,kBAAkB,aAAuC;AACvE,QAAM,OAAO,YAAY,gBAAgB,CAAC;AAC1C,QAAM,UAAU,YAAY,mBAAmB,CAAC;AAChD,QAAM,UAAU,EAAE,GAAG,MAAM,GAAG,QAAQ;AAEtC,QAAM,gBAAgB,gBAAgB;AACtC,QAAM,WAAW,WAAW,QAAQ,WAAW;AAC/C,QAAM,YAAY,UAAU;AAC5B,QAAM,WAAW,WAAW,QAAQ,WAAW;AAC/C,QAAM,SAAS,SAAS,QAAQ,SAAS;AACzC,QAAM,UAAU,UAAU;AAC1B,QAAM,YAAY,YAAY,QAAQ,YAAY;AAClD,QAAM,eAAe,mBAAmB,QAAQ,mBAAmB;AACnE,QAAM,cAAc,cAAc,QAAQ,cAAc;AACxD,QAAM,YAAY,YAAY;AAC9B,QAAM,gBAAgB,sBAAsB;AAC5C,QAAM,cAAc,iBAAiB;AAGrC,QAAM,iBAAiB,CAAC,EAAE,YAAY,QAAQ,YAAY,UAAU,YAAY;AAChF,QAAM,gBAAgB,kBAAkB,YAAY,YAAY;AAEhE,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,OAAO,YAAY;AAAA;AAAA,IACnB,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,KAAK,UAAU;AAAA;AAAA,IACf,MAAM;AAAA,IACN,QAAQ,aAAa;AAAA;AAAA,IACrB,WAAW;AAAA,IACX,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,oBAAoB;AAAA,EACtB;AACF;","names":[]}
@@ -1,15 +0,0 @@
1
- import {
2
- exists
3
- } from "./chunk-ARIAOK2F.js";
4
-
5
- // src/utils/git.ts
6
- import { execSync } from "child_process";
7
- import { join } from "path";
8
- function isGitRepo(cwd) {
9
- return exists(join(cwd, ".git"));
10
- }
11
-
12
- export {
13
- isGitRepo
14
- };
15
- //# sourceMappingURL=chunk-75FKNZUM.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/git.ts"],"sourcesContent":["/**\n * Git utilities for CLI operations\n */\n\nimport { execSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { exists, readFile, writeFile, ensureDir, makeExecutable } from './fs.js';\n\nconst MARKER_START = '# SAFEWORD_ARCH_CHECK_START';\nconst MARKER_END = '# SAFEWORD_ARCH_CHECK_END';\n\n/**\n * Check if directory is a git repository\n */\nexport function isGitRepo(cwd: string): boolean {\n return exists(join(cwd, '.git'));\n}\n\n/**\n * Initialize a git repository\n */\nexport function initGitRepo(cwd: string): void {\n execSync('git init', { cwd, stdio: 'pipe' });\n}\n\n/**\n * Get the pre-commit hook content to add\n */\nfunction getHookContent(): string {\n return `\n${MARKER_START}\n# Safeword pre-commit linting\n# This section is managed by safeword - do not edit manually\nif [ -f \".safeword/hooks/git-pre-commit.sh\" ]; then\n bash .safeword/hooks/git-pre-commit.sh\nfi\n${MARKER_END}\n`;\n}\n\n/**\n * Install safeword markers into pre-commit hook\n */\nexport function installGitHook(cwd: string): void {\n const hooksDir = join(cwd, '.git', 'hooks');\n const hookPath = join(hooksDir, 'pre-commit');\n\n ensureDir(hooksDir);\n\n let content = '';\n\n if (exists(hookPath)) {\n content = readFile(hookPath);\n\n // Check if already has safeword markers\n if (content.includes(MARKER_START)) {\n // Remove existing safeword section and re-add (update)\n content = removeMarkerSection(content);\n }\n } else {\n // Create new hook file with shebang\n content = '#!/bin/bash\\n';\n }\n\n // Add safeword section\n content = content.trimEnd() + '\\n' + getHookContent();\n\n writeFile(hookPath, content);\n makeExecutable(hookPath);\n}\n\n/**\n * Remove safeword markers from pre-commit hook\n */\nexport function removeGitHook(cwd: string): void {\n const hookPath = join(cwd, '.git', 'hooks', 'pre-commit');\n\n if (!exists(hookPath)) return;\n\n let content = readFile(hookPath);\n\n if (!content.includes(MARKER_START)) return;\n\n content = removeMarkerSection(content);\n\n // If only shebang remains, we could delete the file\n // but safer to leave it\n writeFile(hookPath, content);\n}\n\n/**\n * Remove the section between markers (inclusive)\n */\nfunction removeMarkerSection(content: string): string {\n const lines = content.split('\\n');\n const result: string[] = [];\n let inMarkerSection = false;\n\n for (const line of lines) {\n if (line.includes(MARKER_START)) {\n inMarkerSection = true;\n continue;\n }\n if (line.includes(MARKER_END)) {\n inMarkerSection = false;\n continue;\n }\n if (!inMarkerSection) {\n result.push(line);\n }\n }\n\n return result.join('\\n').trim() + '\\n';\n}\n\n/**\n * Check if git hooks have safeword markers\n */\nexport function hasGitHook(cwd: string): boolean {\n const hookPath = join(cwd, '.git', 'hooks', 'pre-commit');\n if (!exists(hookPath)) return false;\n const content = readFile(hookPath);\n return content.includes(MARKER_START);\n}\n"],"mappings":";;;;;AAIA,SAAS,gBAAgB;AACzB,SAAS,YAAY;AASd,SAAS,UAAU,KAAsB;AAC9C,SAAO,OAAO,KAAK,KAAK,MAAM,CAAC;AACjC;","names":[]}