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,256 +0,0 @@
1
- # ESLint Plugin Suite
2
-
3
- **Parent:** [013-cli-self-contained-templates.md](./013-cli-self-contained-templates.md)
4
- **Date:** 2025-11-29
5
-
6
- ---
7
-
8
- Safeword sets up a comprehensive linting configuration. The plugin list varies by detected project type.
9
-
10
- ## Core Plugins (Always Installed)
11
-
12
- | Plugin | Purpose | npm Package |
13
- |--------|---------|-------------|
14
- | SonarJS | Code smells, complexity, bugs | `eslint-plugin-sonarjs` |
15
- | Microsoft SDL | Security Development Lifecycle (includes eslint-plugin-security rules) | `@microsoft/eslint-plugin-sdl` |
16
-
17
- **Note:** We use `@microsoft/eslint-plugin-sdl` instead of `eslint-plugin-security` because [SDL is a superset](https://github.com/microsoft/eslint-plugin-sdl) that includes security rules plus additional framework-specific rules (React, Angular, TypeScript). Using both would be redundant.
18
-
19
- ## Architecture Enforcement (Auto-Detected)
20
-
21
- | Plugin | npm Package | Mode |
22
- |--------|-------------|------|
23
- | Boundaries | `eslint-plugin-boundaries` | Auto-detect dirs, generate config, warn severity |
24
-
25
- Safeword auto-generates boundaries config when it detects 3+ architecture directories:
26
-
27
- **Detection logic:**
28
- ```typescript
29
- const architectureDirs = ['app', 'components', 'lib', 'utils', 'hooks', 'services', 'types', 'features', 'modules'];
30
-
31
- // Check both root and src/ prefix
32
- const found = architectureDirs.filter(d =>
33
- existsSync(join(projectDir, d)) || existsSync(join(projectDir, 'src', d))
34
- );
35
-
36
- if (found.length >= 3) {
37
- generateBoundariesConfig(projectDir, found);
38
- }
39
- ```
40
-
41
- **Generated hierarchy** (from most to least restrictive):
42
- ```
43
- types → can be imported by: everything
44
- utils → can import: types
45
- lib → can import: utils, types
46
- hooks → can import: lib, utils, types
47
- services → can import: lib, utils, types
48
- components → can import: hooks, services, lib, utils, types
49
- features → can import: components, hooks, services, lib, utils, types
50
- modules → can import: components, hooks, services, lib, utils, types
51
- app → can import: everything
52
- ```
53
-
54
- **Key design decisions:**
55
- - Uses `warn` severity (not `error`) - informative, not blocking
56
- - Config placed in `.safeword/eslint-boundaries.config.js` (safeword owns)
57
- - User can override in their `eslint.config.js` (takes precedence)
58
- - Setup prints: "Detected architecture boundaries - review `.safeword/eslint-boundaries.config.js`"
59
-
60
- **Generated config example:**
61
- ```javascript
62
- // .safeword/eslint-boundaries.config.js (AUTO-GENERATED)
63
- import boundaries from 'eslint-plugin-boundaries';
64
-
65
- // Detected directories: components, lib, utils, types (in src/)
66
- export default {
67
- plugins: { boundaries },
68
- settings: {
69
- 'boundaries/elements': [
70
- { type: 'components', pattern: 'src/components/**' },
71
- { type: 'lib', pattern: 'src/lib/**' },
72
- { type: 'utils', pattern: 'src/utils/**' },
73
- { type: 'types', pattern: 'src/types/**' },
74
- ],
75
- },
76
- rules: {
77
- 'boundaries/element-types': ['warn', {
78
- default: 'disallow',
79
- rules: [
80
- { from: 'components', allow: ['lib', 'utils', 'types'] },
81
- { from: 'lib', allow: ['utils', 'types'] },
82
- { from: 'utils', allow: ['types'] },
83
- // types can be imported by anything (no restriction)
84
- ],
85
- }],
86
- },
87
- };
88
- ```
89
-
90
- **User override** (in their `eslint.config.js`):
91
- ```javascript
92
- export default defineConfig([
93
- { extends: [safeword] },
94
-
95
- // Override boundaries with custom architecture
96
- {
97
- settings: {
98
- 'boundaries/elements': [
99
- // Your custom architecture
100
- ],
101
- },
102
- rules: {
103
- 'boundaries/element-types': ['error', { /* stricter rules */ }],
104
- },
105
- },
106
- ]);
107
- ```
108
-
109
- ## Framework-Specific Plugins (Auto-Detected)
110
-
111
- | Framework | Plugin | npm Package | Detection |
112
- |-----------|--------|-------------|-----------|
113
- | React | React rules | `eslint-plugin-react` | `react` in dependencies |
114
- | React | Hooks rules | `eslint-plugin-react-hooks` | `react` in dependencies |
115
- | Next.js | Next.js rules | `@next/eslint-plugin-next` | `next` in dependencies |
116
- | Astro | Astro rules | `eslint-plugin-astro` | `astro` in dependencies |
117
- | Electron | Electron config | `@electron-toolkit/eslint-config` | `electron` in dependencies |
118
-
119
- ## Testing Plugins (Auto-Detected)
120
-
121
- | Framework | Plugin | npm Package | Detection |
122
- |-----------|--------|-------------|-----------|
123
- | Vitest | Vitest rules | `@vitest/eslint-plugin` | `vitest` in devDependencies |
124
- | Playwright | Playwright rules | `eslint-plugin-playwright` | `@playwright/test` in devDependencies |
125
-
126
- **Note:** Testing plugins use `files` patterns to scope rules to test files only:
127
- - Vitest: `['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}', '**/tests/**']`
128
- - Playwright: `['**/e2e/**', '**/*.e2e.{ts,tsx}', '**/playwright/**']`
129
-
130
- Patterns are detected from `playwright.config.ts` if present, otherwise defaults are used.
131
-
132
- ## Database Plugins (Auto-Detected)
133
-
134
- | ORM/Database | Plugin | npm Package | Detection |
135
- |--------------|--------|-------------|-----------|
136
- | Drizzle ORM | Drizzle rules | `eslint-plugin-drizzle` | `drizzle-orm` in dependencies |
137
-
138
- **Available but not auto-installed** (too niche for most projects):
139
- - `eslint-plugin-sql` - SQL linting in template literals (for raw SQL usage)
140
- - `eslint-plugin-sql-template` - Enforces `sql` tag to prevent injection
141
-
142
- ## Container Linting (Separate Tool)
143
-
144
- Dockerfile linting is handled by [Hadolint](https://github.com/hadolint/hadolint), not ESLint:
145
-
146
- ```bash
147
- # Installation
148
- brew install hadolint # macOS
149
- scoop install hadolint # Windows
150
- docker pull hadolint/hadolint # Docker
151
-
152
- # Usage
153
- hadolint Dockerfile
154
- ```
155
-
156
- **Safeword integration:** The `safeword setup` command adds Hadolint to pre-commit hooks if Dockerfile is detected in project root. Requires Hadolint to be installed on the system.
157
-
158
- ## Plugin Detection Logic
159
-
160
- ```typescript
161
- function detectPlugins(projectDir: string): string[] {
162
- const pkg = readPackageJson(projectDir);
163
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
164
- const devDeps = pkg.devDependencies || {};
165
- const plugins: string[] = [];
166
-
167
- // Core (always)
168
- plugins.push('eslint-plugin-sonarjs');
169
- plugins.push('@microsoft/eslint-plugin-sdl'); // Superset of eslint-plugin-security
170
-
171
- // Framework detection
172
- if (deps['react']) {
173
- plugins.push('eslint-plugin-react');
174
- plugins.push('eslint-plugin-react-hooks');
175
- }
176
- if (deps['next']) {
177
- plugins.push('@next/eslint-plugin-next');
178
- }
179
- if (deps['astro']) {
180
- plugins.push('eslint-plugin-astro');
181
- }
182
- if (deps['electron']) {
183
- plugins.push('@electron-toolkit/eslint-config');
184
- }
185
-
186
- // Testing detection
187
- if (devDeps['vitest']) {
188
- plugins.push('@vitest/eslint-plugin');
189
- }
190
- if (devDeps['@playwright/test']) {
191
- plugins.push('eslint-plugin-playwright');
192
- }
193
-
194
- // Database detection (only Drizzle - raw SQL plugins are niche)
195
- if (deps['drizzle-orm']) {
196
- plugins.push('eslint-plugin-drizzle');
197
- }
198
-
199
- // Architecture boundaries (auto-detected if 3+ dirs found)
200
- if (detectArchitectureFolders(projectDir)) {
201
- plugins.push('eslint-plugin-boundaries');
202
- }
203
-
204
- return plugins;
205
- }
206
-
207
- function detectArchitectureFolders(projectDir: string): boolean {
208
- const architectureDirs = ['app', 'components', 'lib', 'utils', 'hooks', 'services', 'types', 'features', 'modules'];
209
-
210
- const found = architectureDirs.filter(d =>
211
- existsSync(join(projectDir, d)) || existsSync(join(projectDir, 'src', d))
212
- );
213
- return found.length >= 3;
214
- }
215
- ```
216
-
217
- ## ESLint Config Generation
218
-
219
- The generated `.safeword/eslint.config.js` dynamically includes plugins based on detection:
220
-
221
- ```javascript
222
- // .safeword/eslint.config.js (SAFEWORD OWNS)
223
- // ⚠️ AUTO-GENERATED BY SAFEWORD - DO NOT EDIT
224
- // Customize in your root eslint.config.js instead
225
-
226
- import js from '@eslint/js';
227
- import tseslint from 'typescript-eslint';
228
- import sonarjs from 'eslint-plugin-sonarjs';
229
- import sdl from '@microsoft/eslint-plugin-sdl';
230
- // Framework imports added based on detection (react, next, vitest, etc.)
231
- // Boundaries import added if 3+ architecture dirs detected
232
-
233
- export default [
234
- // Base (always included)
235
- js.configs.recommended,
236
- ...tseslint.configs.recommended,
237
- sonarjs.configs.recommended,
238
- ...sdl.configs.recommended,
239
-
240
- // Framework configs (conditionally included based on package.json)
241
- // ...react configs if react detected
242
- // ...next configs if next detected
243
- // ...vitest configs if vitest detected
244
- // ...playwright configs if @playwright/test detected
245
-
246
- // Boundaries (conditionally included if 3+ architecture dirs found)
247
- // boundariesConfig,
248
-
249
- // Global ignores
250
- { ignores: ['node_modules/', 'dist/', '.next/', 'build/'] },
251
- ];
252
- ```
253
-
254
- **Implementation note:** The CLI generates this file dynamically based on detected dependencies and directory structure. Only detected plugins are imported and included.
255
-
256
- **Note:** Vite doesn't need ESLint rules - use `vite-plugin-eslint2` for dev server integration.
@@ -1,385 +0,0 @@
1
- # Implementation Snippets
2
-
3
- **Parent:** [013-cli-self-contained-templates.md](./013-cli-self-contained-templates.md)
4
- **Date:** 2025-11-29
5
-
6
- Reference code snippets for implementing the CLI. Copy/adapt as needed.
7
-
8
- ---
9
-
10
- ## Template Reader (src/utils/templates.ts)
11
-
12
- ```typescript
13
- import { readFileSync, cpSync } from 'node:fs';
14
- import { join, dirname } from 'node:path';
15
- import { fileURLToPath } from 'node:url';
16
-
17
- const __dirname = dirname(fileURLToPath(import.meta.url));
18
- const TEMPLATES_DIR = join(__dirname, '..', 'templates');
19
-
20
- export function getTemplate(relativePath: string): string {
21
- return readFileSync(join(TEMPLATES_DIR, relativePath), 'utf-8');
22
- }
23
-
24
- export function copyTemplateDir(src: string, dest: string): void {
25
- cpSync(join(TEMPLATES_DIR, src), dest, { recursive: true });
26
- }
27
- ```
28
-
29
- ---
30
-
31
- ## Setup/Upgrade Usage
32
-
33
- ```typescript
34
- import { getTemplate, copyTemplateDir } from '../utils/templates.js';
35
-
36
- // Copy templates to .safeword/
37
- writeFile(join(safewordDir, 'SAFEWORD.md'), getTemplate('safeword/SAFEWORD.md'));
38
- copyTemplateDir('safeword/guides', join(safewordDir, 'guides'));
39
- copyTemplateDir('safeword/doc-templates', join(safewordDir, 'doc-templates'));
40
- copyTemplateDir('safeword/prompts', join(safewordDir, 'prompts'));
41
- copyTemplateDir('safeword/hooks', join(safewordDir, 'hooks'));
42
- copyTemplateDir('safeword/lib', join(safewordDir, 'lib'));
43
- copyTemplateDir('safeword/git', join(safewordDir, 'git'));
44
-
45
- // Copy templates to .claude/
46
- copyTemplateDir('claude/commands', join(claudeDir, 'commands'));
47
- copyTemplateDir('claude/skills', join(claudeDir, 'skills'));
48
-
49
- // Create empty planning directories
50
- ensureDir(join(safewordDir, 'planning', 'user-stories'));
51
- ensureDir(join(safewordDir, 'planning', 'design'));
52
- ensureDir(join(safewordDir, 'tickets', 'completed'));
53
- ensureDir(join(safewordDir, 'learnings'));
54
- ```
55
-
56
- ---
57
-
58
- ## Merge settings.json
59
-
60
- ```typescript
61
- import { SETTINGS_HOOKS } from '../config/hooks.js';
62
-
63
- function mergeSettingsJson(claudeDir: string): void {
64
- const settingsPath = join(claudeDir, 'settings.json');
65
- let existing = existsSync(settingsPath)
66
- ? JSON.parse(readFileSync(settingsPath, 'utf-8'))
67
- : {};
68
-
69
- const merged = {
70
- ...existing,
71
- hooks: mergeHooks(existing.hooks || {}, SETTINGS_HOOKS),
72
- };
73
- writeFileSync(settingsPath, JSON.stringify(merged, null, 2));
74
- }
75
-
76
- function mergeHooks(existing: object, safeword: object): object {
77
- const result = { ...existing };
78
- for (const [event, hooks] of Object.entries(safeword)) {
79
- result[event] = [...(result[event] || []), ...hooks];
80
- }
81
- return result;
82
- }
83
- ```
84
-
85
- ---
86
-
87
- ## Linting Setup (Preserve Existing)
88
-
89
- ```typescript
90
- function setupLinting(projectDir: string): void {
91
- const eslintPath = join(projectDir, 'eslint.config.js');
92
- const prettierPath = join(projectDir, '.prettierrc');
93
-
94
- if (!existsSync(eslintPath) && !existsSync(join(projectDir, '.eslintrc.js'))) {
95
- writeFileSync(eslintPath, getEslintConfig(detectProjectType(projectDir)));
96
- }
97
-
98
- if (!existsSync(prettierPath) && !existsSync(join(projectDir, '.prettierrc.js'))) {
99
- writeFileSync(prettierPath, JSON.stringify({ semi: true, singleQuote: true }, null, 2));
100
- }
101
-
102
- addLintScriptsIfMissing(projectDir);
103
- }
104
- ```
105
-
106
- ---
107
-
108
- ## Error Handling
109
-
110
- ```typescript
111
- // src/utils/errors.ts
112
- export class SafewordError extends Error {
113
- constructor(message: string, public fatal: boolean = true) {
114
- super(message);
115
- this.name = 'SafewordError';
116
- }
117
- }
118
-
119
- export function handleError(error: SafewordError, context: string): void {
120
- if (error.fatal) {
121
- console.error(`❌ Fatal: ${error.message}`);
122
- process.exit(1);
123
- } else {
124
- console.warn(`⚠️ Warning: ${error.message} (continuing...)`);
125
- }
126
- }
127
- ```
128
-
129
- ---
130
-
131
- ## Rollback
132
-
133
- ```typescript
134
- // src/utils/rollback.ts
135
- export async function withRollback<T>(
136
- operation: () => Promise<T>,
137
- backup: () => Promise<void>,
138
- restore: () => Promise<void>
139
- ): Promise<T> {
140
- await backup();
141
- try {
142
- return await operation();
143
- } catch (error) {
144
- console.error('❌ Operation failed, rolling back...');
145
- await restore();
146
- throw error;
147
- }
148
- }
149
-
150
- // Usage
151
- await withRollback(
152
- () => installSafeword(options),
153
- () => backupExisting('.safeword', '.claude'),
154
- () => restoreBackup('.safeword', '.claude')
155
- );
156
- ```
157
-
158
- Backup locations: `.safeword.backup/`, `.claude.backup/` (deleted on success)
159
-
160
- ---
161
-
162
- ## ESLint Plugin Installation
163
-
164
- ```typescript
165
- // src/commands/init.ts
166
- async function installEslintPlugins(detected: DetectedStack): Promise<void> {
167
- const plugins: string[] = ['@eslint/js'];
168
-
169
- if (detected.typescript) plugins.push('typescript-eslint');
170
- if (detected.react) plugins.push('eslint-plugin-react', 'eslint-plugin-react-hooks');
171
- if (detected.security) plugins.push('@microsoft/eslint-plugin-sdl');
172
- if (detected.playwright) plugins.push('eslint-plugin-playwright');
173
- if (detected.vitest) plugins.push('@vitest/eslint-plugin');
174
- if (detected.boundaries) plugins.push('eslint-plugin-boundaries');
175
-
176
- const pm = detectPackageManager();
177
- const installCmd = {
178
- npm: `npm install -D ${plugins.join(' ')}`,
179
- yarn: `yarn add -D ${plugins.join(' ')}`,
180
- pnpm: `pnpm add -D ${plugins.join(' ')}`,
181
- bun: `bun add -D ${plugins.join(' ')}`,
182
- }[pm];
183
-
184
- console.log(`📦 Installing ESLint plugins: ${plugins.join(', ')}`);
185
- execSync(installCmd, { stdio: 'inherit' });
186
- }
187
- ```
188
-
189
- ---
190
-
191
- ## MCP Config Setup
192
-
193
- ```typescript
194
- // src/commands/init.ts
195
- async function setupMcpConfigs(): Promise<void> {
196
- const mcpPath = '.mcp.json';
197
- const samplePath = '.mcp.json.sample';
198
-
199
- let mcpConfig: { mcpServers: Record<string, unknown> } = { mcpServers: {} };
200
- try {
201
- const existing = await fs.readFile(mcpPath, 'utf-8');
202
- mcpConfig = JSON.parse(existing);
203
- } catch { /* file doesn't exist */ }
204
-
205
- // Auto-activate free servers (merge, don't overwrite)
206
- mcpConfig.mcpServers['context7'] ??= {
207
- command: 'npx',
208
- args: ['-y', '@upstash/context7-mcp@latest'],
209
- };
210
- mcpConfig.mcpServers['playwright'] ??= {
211
- command: 'npx',
212
- args: ['@playwright/mcp@latest'],
213
- };
214
-
215
- await fs.writeFile(mcpPath, JSON.stringify(mcpConfig, null, 2));
216
-
217
- // Sample for API-key servers
218
- const sampleExists = await fs.access(samplePath).then(() => true).catch(() => false);
219
- if (!sampleExists) {
220
- const sample = {
221
- mcpServers: {
222
- arcade: {
223
- type: 'http',
224
- url: 'https://mcp.arcade.dev',
225
- headers: { Authorization: 'Bearer YOUR_ARCADE_API_KEY' },
226
- },
227
- },
228
- };
229
- await fs.writeFile(samplePath, JSON.stringify(sample, null, 2));
230
- }
231
-
232
- console.log('📡 MCP: Context7 ✓, Playwright ✓ (see .mcp.json.sample for Arcade)');
233
- }
234
- ```
235
-
236
- ---
237
-
238
- ## MCP Cleanup (Reset)
239
-
240
- ```typescript
241
- // src/commands/reset.ts
242
- async function cleanupMcpConfig(): Promise<void> {
243
- const mcpPath = '.mcp.json';
244
- try {
245
- const content = await fs.readFile(mcpPath, 'utf-8');
246
- const config = JSON.parse(content);
247
-
248
- delete config.mcpServers?.['context7'];
249
- delete config.mcpServers?.['playwright'];
250
-
251
- if (Object.keys(config.mcpServers || {}).length === 0) {
252
- await fs.unlink(mcpPath);
253
- } else {
254
- await fs.writeFile(mcpPath, JSON.stringify(config, null, 2));
255
- }
256
- } catch { /* file doesn't exist */ }
257
-
258
- await fs.unlink('.mcp.json.sample').catch(() => {});
259
- }
260
- ```
261
-
262
- ---
263
-
264
- ## Hook Output (hookSpecificOutput)
265
-
266
- ```bash
267
- # .safeword/hooks/session-verify-agents.sh
268
- #!/bin/bash
269
- if [[ -f "AGENTS.md" ]]; then
270
- jq -n '{"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "AGENTS.md verified"}}'
271
- else
272
- jq -n '{"hookSpecificOutput": {"hookEventName": "SessionStart", "blockReason": "AGENTS.md not found - run safeword init"}}'
273
- exit 2
274
- fi
275
- ```
276
-
277
- ```bash
278
- # .safeword/hooks/prompt-timestamp.sh
279
- #!/bin/bash
280
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
281
- jq -n --arg ts "$TIMESTAMP" \
282
- '{"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "additionalContext": ("Timestamp: " + $ts)}}'
283
- ```
284
-
285
- ```bash
286
- # .safeword/hooks/post-tool-lint.sh
287
- #!/bin/bash
288
- LINT_OUTPUT=$(npm run lint 2>&1 || true)
289
- if echo "$LINT_OUTPUT" | grep -q "error"; then
290
- jq -n '{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Lint errors found - please fix"}}'
291
- else
292
- jq -n '{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Lint passed"}}'
293
- fi
294
- ```
295
-
296
- ---
297
-
298
- ## Slash Command Templates
299
-
300
- ```typescript
301
- // src/config/commands.ts
302
- export const COMMANDS = {
303
- 'arch-review.md': `---
304
- description: Run architecture review on current changes
305
- allowed-tools: ["Read", "Glob", "Grep", "WebFetch", "WebSearch"]
306
- ---
307
-
308
- Review the architecture of the current changes against the project's architecture guide.
309
- Focus on: layering violations, dependency direction, and interface contracts.
310
- `,
311
- 'quality-review.md': `---
312
- description: Deep code quality review with web research
313
- allowed-tools: ["Read", "Glob", "Grep", "WebFetch", "WebSearch"]
314
- ---
315
-
316
- Perform a comprehensive quality review of the current changes.
317
- Check for security issues, performance concerns, and best practice violations.
318
- `,
319
- };
320
- ```
321
-
322
- ---
323
-
324
- ## Testing Examples
325
-
326
- ### Unit Tests
327
-
328
- ```typescript
329
- // tests/unit/detect.test.ts
330
- describe('detectStack', () => {
331
- it('detects TypeScript from tsconfig.json', async () => {
332
- const result = await detectStack('/mock/project');
333
- expect(result.typescript).toBe(true);
334
- });
335
-
336
- it('detects boundaries architecture (3+ dirs)', async () => {
337
- const result = await detectStack('/mock/layered-project');
338
- expect(result.boundaries).toBe(true);
339
- });
340
- });
341
- ```
342
-
343
- ### Integration Tests
344
-
345
- ```typescript
346
- // tests/integration/init.test.ts
347
- describe('safeword init', () => {
348
- const testDir = '/tmp/safeword-test-project';
349
-
350
- beforeEach(async () => {
351
- await fs.mkdir(testDir, { recursive: true });
352
- await fs.writeFile(path.join(testDir, 'package.json'), JSON.stringify({ name: 'test', type: 'module' }));
353
- });
354
-
355
- afterEach(async () => {
356
- await fs.rm(testDir, { recursive: true, force: true });
357
- });
358
-
359
- it('creates .safeword directory', async () => {
360
- execSync('npx safeword init', { cwd: testDir });
361
- const exists = await fs.access(path.join(testDir, '.safeword')).then(() => true).catch(() => false);
362
- expect(exists).toBe(true);
363
- });
364
- });
365
- ```
366
-
367
- ### LLM Evals (promptfoo)
368
-
369
- ```yaml
370
- # promptfoo.yaml
371
- providers:
372
- - id: anthropic:messages:claude-sonnet-4-20250514
373
- config:
374
- temperature: 0
375
-
376
- tests:
377
- - description: "init creates correct structure for React + TS project"
378
- vars:
379
- project_type: "React + TypeScript"
380
- assert:
381
- - type: contains
382
- value: ".safeword/"
383
- - type: contains
384
- value: "eslint-plugin-react"
385
- ```