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.
- package/dist/{check-PECCGHEA.js → check-OYYSYHFP.js} +41 -23
- package/dist/check-OYYSYHFP.js.map +1 -0
- package/dist/chunk-LNSEDZIW.js +454 -0
- package/dist/chunk-LNSEDZIW.js.map +1 -0
- package/dist/chunk-ZS3Z3Q37.js +729 -0
- package/dist/chunk-ZS3Z3Q37.js.map +1 -0
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/diff-325TIZ63.js +168 -0
- package/dist/diff-325TIZ63.js.map +1 -0
- package/dist/reset-ZGJIKMUW.js +74 -0
- package/dist/reset-ZGJIKMUW.js.map +1 -0
- package/dist/setup-GAMXTFM2.js +103 -0
- package/dist/setup-GAMXTFM2.js.map +1 -0
- package/dist/{sync-4XBMKLXS.js → sync-BFMXZEHM.js} +33 -32
- package/dist/sync-BFMXZEHM.js.map +1 -0
- package/dist/upgrade-X4GREJXN.js +73 -0
- package/dist/upgrade-X4GREJXN.js.map +1 -0
- package/package.json +15 -14
- package/templates/SAFEWORD.md +101 -689
- package/templates/guides/architecture-guide.md +1 -1
- package/templates/guides/cli-reference.md +35 -0
- package/templates/guides/code-philosophy.md +22 -19
- package/templates/guides/context-files-guide.md +2 -2
- package/templates/guides/data-architecture-guide.md +1 -1
- package/templates/guides/design-doc-guide.md +1 -1
- package/templates/guides/{testing-methodology.md → development-workflow.md} +1 -1
- package/templates/guides/learning-extraction.md +1 -1
- package/templates/guides/{llm-instruction-design.md → llm-guide.md} +93 -29
- package/templates/guides/tdd-best-practices.md +2 -2
- package/templates/guides/test-definitions-guide.md +1 -1
- package/templates/guides/user-story-guide.md +1 -1
- package/templates/guides/zombie-process-cleanup.md +1 -1
- package/dist/check-PECCGHEA.js.map +0 -1
- package/dist/chunk-6CVTH67L.js +0 -43
- package/dist/chunk-6CVTH67L.js.map +0 -1
- package/dist/chunk-75FKNZUM.js +0 -15
- package/dist/chunk-75FKNZUM.js.map +0 -1
- package/dist/chunk-ARIAOK2F.js +0 -110
- package/dist/chunk-ARIAOK2F.js.map +0 -1
- package/dist/chunk-FRPJITGG.js +0 -35
- package/dist/chunk-FRPJITGG.js.map +0 -1
- package/dist/chunk-IWWBZVHT.js +0 -274
- package/dist/chunk-IWWBZVHT.js.map +0 -1
- package/dist/diff-ZACVJKOU.js +0 -171
- package/dist/diff-ZACVJKOU.js.map +0 -1
- package/dist/reset-5SRM3P6J.js +0 -145
- package/dist/reset-5SRM3P6J.js.map +0 -1
- package/dist/setup-65EVU5OT.js +0 -437
- package/dist/setup-65EVU5OT.js.map +0 -1
- package/dist/sync-4XBMKLXS.js.map +0 -1
- package/dist/upgrade-P3WX3ODU.js +0 -153
- package/dist/upgrade-P3WX3ODU.js.map +0 -1
- package/templates/guides/llm-prompting.md +0 -102
- /package/templates/prompts/{review.md → quality-review.md} +0 -0
|
@@ -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-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
|
47
|
-
|
|
48
|
-
|
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
|
52
|
+
|
|
53
|
+
| ❌ Bad | ✅ Good |
|
|
54
|
+
| ------------------ | ------------------------------ |
|
|
55
|
+
| `calcTot` | `calculateTotalWithTax` |
|
|
54
56
|
| `d`, `tmp`, `data` | `userProfile`, `pendingOrders` |
|
|
55
|
-
| `handleClick`
|
|
56
|
-
| `process()`
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
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/
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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/
|
|
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-
|
|
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
|
-
#
|
|
1
|
+
# LLM Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This guide covers two related topics:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Part 1: Integration** - How to call LLMs effectively (API calls, structured outputs, caching, testing)
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Part 2: Writing for LLMs** - How to write documentation that LLMs will read and follow (SAFEWORD.md, CLAUDE.md, guides)
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
161
|
+
### 6. Actionable Over Vague
|
|
98
162
|
|
|
99
|
-
|
|
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
|
-
|
|
175
|
+
### 7. Decision Trees: Sequential Over Parallel
|
|
112
176
|
|
|
113
|
-
Structure decisions as ordered steps, not simultaneous checks.
|
|
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
|
|
185
|
+
✅ GOOD - Sequential:
|
|
122
186
|
Answer questions IN ORDER. Stop at the first match.
|
|
123
187
|
```
|
|
124
188
|
|
|
125
|
-
|
|
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
|
-
|
|
201
|
+
### 9. Lookup Tables for Complex Decisions
|
|
138
202
|
|
|
139
|
-
When decision logic has 3+ branches,
|
|
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
|
-
|
|
213
|
+
### 10. Avoid Caveats in Tables
|
|
150
214
|
|
|
151
|
-
Keep patterns clean. Parentheticals break LLM pattern matching.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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/
|
|
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-
|
|
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-
|
|
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 `
|
|
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":"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","names":[]}
|
package/dist/chunk-6CVTH67L.js
DELETED
|
@@ -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":[]}
|
package/dist/chunk-75FKNZUM.js
DELETED
|
@@ -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":[]}
|