safeword 0.2.4 → 0.2.6

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 (235) hide show
  1. package/dist/check-3NGQ4NR5.js +129 -0
  2. package/dist/check-3NGQ4NR5.js.map +1 -0
  3. package/dist/chunk-2XWIUEQK.js +190 -0
  4. package/dist/chunk-2XWIUEQK.js.map +1 -0
  5. package/dist/chunk-GZRQL3SX.js +146 -0
  6. package/dist/chunk-GZRQL3SX.js.map +1 -0
  7. package/dist/chunk-ORQHKDT2.js +10 -0
  8. package/dist/chunk-ORQHKDT2.js.map +1 -0
  9. package/dist/chunk-W66Z3C5H.js +21 -0
  10. package/dist/chunk-W66Z3C5H.js.map +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +34 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/diff-Y6QTAW4O.js +166 -0
  15. package/dist/diff-Y6QTAW4O.js.map +1 -0
  16. package/dist/index.d.ts +11 -0
  17. package/dist/index.js +7 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/reset-3ACTIYYE.js +143 -0
  20. package/dist/reset-3ACTIYYE.js.map +1 -0
  21. package/dist/setup-AIL5RL45.js +276 -0
  22. package/dist/setup-AIL5RL45.js.map +1 -0
  23. package/dist/upgrade-6AR3DHUV.js +134 -0
  24. package/dist/upgrade-6AR3DHUV.js.map +1 -0
  25. package/package.json +44 -19
  26. package/{.safeword → templates}/hooks/agents-md-check.sh +0 -0
  27. package/{.safeword → templates}/hooks/post-tool.sh +0 -0
  28. package/{.safeword → templates}/hooks/pre-commit.sh +0 -0
  29. package/.claude/commands/arch-review.md +0 -32
  30. package/.claude/commands/lint.md +0 -6
  31. package/.claude/commands/quality-review.md +0 -13
  32. package/.claude/commands/setup-linting.md +0 -6
  33. package/.claude/hooks/auto-lint.sh +0 -6
  34. package/.claude/hooks/auto-quality-review.sh +0 -170
  35. package/.claude/hooks/check-linting-sync.sh +0 -17
  36. package/.claude/hooks/inject-timestamp.sh +0 -6
  37. package/.claude/hooks/question-protocol.sh +0 -12
  38. package/.claude/hooks/run-linters.sh +0 -8
  39. package/.claude/hooks/run-quality-review.sh +0 -76
  40. package/.claude/hooks/version-check.sh +0 -10
  41. package/.claude/mcp/README.md +0 -96
  42. package/.claude/mcp/arcade.sample.json +0 -9
  43. package/.claude/mcp/context7.sample.json +0 -7
  44. package/.claude/mcp/playwright.sample.json +0 -7
  45. package/.claude/settings.json +0 -62
  46. package/.claude/skills/quality-reviewer/SKILL.md +0 -190
  47. package/.claude/skills/safeword-quality-reviewer/SKILL.md +0 -13
  48. package/.env.arcade.example +0 -4
  49. package/.env.example +0 -11
  50. package/.gitmodules +0 -4
  51. package/.safeword/SAFEWORD.md +0 -33
  52. package/.safeword/eslint/eslint-base.mjs +0 -101
  53. package/.safeword/guides/architecture-guide.md +0 -404
  54. package/.safeword/guides/code-philosophy.md +0 -174
  55. package/.safeword/guides/context-files-guide.md +0 -405
  56. package/.safeword/guides/data-architecture-guide.md +0 -183
  57. package/.safeword/guides/design-doc-guide.md +0 -165
  58. package/.safeword/guides/learning-extraction.md +0 -515
  59. package/.safeword/guides/llm-instruction-design.md +0 -239
  60. package/.safeword/guides/llm-prompting.md +0 -95
  61. package/.safeword/guides/tdd-best-practices.md +0 -570
  62. package/.safeword/guides/test-definitions-guide.md +0 -243
  63. package/.safeword/guides/testing-methodology.md +0 -573
  64. package/.safeword/guides/user-story-guide.md +0 -237
  65. package/.safeword/guides/zombie-process-cleanup.md +0 -214
  66. package/.safeword/planning/002-user-story-quality-evaluation.md +0 -1840
  67. package/.safeword/planning/003-langsmith-eval-setup-prompt.md +0 -363
  68. package/.safeword/planning/004-llm-eval-test-cases.md +0 -3226
  69. package/.safeword/planning/005-architecture-enforcement-system.md +0 -169
  70. package/.safeword/planning/006-reactive-fix-prevention-research.md +0 -135
  71. package/.safeword/planning/011-cli-ux-vision.md +0 -330
  72. package/.safeword/planning/012-project-structure-cleanup.md +0 -154
  73. package/.safeword/planning/README.md +0 -39
  74. package/.safeword/planning/automation-plan-v2.md +0 -1225
  75. package/.safeword/planning/automation-plan-v3.md +0 -1291
  76. package/.safeword/planning/automation-plan.md +0 -3058
  77. package/.safeword/planning/design/005-cli-implementation.md +0 -343
  78. package/.safeword/planning/design/013-cli-self-contained-templates.md +0 -596
  79. package/.safeword/planning/design/013a-eslint-plugin-suite.md +0 -256
  80. package/.safeword/planning/design/013b-implementation-snippets.md +0 -385
  81. package/.safeword/planning/design/013c-config-isolation-strategy.md +0 -242
  82. package/.safeword/planning/design/code-philosophy-improvements.md +0 -60
  83. package/.safeword/planning/mcp-analysis.md +0 -545
  84. package/.safeword/planning/phase2-subagents-vs-skills-analysis.md +0 -451
  85. package/.safeword/planning/settings-improvements.md +0 -970
  86. package/.safeword/planning/test-definitions/005-cli-implementation.md +0 -1301
  87. package/.safeword/planning/test-definitions/cli-self-contained-templates.md +0 -205
  88. package/.safeword/planning/user-stories/001-guides-review-user-stories.md +0 -1381
  89. package/.safeword/planning/user-stories/003-reactive-fix-prevention.md +0 -132
  90. package/.safeword/planning/user-stories/004-technical-constraints.md +0 -86
  91. package/.safeword/planning/user-stories/005-cli-implementation.md +0 -311
  92. package/.safeword/planning/user-stories/cli-self-contained-templates.md +0 -172
  93. package/.safeword/planning/versioned-distribution.md +0 -740
  94. package/.safeword/prompts/arch-review.md +0 -43
  95. package/.safeword/prompts/quality-review.md +0 -11
  96. package/.safeword/scripts/arch-review.sh +0 -235
  97. package/.safeword/scripts/check-linting-sync.sh +0 -58
  98. package/.safeword/scripts/setup-linting.sh +0 -559
  99. package/.safeword/templates/architecture-template.md +0 -136
  100. package/.safeword/templates/ci/architecture-check.yml +0 -79
  101. package/.safeword/templates/design-doc-template.md +0 -127
  102. package/.safeword/templates/test-definitions-feature.md +0 -100
  103. package/.safeword/templates/ticket-template.md +0 -74
  104. package/.safeword/templates/user-stories-template.md +0 -82
  105. package/.safeword/tickets/001-guides-review-user-stories.md +0 -83
  106. package/.safeword/tickets/002-architecture-enforcement.md +0 -211
  107. package/.safeword/tickets/003-reactive-fix-prevention.md +0 -57
  108. package/.safeword/tickets/004-technical-constraints-in-user-stories.md +0 -39
  109. package/.safeword/tickets/005-cli-implementation.md +0 -248
  110. package/.safeword/tickets/006-flesh-out-skills.md +0 -43
  111. package/.safeword/tickets/007-flesh-out-questioning.md +0 -44
  112. package/.safeword/tickets/008-upgrade-questioning.md +0 -58
  113. package/.safeword/tickets/009-naming-conventions.md +0 -41
  114. package/.safeword/tickets/010-safeword-md-cleanup.md +0 -34
  115. package/.safeword/tickets/011-cursor-setup.md +0 -86
  116. package/.safeword/tickets/README.md +0 -73
  117. package/.safeword/version +0 -1
  118. package/AGENTS.md +0 -59
  119. package/CLAUDE.md +0 -12
  120. package/README.md +0 -347
  121. package/docs/001-cli-implementation-plan.md +0 -856
  122. package/docs/elite-dx-implementation-plan.md +0 -1034
  123. package/framework/README.md +0 -131
  124. package/framework/mcp/README.md +0 -96
  125. package/framework/mcp/arcade.sample.json +0 -8
  126. package/framework/mcp/context7.sample.json +0 -6
  127. package/framework/mcp/playwright.sample.json +0 -6
  128. package/framework/scripts/arch-review.sh +0 -235
  129. package/framework/scripts/check-linting-sync.sh +0 -58
  130. package/framework/scripts/load-env.sh +0 -49
  131. package/framework/scripts/setup-claude.sh +0 -223
  132. package/framework/scripts/setup-linting.sh +0 -559
  133. package/framework/scripts/setup-quality.sh +0 -477
  134. package/framework/scripts/setup-safeword.sh +0 -550
  135. package/framework/templates/ci/architecture-check.yml +0 -78
  136. package/learnings/ai-sdk-v5-breaking-changes.md +0 -178
  137. package/learnings/e2e-test-zombie-processes.md +0 -231
  138. package/learnings/milkdown-crepe-editor-property.md +0 -96
  139. package/learnings/prosemirror-fragment-traversal.md +0 -119
  140. package/packages/cli/AGENTS.md +0 -1
  141. package/packages/cli/ARCHITECTURE.md +0 -279
  142. package/packages/cli/package.json +0 -51
  143. package/packages/cli/src/cli.ts +0 -63
  144. package/packages/cli/src/commands/check.ts +0 -166
  145. package/packages/cli/src/commands/diff.ts +0 -209
  146. package/packages/cli/src/commands/reset.ts +0 -190
  147. package/packages/cli/src/commands/setup.ts +0 -325
  148. package/packages/cli/src/commands/upgrade.ts +0 -163
  149. package/packages/cli/src/index.ts +0 -3
  150. package/packages/cli/src/templates/config.ts +0 -58
  151. package/packages/cli/src/templates/content.ts +0 -18
  152. package/packages/cli/src/templates/index.ts +0 -12
  153. package/packages/cli/src/utils/agents-md.ts +0 -66
  154. package/packages/cli/src/utils/fs.ts +0 -179
  155. package/packages/cli/src/utils/git.ts +0 -124
  156. package/packages/cli/src/utils/hooks.ts +0 -29
  157. package/packages/cli/src/utils/output.ts +0 -60
  158. package/packages/cli/src/utils/project-detector.test.ts +0 -185
  159. package/packages/cli/src/utils/project-detector.ts +0 -44
  160. package/packages/cli/src/utils/version.ts +0 -28
  161. package/packages/cli/src/version.ts +0 -6
  162. package/packages/cli/templates/SAFEWORD.md +0 -776
  163. package/packages/cli/templates/doc-templates/architecture-template.md +0 -136
  164. package/packages/cli/templates/doc-templates/design-doc-template.md +0 -134
  165. package/packages/cli/templates/doc-templates/test-definitions-feature.md +0 -131
  166. package/packages/cli/templates/doc-templates/ticket-template.md +0 -82
  167. package/packages/cli/templates/doc-templates/user-stories-template.md +0 -92
  168. package/packages/cli/templates/guides/architecture-guide.md +0 -423
  169. package/packages/cli/templates/guides/code-philosophy.md +0 -195
  170. package/packages/cli/templates/guides/context-files-guide.md +0 -457
  171. package/packages/cli/templates/guides/data-architecture-guide.md +0 -200
  172. package/packages/cli/templates/guides/design-doc-guide.md +0 -171
  173. package/packages/cli/templates/guides/learning-extraction.md +0 -552
  174. package/packages/cli/templates/guides/llm-instruction-design.md +0 -248
  175. package/packages/cli/templates/guides/llm-prompting.md +0 -102
  176. package/packages/cli/templates/guides/tdd-best-practices.md +0 -615
  177. package/packages/cli/templates/guides/test-definitions-guide.md +0 -334
  178. package/packages/cli/templates/guides/testing-methodology.md +0 -618
  179. package/packages/cli/templates/guides/user-story-guide.md +0 -256
  180. package/packages/cli/templates/guides/zombie-process-cleanup.md +0 -219
  181. package/packages/cli/templates/hooks/agents-md-check.sh +0 -27
  182. package/packages/cli/templates/hooks/post-tool.sh +0 -4
  183. package/packages/cli/templates/hooks/pre-commit.sh +0 -10
  184. package/packages/cli/templates/prompts/arch-review.md +0 -43
  185. package/packages/cli/templates/prompts/quality-review.md +0 -10
  186. package/packages/cli/templates/skills/safeword-quality-reviewer/SKILL.md +0 -207
  187. package/packages/cli/tests/commands/check.test.ts +0 -129
  188. package/packages/cli/tests/commands/cli.test.ts +0 -89
  189. package/packages/cli/tests/commands/diff.test.ts +0 -115
  190. package/packages/cli/tests/commands/reset.test.ts +0 -310
  191. package/packages/cli/tests/commands/self-healing.test.ts +0 -170
  192. package/packages/cli/tests/commands/setup-blocking.test.ts +0 -71
  193. package/packages/cli/tests/commands/setup-core.test.ts +0 -135
  194. package/packages/cli/tests/commands/setup-git.test.ts +0 -139
  195. package/packages/cli/tests/commands/setup-hooks.test.ts +0 -334
  196. package/packages/cli/tests/commands/setup-linting.test.ts +0 -189
  197. package/packages/cli/tests/commands/setup-noninteractive.test.ts +0 -80
  198. package/packages/cli/tests/commands/setup-templates.test.ts +0 -181
  199. package/packages/cli/tests/commands/upgrade.test.ts +0 -215
  200. package/packages/cli/tests/helpers.ts +0 -243
  201. package/packages/cli/tests/npm-package.test.ts +0 -83
  202. package/packages/cli/tests/technical-constraints.test.ts +0 -96
  203. package/packages/cli/tsconfig.json +0 -25
  204. package/packages/cli/tsup.config.ts +0 -11
  205. package/packages/cli/vitest.config.ts +0 -23
  206. package/promptfoo.yaml +0 -3270
  207. /package/{framework → templates}/SAFEWORD.md +0 -0
  208. /package/{packages/cli/templates → templates}/commands/arch-review.md +0 -0
  209. /package/{packages/cli/templates → templates}/commands/lint.md +0 -0
  210. /package/{packages/cli/templates → templates}/commands/quality-review.md +0 -0
  211. /package/{framework/templates → templates/doc-templates}/architecture-template.md +0 -0
  212. /package/{framework/templates → templates/doc-templates}/design-doc-template.md +0 -0
  213. /package/{framework/templates → templates/doc-templates}/test-definitions-feature.md +0 -0
  214. /package/{framework/templates → templates/doc-templates}/ticket-template.md +0 -0
  215. /package/{framework/templates → templates/doc-templates}/user-stories-template.md +0 -0
  216. /package/{framework → templates}/guides/architecture-guide.md +0 -0
  217. /package/{framework → templates}/guides/code-philosophy.md +0 -0
  218. /package/{framework → templates}/guides/context-files-guide.md +0 -0
  219. /package/{framework → templates}/guides/data-architecture-guide.md +0 -0
  220. /package/{framework → templates}/guides/design-doc-guide.md +0 -0
  221. /package/{framework → templates}/guides/learning-extraction.md +0 -0
  222. /package/{framework → templates}/guides/llm-instruction-design.md +0 -0
  223. /package/{framework → templates}/guides/llm-prompting.md +0 -0
  224. /package/{framework → templates}/guides/tdd-best-practices.md +0 -0
  225. /package/{framework → templates}/guides/test-definitions-guide.md +0 -0
  226. /package/{framework → templates}/guides/testing-methodology.md +0 -0
  227. /package/{framework → templates}/guides/user-story-guide.md +0 -0
  228. /package/{framework → templates}/guides/zombie-process-cleanup.md +0 -0
  229. /package/{packages/cli/templates → templates}/hooks/inject-timestamp.sh +0 -0
  230. /package/{packages/cli/templates → templates}/lib/common.sh +0 -0
  231. /package/{packages/cli/templates → templates}/lib/jq-fallback.sh +0 -0
  232. /package/{packages/cli/templates → templates}/markdownlint.jsonc +0 -0
  233. /package/{framework → templates}/prompts/arch-review.md +0 -0
  234. /package/{framework → templates}/prompts/quality-review.md +0 -0
  235. /package/{framework/skills/quality-reviewer → templates/skills/safeword-quality-reviewer}/SKILL.md +0 -0
@@ -1,178 +0,0 @@
1
- # AI SDK v5 Breaking Changes
2
-
3
- **Principle:** AI SDK v5 removed `input` and `setInput` from `useChat` hook. You must manage text input state locally.
4
-
5
- ## The Gotcha
6
-
7
- What breaks when upgrading from AI SDK v4 to v5:
8
-
9
- ❌ **Bad (AI SDK v4 pattern):**
10
-
11
- ```typescript
12
- import { useChat } from '@ai-sdk/react'
13
-
14
- export function Chat() {
15
- const { input, setInput, messages, sendMessage } = useChat({
16
- api: '/api/chat'
17
- })
18
-
19
- // input = undefined, setInput = undefined in v5!
20
- return <input value={input} onChange={(e) => setInput(e.target.value)} />
21
- }
22
- ```
23
-
24
- ✅ **Good (AI SDK v5 pattern):**
25
-
26
- ```typescript
27
- import { useChat } from '@ai-sdk/react'
28
- import { useState } from 'react'
29
-
30
- export function Chat() {
31
- // Manage input state locally
32
- const [input, setInput] = useState('')
33
-
34
- const { messages, sendMessage } = useChat({
35
- api: '/api/chat'
36
- })
37
-
38
- return <input value={input} onChange={(e) => setInput(e.target.value)} />
39
- }
40
- ```
41
-
42
- **Why it matters:**
43
-
44
- - `!undefined` = `true` → buttons stay disabled when checking `!input`
45
- - `setInput(value)` throws "setInput is not a function"
46
- - Tests may pass (if using controlled component wrappers) while browser fails
47
-
48
- ## Breaking Changes in AI SDK v5
49
-
50
- ### Removed from useChat return value:
51
-
52
- - ❌ `input` (string)
53
- - ❌ `setInput` (function)
54
- - ❌ `handleSubmit` (function)
55
- - ❌ `handleInputChange` (function)
56
-
57
- ### Still available:
58
-
59
- - ✅ `messages`
60
- - ✅ `setMessages`
61
- - ✅ `sendMessage`
62
- - ✅ `status`
63
- - ✅ `stop`
64
- - ✅ `reload`
65
- - ✅ `data` (for data stream)
66
-
67
- ### New architecture:
68
-
69
- AI SDK v5 uses **transport-based design**. Input management is now the developer's responsibility.
70
-
71
- ## Migration Pattern
72
-
73
- **Before (v4):**
74
-
75
- ```typescript
76
- const { input, setInput, handleSubmit } = useChat()
77
-
78
- <form onSubmit={handleSubmit}>
79
- <input value={input} onChange={handleInputChange} />
80
- </form>
81
- ```
82
-
83
- **After (v5):**
84
-
85
- ```typescript
86
- const [input, setInput] = useState('')
87
- const { sendMessage } = useChat()
88
-
89
- const handleSubmit = (e: FormEvent) => {
90
- e.preventDefault()
91
- if (input.trim()) {
92
- sendMessage({ text: input })
93
- setInput('')
94
- }
95
- }
96
-
97
- <form onSubmit={handleSubmit}>
98
- <input
99
- value={input}
100
- onChange={(e) => setInput(e.target.value)}
101
- />
102
- </form>
103
- ```
104
-
105
- ## Testing Trap
106
-
107
- Integration tests using `useState` in a TestWrapper will pass even if production code is broken:
108
-
109
- ```typescript
110
- // Test (passes even with broken useChat usage)
111
- function TestWrapper() {
112
- const [input, setInput] = useState('') // Local state works
113
- return <MultimodalInput input={input} setInput={setInput} />
114
- }
115
-
116
- // Production (broken - input/setInput are undefined)
117
- function Chat() {
118
- const { input, setInput } = useChat() // undefined in v5!
119
- return <MultimodalInput input={input} setInput={setInput} />
120
- }
121
- ```
122
-
123
- **Solution:** Tests should verify the actual integration pattern, not just component behavior. Or add E2E tests that exercise the full Chat → useChat → MultimodalInput chain.
124
-
125
- ## Symptoms
126
-
127
- If you see these symptoms, check if you're destructuring removed properties:
128
-
129
- 1. **Button stays disabled when typing** - `!undefined` = `true`
130
- 2. **Console error: "X is not a function"** - `setInput` doesn't exist
131
- 3. **Input state is undefined** - Check console: `input: undefined`
132
- 4. **Tests pass but browser fails** - TestWrapper uses local state, production uses broken useChat
133
-
134
- ## Debug Strategy
135
-
136
- 1. Add logging to see what useChat returns:
137
-
138
- ```typescript
139
- const chatHelpers = useChat({ ... })
140
- console.log('useChat returned:', Object.keys(chatHelpers))
141
- ```
142
-
143
- 2. Check if input/setInput exist:
144
-
145
- ```typescript
146
- console.log('input:', input, 'setInput:', typeof setInput);
147
- // v5: input: undefined setInput: undefined
148
- ```
149
-
150
- 3. Verify AI SDK version:
151
-
152
- ```bash
153
- grep '"ai":' package.json
154
- # "ai": "5.0.87" = v5 (breaking changes)
155
- ```
156
-
157
- ## References
158
-
159
- - AI SDK v5 docs: https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat
160
- - Migration guide: https://ai-sdk.dev/docs/ai-sdk-ui/migration-guide
161
- - Breaking change announcement: "The useChat API has been significantly updated in AI SDK 5.0"
162
-
163
- ## Real-World Example
164
-
165
- **Project:** Chat application with send button
166
- **Bug:** Button stayed disabled when typing
167
- **Root cause:** Destructuring `input` and `setInput` from useChat (both undefined in v5)
168
- **Fix:** Add `const [input, setInput] = useState('')` in Chat component
169
- **Commit:** See project history for detailed investigation
170
-
171
- ## Prevention
172
-
173
- **When upgrading to AI SDK v5:**
174
-
175
- 1. Search codebase for `useChat` usage: `grep -r "useChat" --include="*.tsx"`
176
- 2. Check each usage for destructuring `input`, `setInput`, `handleSubmit`, `handleInputChange`
177
- 3. Replace with local state management
178
- 4. Test in actual browser (integration tests may give false confidence)
@@ -1,231 +0,0 @@
1
- # E2E Test Zombie Processes
2
-
3
- **Principle:** E2E tests that spawn dev servers can create zombie processes if run in parallel. Always enforce sequential execution and provide clear cleanup instructions.
4
-
5
- ## The Gotcha
6
-
7
- When E2E test frameworks (Playwright, Cypress) start dev servers via `webServer` config, running multiple test commands in parallel creates competing server instances that fight for the same port. This leads to:
8
-
9
- - Port conflicts (EADDRINUSE)
10
- - Orphaned processes that never terminate
11
- - Tests hanging indefinitely during retry phase
12
- - Accumulating zombie processes across multiple test runs
13
-
14
- ❌ **Bad:** Running multiple test commands simultaneously
15
-
16
- ```bash
17
- # Terminal 1
18
- pnpm test &
19
-
20
- # Terminal 2 (before Terminal 1 completes)
21
- pnpm test &
22
-
23
- # Result: 2 webServers competing for port 3000
24
- # Both hang, neither completes, processes accumulate
25
- ```
26
-
27
- ✅ **Good:** Enforce sequential execution
28
-
29
- ```bash
30
- # Terminal 1
31
- pnpm test
32
-
33
- # Wait for completion...
34
-
35
- # Terminal 2 (only after Terminal 1 finishes)
36
- pnpm test
37
- ```
38
-
39
- **Why it matters:**
40
-
41
- - Users don't realize background processes are still running
42
- - Each retry attempt doubles the problem (retry #1 spawns another webServer)
43
- - Can accumulate 8+ zombie processes across a debugging session
44
- - Port conflicts make ALL subsequent tests fail
45
-
46
- ## Prevention
47
-
48
- ### 1. Document Sequential Execution Requirement
49
-
50
- In your testing docs (e.g., `tests/SAFEWORD.md`), add prominent warning:
51
-
52
- ```markdown
53
- ### Zombie processes / Port already in use
54
-
55
- **NEVER run multiple `pnpm test` commands simultaneously**
56
-
57
- - Only run ONE test command at a time
58
- - Wait for previous test run to complete before starting another
59
-
60
- Why: Each test run spawns its own webServer. Multiple parallel runs
61
- fight for same port, creating zombie processes.
62
- ```
63
-
64
- ### 2. Configure playwright.config.ts for Safety
65
-
66
- ```typescript
67
- export default defineConfig({
68
- // In CI: workers=1 (sequential)
69
- // Locally: workers=undefined (allows parallelism within single run)
70
- workers: process.env.CI ? 1 : undefined,
71
-
72
- webServer: {
73
- command: 'npm run dev',
74
- url: 'http://localhost:3000',
75
- timeout: 120000,
76
- reuseExistingServer: !process.env.CI, // Reuse locally, fresh in CI
77
- stdout: 'pipe', // Capture output for debugging hangs
78
- stderr: 'pipe', // Capture errors
79
- },
80
- });
81
- ```
82
-
83
- **Key settings:**
84
-
85
- - `reuseExistingServer: !process.env.CI` - Reuse server locally (faster), fresh in CI (reliable)
86
- - `stdout: 'pipe'` / `stderr: 'pipe'` - Capture output to debug when webServer hangs
87
- - `workers: 1` in CI - Forces sequential execution where parallelism is most dangerous
88
-
89
- ### 3. Provide Clear Cleanup Instructions
90
-
91
- ```bash
92
- # Kill all processes on test ports
93
- lsof -ti:3000 | xargs kill -9 2>/dev/null
94
- lsof -ti:3001 | xargs kill -9 2>/dev/null
95
-
96
- # Or kill by process name pattern
97
- pkill -9 -f "playwright.*test-name"
98
- pkill -9 -f "next dev"
99
-
100
- # Wait before starting tests again
101
- sleep 5
102
- ```
103
-
104
- ## Debugging Zombie Processes
105
-
106
- ### Symptoms
107
-
108
- - Error: `Port 3000 is in use`
109
- - Error: `Timed out waiting 120000ms from config.webServer`
110
- - Tests hang during retry phase
111
- - `ps aux | grep test` shows multiple test processes
112
-
113
- ### Root Cause Investigation
114
-
115
- ```bash
116
- # Check for background test processes
117
- ps aux | grep -E "(playwright|next dev|pnpm test)"
118
-
119
- # Check what's using the port
120
- lsof -i:3000
121
-
122
- # Check for orphaned Node processes
123
- pgrep -fl node
124
- ```
125
-
126
- ### Fix
127
-
128
- 1. **Kill all test-related processes**
129
-
130
- ```bash
131
- pkill -9 -f "pnpm test"
132
- pkill -9 -f "next dev"
133
- pkill -9 -f "playwright"
134
- ```
135
-
136
- 2. **Verify ports are clear**
137
-
138
- ```bash
139
- lsof -i:3000 # Should return nothing
140
- ```
141
-
142
- 3. **Wait before restarting**
143
-
144
- ```bash
145
- sleep 5 # Give processes time to fully terminate
146
- ```
147
-
148
- 4. **Review process management**
149
- - Check if any terminals have background jobs (`jobs` command)
150
- - Check if any tests are running in other terminal windows
151
- - Restart terminal session if processes won't die
152
-
153
- ## Testing Trap
154
-
155
- **Tests pass locally but fail in CI:**
156
-
157
- - **Cause:** Local machine has `reuseExistingServer: true`, so dev server stays running across test runs
158
- - **In CI:** Fresh server every time, exposes timing/startup issues
159
- - **Fix:** Occasionally test locally with `reuseExistingServer: false` to catch CI-only failures
160
-
161
- **Tests hang during retry phase:**
162
-
163
- - **Cause:** Retry spawns another webServer while first is still running
164
- - **Amplification:** Each retry doubles the zombie processes (1 → 2 → 4 → 8)
165
- - **Fix:**
166
- - Reduce retries: `retries: process.env.CI ? 2 : 1`
167
- - Fix flaky tests so retries aren't needed
168
- - Add debugging output to understand why tests are failing
169
-
170
- ## Examples
171
-
172
- ### ✅ Good: Sequential Execution in Documentation
173
-
174
- ````markdown
175
- ## Running Tests
176
-
177
- ```bash
178
- # Run all tests (ONE command at a time)
179
- pnpm test
180
-
181
- # Run specific test file
182
- pnpm test -- path/to/file.test.ts
183
-
184
- # IMPORTANT: Wait for test run to complete before starting another
185
- ```
186
- ````
187
-
188
- ````
189
-
190
- ### ✅ Good: Clear Warning in CI/CD Setup
191
-
192
- ```yaml
193
- # .github/workflows/test.yml
194
- - name: Run E2E tests
195
- run: pnpm test
196
- # NOTE: Do not run multiple test commands in parallel
197
- # Each spawns a webServer competing for ports
198
- ````
199
-
200
- ### ❌ Bad: No Warning About Parallel Execution
201
-
202
- ````markdown
203
- ## Running Tests
204
-
205
- ```bash
206
- pnpm test # Run all tests
207
- ```
208
- ````
209
-
210
- ```
211
-
212
- **Problem:** Developers don't know that running `pnpm test` in multiple terminals simultaneously will cause zombie processes.
213
-
214
- ## Key Principles
215
-
216
- 1. **Educate developers** - Most don't realize background processes are still running
217
- 2. **Enforce sequential execution** - Document it prominently in testing guides
218
- 3. **Add debugging visibility** - Use `stdout: 'pipe'` to capture webServer output
219
- 4. **Provide cleanup scripts** - Make it easy to kill zombie processes when they happen
220
- 5. **Test locally like CI** - Occasionally disable `reuseExistingServer` to catch CI-only issues
221
-
222
- ## Related Patterns
223
-
224
- - **Test retry amplification** - Each retry doubles zombie processes
225
- - **Port conflict cascades** - One zombie process blocks all future test runs
226
- - **Silent background jobs** - Users don't notice processes still running after terminal close
227
-
228
- ## Reference
229
-
230
- Discovered during demo mode E2E testing implementation (2025-11-01). See project-specific learning for demo mode testing patterns.
231
- ```
@@ -1,96 +0,0 @@
1
- # Milkdown Crepe .editor Property is Official Public API
2
-
3
- **Principle:** `crepe.editor` is the **official way** to access the underlying Milkdown Editor and register custom plugins - it's not a hack.
4
-
5
- ## The Gotcha
6
-
7
- When extending Crepe with custom ProseMirror plugins, you might think accessing `.editor` is bypassing the public API:
8
-
9
- ❌ **Assumption:** "We're accessing an internal property"
10
- ✅ **Reality:** `.editor` is explicitly declared in TypeScript definitions as a public getter
11
-
12
- **Why it matters:** This is a **correct, supported pattern** for extending Crepe. No need to replace Crepe with core Milkdown just to avoid this "hack."
13
-
14
- ## Evidence
15
-
16
- From `@milkdown/crepe` TypeScript definitions (`node_modules/@milkdown/crepe/lib/types/core/builder.d.ts:18`):
17
-
18
- ```typescript
19
- export declare class CrepeBuilder {
20
- #private;
21
- constructor({ root, defaultValue }?: CrepeBuilderConfig);
22
- addFeature: { /* ... */ };
23
- create: () => Promise<Editor>;
24
- destroy: () => Promise<Editor>;
25
- get editor(): Editor; // ← PUBLIC API GETTER
26
- get readonly(): boolean;
27
- setReadonly: (value: boolean) => this;
28
- getMarkdown: () => string;
29
- on: (fn: (api: ListenerManager) => void) => this;
30
- }
31
-
32
- export declare class Crepe extends CrepeBuilder {
33
- // Inherits all properties from CrepeBuilder, including .editor getter
34
- }
35
- ```
36
-
37
- ## Correct Usage Pattern
38
-
39
- ```typescript
40
- import { Crepe } from '@milkdown/crepe';
41
- import { trackChangesPlugin } from './plugins/trackChangesPlugin';
42
-
43
- const crepe = new Crepe({ root, defaultValue: '', features: {} });
44
-
45
- // ✅ CORRECT - Official public API
46
- crepe.editor.use(trackChangesPlugin);
47
-
48
- return crepe.editor;
49
- ```
50
-
51
- ## When to Use This Pattern
52
-
53
- Use `crepe.editor.use()` when you need to:
54
-
55
- - Register custom ProseMirror plugins that Crepe doesn't provide
56
- - Access the underlying Milkdown Editor instance for direct API calls
57
- - Configure plugins that aren't available through Crepe's feature system
58
-
59
- ## Alternative (Not Recommended)
60
-
61
- You could replace Crepe with core Milkdown:
62
-
63
- ```typescript
64
- // ❌ NOT RECOMMENDED - Loses Crepe's pre-built features
65
- import { Editor } from '@milkdown/core';
66
-
67
- return Editor.make().use(commonmark).use(listener).use(history).use(trackChangesPlugin);
68
- ```
69
-
70
- **Why not recommended:**
71
-
72
- - Lose Crepe's toolbar, slash commands, block editing UI
73
- - 2-3 weeks to rebuild features
74
- - More code to maintain
75
- - Violates "avoid bloat" principle
76
- - Zero user value
77
-
78
- ## Research Sources
79
-
80
- - **GitHub Discussion:** [How to add extra plugins to the crepe editor? #1432](https://github.com/orgs/Milkdown/discussions/1432)
81
- - Maintainer confirmed: "Register the plugin as usual" after fixing rollup bug in v7.5.2
82
- - **Web Search:** Multiple examples show `crepe.editor.use()` pattern in production code
83
- - **TypeScript Definitions:** Explicit public getter declaration
84
-
85
- ## Conclusion
86
-
87
- When you see `crepe.editor.use(plugin)` in code reviews, **don't flag it as a hack**. It's the correct, documented way to extend Crepe with custom functionality.
88
-
89
- **Decision:** Keep the current implementation. No refactoring needed.
90
-
91
- ---
92
-
93
- **Related Files:**
94
-
95
- - `src/components/Editor.tsx` - Uses this pattern to register trackChangesPlugin
96
- - `planning/design/track-changes-refactoring-plan.md` - Initially flagged as hack, now corrected
@@ -1,119 +0,0 @@
1
- # ProseMirror Fragment Traversal
2
-
3
- **Principle:** `Fragment.forEach()` only iterates direct children, not descendants. Use recursive traversal to process nested text nodes.
4
-
5
- ## The Gotcha
6
-
7
- When applying marks to typed text in ProseMirror, `Fragment.forEach()` doesn't reach text nodes nested inside block nodes (paragraphs, headings, etc.).
8
-
9
- ❌ **Bad:** Iterating fragments without recursion
10
-
11
- ```typescript
12
- // This ONLY sees paragraph nodes, not text inside them
13
- const markedNodes: Node[] = [];
14
- fragment.forEach((node: Node) => {
15
- if (node.isText) {
16
- markedNodes.push(node.mark([...node.marks, insertionMark]));
17
- }
18
- // Paragraph node with text children → mark NOT applied
19
- });
20
- ```
21
-
22
- ✅ **Good:** Recursive traversal
23
-
24
- ```typescript
25
- function markFragmentRecursive(fragment: Fragment, mark: Mark): Fragment {
26
- const markedNodes: Node[] = [];
27
-
28
- fragment.forEach((node: Node) => {
29
- if (node.isText) {
30
- // Text node - add mark
31
- markedNodes.push(node.mark([...node.marks, mark]));
32
- } else if (node.content.size > 0) {
33
- // Block node with children - recursively mark children
34
- const markedContent = markFragmentRecursive(node.content, mark);
35
- markedNodes.push(node.copy(markedContent));
36
- } else {
37
- // Leaf node without content - keep as-is
38
- markedNodes.push(node);
39
- }
40
- });
41
-
42
- return Fragment.from(markedNodes);
43
- }
44
- ```
45
-
46
- **Why it matters:** User types "hello" → ProseMirror wraps it in paragraph → `Fragment → Paragraph → Text("hello")`. Without recursion, `forEach()` only sees the paragraph node, and marks are applied to the wrong level.
47
-
48
- ## Document Structure
49
-
50
- ProseMirror documents are tree structures:
51
-
52
- ```
53
- Fragment (from ReplaceStep.slice.content)
54
- ├─ Paragraph node
55
- │ └─ Text("hello") ← forEach() DOESN'T reach this
56
- └─ Heading node
57
- └─ Text("world") ← forEach() DOESN'T reach this
58
- ```
59
-
60
- ## Testing Trap
61
-
62
- Tests might pass if you manually create flat fragments in test fixtures:
63
-
64
- ```typescript
65
- // ✅ Test passes (flat structure)
66
- const testFragment = Fragment.from(schema.text('hello'));
67
- // Fragment → Text("hello") ← forEach() DOES reach this
68
-
69
- // ❌ Real app breaks (nested structure)
70
- // User types "hello" → editor wraps in paragraph
71
- // Fragment → Paragraph → Text("hello") ← forEach() DOESN'T reach this
72
- ```
73
-
74
- **Solution:** Test with realistic document structures (paragraphs, headings) that match actual user input, not artificially flat fragments.
75
-
76
- ## When to Use Recursive Traversal
77
-
78
- Use recursive traversal when:
79
-
80
- - Processing text content across entire document (search, replace, mark application)
81
- - Transforming node structure at any depth (format conversion, sanitization)
82
- - Analyzing document content (word count, spell check)
83
-
84
- Use simple `forEach()` when:
85
-
86
- - Only processing top-level nodes (block-level operations)
87
- - Explicitly working with known flat structures
88
-
89
- ## Reference Pattern
90
-
91
- ```typescript
92
- // Recursive helper for deep traversal
93
- function processFragmentRecursive(fragment: Fragment, operation: (node: Node) => Node): Fragment {
94
- const processedNodes: Node[] = [];
95
-
96
- fragment.forEach((node: Node) => {
97
- if (node.isText) {
98
- processedNodes.push(operation(node));
99
- } else if (node.content.size > 0) {
100
- const processedContent = processFragmentRecursive(node.content, operation);
101
- processedNodes.push(node.copy(processedContent));
102
- } else {
103
- processedNodes.push(node);
104
- }
105
- });
106
-
107
- return Fragment.from(processedNodes);
108
- }
109
- ```
110
-
111
- ## Additional Resources
112
-
113
- - [ProseMirror Node docs](https://prosemirror.net/docs/ref/#model.Node) - `node.copy()` for immutable updates
114
- - [Fragment API](https://prosemirror.net/docs/ref/#model.Fragment) - `forEach()` behavior
115
- - Track Changes implementation example: `packages/web/src/plugins/trackChangesPlugin.ts:17-45`
116
-
117
- ## Summary
118
-
119
- **Fragment.forEach() is shallow.** Always use recursive traversal when processing text content, as ProseMirror wraps text in block nodes (paragraphs, headings). Test with realistic nested structures, not flat fragments.
@@ -1 +0,0 @@
1
- **⚠️ ALWAYS READ FIRST: @./.safeword/SAFEWORD.md**