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,139 +0,0 @@
1
- /**
2
- * Test Suite 7: Git Repository Handling
3
- *
4
- * Tests for git detection and hook installation.
5
- */
6
-
7
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
8
- import {
9
- createTempDir,
10
- removeTempDir,
11
- createTypeScriptPackageJson,
12
- runCli,
13
- readTestFile,
14
- writeTestFile,
15
- fileExists,
16
- initGitRepo,
17
- } from '../helpers';
18
-
19
- describe('Test Suite 7: Git Repository Handling', () => {
20
- let tempDir: string;
21
-
22
- beforeEach(() => {
23
- tempDir = createTempDir();
24
- });
25
-
26
- afterEach(() => {
27
- removeTempDir(tempDir);
28
- });
29
-
30
- describe('Test 7.1: Prompts for git init when no .git', () => {
31
- it('should mention git initialization in output', async () => {
32
- createTypeScriptPackageJson(tempDir);
33
- // No git init
34
-
35
- // In non-interactive mode with --yes, we expect it to skip
36
- // The prompt behavior is tested via TTY simulation
37
- const result = await runCli(['setup', '--yes'], { cwd: tempDir });
38
-
39
- // Output should mention git
40
- const output = result.stdout + result.stderr;
41
- expect(output.toLowerCase()).toMatch(/git/i);
42
- });
43
- });
44
-
45
- describe('Test 7.2: Runs git init when user confirms', () => {
46
- // This test would require TTY simulation for interactive input
47
- // For now, we test that git hooks work when .git exists
48
- it('should work with existing git repository', async () => {
49
- createTypeScriptPackageJson(tempDir);
50
- initGitRepo(tempDir);
51
-
52
- const result = await runCli(['setup', '--yes'], { cwd: tempDir });
53
-
54
- expect(result.exitCode).toBe(0);
55
- expect(fileExists(tempDir, '.git')).toBe(true);
56
- });
57
- });
58
-
59
- describe('Test 7.3: Skips git init when user declines', () => {
60
- // In --yes mode, git init is skipped by default
61
- it('should skip git init in non-interactive mode', async () => {
62
- createTypeScriptPackageJson(tempDir);
63
- // No git init
64
-
65
- const result = await runCli(['setup', '--yes'], { cwd: tempDir });
66
-
67
- expect(result.exitCode).toBe(0);
68
- // Git should NOT be initialized
69
- expect(fileExists(tempDir, '.git')).toBe(false);
70
-
71
- // Should warn about skipped git hooks
72
- const output = result.stdout + result.stderr;
73
- expect(output.toLowerCase()).toMatch(/skip|warning|git/i);
74
- });
75
- });
76
-
77
- describe('Test 7.4: Installs git hooks when .git present', () => {
78
- it('should create pre-commit hook with safeword markers', async () => {
79
- createTypeScriptPackageJson(tempDir);
80
- initGitRepo(tempDir);
81
-
82
- await runCli(['setup', '--yes'], { cwd: tempDir });
83
-
84
- expect(fileExists(tempDir, '.git/hooks/pre-commit')).toBe(true);
85
-
86
- const content = readTestFile(tempDir, '.git/hooks/pre-commit');
87
- expect(content).toContain('SAFEWORD_ARCH_CHECK_START');
88
- expect(content).toContain('SAFEWORD_ARCH_CHECK_END');
89
- });
90
- });
91
-
92
- describe('Test 7.5: Preserves existing pre-commit hooks', () => {
93
- it('should preserve custom hook content', async () => {
94
- createTypeScriptPackageJson(tempDir);
95
- initGitRepo(tempDir);
96
-
97
- // Create existing pre-commit hook
98
- const customHook = `#!/bin/bash
99
- # Custom hook for running tests
100
- echo "Running custom tests..."
101
- npm test
102
- `;
103
- writeTestFile(tempDir, '.git/hooks/pre-commit', customHook);
104
-
105
- await runCli(['setup', '--yes'], { cwd: tempDir });
106
-
107
- const content = readTestFile(tempDir, '.git/hooks/pre-commit');
108
-
109
- // Original content preserved
110
- expect(content).toContain('Running custom tests');
111
- expect(content).toContain('npm test');
112
-
113
- // Safeword markers added
114
- expect(content).toContain('SAFEWORD_ARCH_CHECK_START');
115
- expect(content).toContain('SAFEWORD_ARCH_CHECK_END');
116
- });
117
-
118
- it('should not duplicate markers on repeated setup', async () => {
119
- createTypeScriptPackageJson(tempDir);
120
- initGitRepo(tempDir);
121
-
122
- // Run setup twice
123
- await runCli(['setup', '--yes'], { cwd: tempDir });
124
-
125
- // For second run, we need to remove .safeword to allow setup
126
- // Or use upgrade. Let's test via upgrade path
127
- await runCli(['upgrade'], { cwd: tempDir });
128
-
129
- const content = readTestFile(tempDir, '.git/hooks/pre-commit');
130
-
131
- // Markers should appear exactly once
132
- const startCount = (content.match(/SAFEWORD_ARCH_CHECK_START/g) || []).length;
133
- const endCount = (content.match(/SAFEWORD_ARCH_CHECK_END/g) || []).length;
134
-
135
- expect(startCount).toBe(1);
136
- expect(endCount).toBe(1);
137
- });
138
- });
139
- });
@@ -1,334 +0,0 @@
1
- /**
2
- * Test Suite 3: Setup - Hooks and Skills
3
- *
4
- * Tests for Claude Code hook registration and skill copying.
5
- */
6
-
7
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
8
- import { chmodSync, readdirSync } from 'node:fs';
9
- import { join } from 'node:path';
10
- import {
11
- createTempDir,
12
- removeTempDir,
13
- createTypeScriptPackageJson,
14
- runCli,
15
- readTestFile,
16
- writeTestFile,
17
- fileExists,
18
- initGitRepo,
19
- } from '../helpers';
20
-
21
- describe('Test Suite 3: Setup - Hooks and Skills', () => {
22
- let tempDir: string;
23
-
24
- beforeEach(() => {
25
- tempDir = createTempDir();
26
- });
27
-
28
- afterEach(() => {
29
- removeTempDir(tempDir);
30
- });
31
-
32
- describe('Test 3.1: Registers hooks in settings.json', () => {
33
- it('should create .claude/settings.json with hooks', async () => {
34
- createTypeScriptPackageJson(tempDir);
35
- initGitRepo(tempDir);
36
-
37
- await runCli(['setup', '--yes'], { cwd: tempDir });
38
-
39
- expect(fileExists(tempDir, '.claude/settings.json')).toBe(true);
40
-
41
- const settings = JSON.parse(readTestFile(tempDir, '.claude/settings.json'));
42
-
43
- expect(settings.hooks).toBeDefined();
44
- expect(settings.hooks.SessionStart).toBeDefined();
45
- expect(Array.isArray(settings.hooks.SessionStart)).toBe(true);
46
- expect(settings.hooks.UserPromptSubmit).toBeDefined();
47
- expect(Array.isArray(settings.hooks.UserPromptSubmit)).toBe(true);
48
- expect(settings.hooks.PostToolUse).toBeDefined();
49
- expect(Array.isArray(settings.hooks.PostToolUse)).toBe(true);
50
- });
51
-
52
- it('should reference .safeword/hooks paths', async () => {
53
- createTypeScriptPackageJson(tempDir);
54
- initGitRepo(tempDir);
55
-
56
- await runCli(['setup', '--yes'], { cwd: tempDir });
57
-
58
- const settings = JSON.parse(readTestFile(tempDir, '.claude/settings.json'));
59
- const settingsStr = JSON.stringify(settings);
60
-
61
- // At least one hook should reference .safeword/hooks
62
- expect(settingsStr).toContain('.safeword/hooks');
63
- });
64
- });
65
-
66
- describe('Test 3.2: Copies skills to .claude/skills', () => {
67
- it('should create skills directory with safeword skills', async () => {
68
- createTypeScriptPackageJson(tempDir);
69
- initGitRepo(tempDir);
70
-
71
- await runCli(['setup', '--yes'], { cwd: tempDir });
72
-
73
- expect(fileExists(tempDir, '.claude/skills')).toBe(true);
74
-
75
- // Check for safeword skill directory
76
- // Should match pattern safeword-* (e.g., safeword-quality-reviewer)
77
- expect(fileExists(tempDir, '.claude/skills/safeword-quality-reviewer')).toBe(true);
78
-
79
- // The skill should contain a SKILL.md file
80
- expect(fileExists(tempDir, '.claude/skills/safeword-quality-reviewer/SKILL.md')).toBe(true);
81
-
82
- const skillContent = readTestFile(
83
- tempDir,
84
- '.claude/skills/safeword-quality-reviewer/SKILL.md',
85
- );
86
- expect(skillContent).toContain('Quality Reviewer');
87
- });
88
- });
89
-
90
- describe('Test 3.3: Preserves existing hooks', () => {
91
- it('should append to existing hooks without replacing', async () => {
92
- createTypeScriptPackageJson(tempDir);
93
- initGitRepo(tempDir);
94
-
95
- // Create existing settings with custom hook
96
- const existingSettings = {
97
- hooks: {
98
- SessionStart: [
99
- {
100
- command: 'echo "My custom hook"',
101
- description: 'Custom SessionStart hook',
102
- },
103
- ],
104
- },
105
- };
106
- writeTestFile(tempDir, '.claude/settings.json', JSON.stringify(existingSettings, null, 2));
107
-
108
- await runCli(['setup', '--yes'], { cwd: tempDir });
109
-
110
- const settings = JSON.parse(readTestFile(tempDir, '.claude/settings.json'));
111
-
112
- // Custom hook should still be present
113
- const hasCustomHook = settings.hooks.SessionStart.some(
114
- (hook: { command?: string }) => hook.command === 'echo "My custom hook"',
115
- );
116
- expect(hasCustomHook).toBe(true);
117
-
118
- // Safeword hooks should be added
119
- expect(settings.hooks.SessionStart.length).toBeGreaterThan(1);
120
- });
121
- });
122
-
123
- describe('Test 3.4: Includes SessionStart hook for AGENTS.md', () => {
124
- it('should include AGENTS.md verification hook', async () => {
125
- createTypeScriptPackageJson(tempDir);
126
- initGitRepo(tempDir);
127
-
128
- await runCli(['setup', '--yes'], { cwd: tempDir });
129
-
130
- const settings = JSON.parse(readTestFile(tempDir, '.claude/settings.json'));
131
-
132
- // Find hook that references AGENTS.md check
133
- const agentsHook = settings.hooks.SessionStart.find(
134
- (hook: { command?: string }) =>
135
- hook.command && (hook.command.includes('AGENTS') || hook.command.includes('agents')),
136
- );
137
-
138
- expect(agentsHook).toBeDefined();
139
-
140
- // Hook script should exist
141
- if (agentsHook?.command) {
142
- // Extract script path from command if it's a bash script
143
- const scriptMatch = agentsHook.command.match(/bash\s+([^\s]+)/);
144
- if (scriptMatch) {
145
- expect(fileExists(tempDir, scriptMatch[1])).toBe(true);
146
- }
147
- }
148
- });
149
- });
150
-
151
- describe('Test 3.6: Includes UserPromptSubmit hook for timestamp injection', () => {
152
- it('should include timestamp injection hook', async () => {
153
- createTypeScriptPackageJson(tempDir);
154
- initGitRepo(tempDir);
155
-
156
- await runCli(['setup', '--yes'], { cwd: tempDir });
157
-
158
- const settings = JSON.parse(readTestFile(tempDir, '.claude/settings.json'));
159
-
160
- // Find hook that references timestamp injection
161
- const timestampHook = settings.hooks.UserPromptSubmit.find(
162
- (hook: { command?: string }) =>
163
- hook.command && hook.command.includes('inject-timestamp'),
164
- );
165
-
166
- expect(timestampHook).toBeDefined();
167
-
168
- // Hook script should exist
169
- expect(fileExists(tempDir, '.safeword/hooks/inject-timestamp.sh')).toBe(true);
170
-
171
- // Script should contain timestamp output
172
- const scriptContent = readTestFile(tempDir, '.safeword/hooks/inject-timestamp.sh');
173
- expect(scriptContent).toContain('date');
174
- });
175
- });
176
-
177
- describe('Test 3.5: Exit 1 if hook registration fails', () => {
178
- it('should fail with exit 1 when settings.json is not writable', async () => {
179
- createTypeScriptPackageJson(tempDir);
180
- initGitRepo(tempDir);
181
-
182
- // Create .claude directory with read-only settings.json
183
- writeTestFile(tempDir, '.claude/settings.json', '{}');
184
- chmodSync(join(tempDir, '.claude/settings.json'), 0o444);
185
-
186
- const result = await runCli(['setup', '--yes'], { cwd: tempDir });
187
-
188
- // Restore permissions for cleanup
189
- chmodSync(join(tempDir, '.claude/settings.json'), 0o644);
190
-
191
- expect(result.exitCode).toBe(1);
192
- expect(result.stderr.toLowerCase()).toMatch(/hook|permission|write|failed/i);
193
- });
194
- });
195
-
196
- describe('Test 3.7: Installs lib scripts', () => {
197
- it('should install shared library scripts to .safeword/lib/', async () => {
198
- createTypeScriptPackageJson(tempDir);
199
- initGitRepo(tempDir);
200
-
201
- await runCli(['setup', '--yes'], { cwd: tempDir });
202
-
203
- // Lib directory should exist with shell scripts
204
- expect(fileExists(tempDir, '.safeword/lib')).toBe(true);
205
-
206
- const libDir = join(tempDir, '.safeword/lib');
207
- const shFiles = readdirSync(libDir).filter((f) => f.endsWith('.sh'));
208
- expect(shFiles.length).toBeGreaterThan(0);
209
- });
210
- });
211
-
212
- describe('Test 3.8: Sets up MCP servers', () => {
213
- it('should create .mcp.json with context7 and playwright', async () => {
214
- createTypeScriptPackageJson(tempDir);
215
- initGitRepo(tempDir);
216
-
217
- await runCli(['setup', '--yes'], { cwd: tempDir });
218
-
219
- expect(fileExists(tempDir, '.mcp.json')).toBe(true);
220
-
221
- const mcpConfig = JSON.parse(readTestFile(tempDir, '.mcp.json'));
222
- expect(mcpConfig.mcpServers).toBeDefined();
223
- expect(mcpConfig.mcpServers.context7).toBeDefined();
224
- expect(mcpConfig.mcpServers.playwright).toBeDefined();
225
- });
226
- });
227
-
228
- describe('Test 3.9: Preserves existing MCP servers', () => {
229
- it('should preserve user MCP servers when adding safeword servers', async () => {
230
- createTypeScriptPackageJson(tempDir);
231
- initGitRepo(tempDir);
232
-
233
- // Create existing .mcp.json with custom server
234
- const existingMcp = {
235
- mcpServers: {
236
- 'my-custom-server': {
237
- command: 'my-server',
238
- args: ['--port', '3000'],
239
- },
240
- },
241
- };
242
- writeTestFile(tempDir, '.mcp.json', JSON.stringify(existingMcp, null, 2));
243
-
244
- await runCli(['setup', '--yes'], { cwd: tempDir });
245
-
246
- const mcpConfig = JSON.parse(readTestFile(tempDir, '.mcp.json'));
247
-
248
- // Custom server should be preserved
249
- expect(mcpConfig.mcpServers['my-custom-server']).toBeDefined();
250
- expect(mcpConfig.mcpServers['my-custom-server'].command).toBe('my-server');
251
-
252
- // Safeword servers should be added
253
- expect(mcpConfig.mcpServers.context7).toBeDefined();
254
- expect(mcpConfig.mcpServers.playwright).toBeDefined();
255
- });
256
- });
257
-
258
- describe('Test 3.10: Installs slash commands', () => {
259
- it('should install slash commands to .claude/commands/', async () => {
260
- createTypeScriptPackageJson(tempDir);
261
- initGitRepo(tempDir);
262
-
263
- await runCli(['setup', '--yes'], { cwd: tempDir });
264
-
265
- // Commands directory should exist
266
- expect(fileExists(tempDir, '.claude/commands')).toBe(true);
267
-
268
- // Should contain markdown command files
269
- const commandsDir = join(tempDir, '.claude/commands');
270
- const mdFiles = readdirSync(commandsDir).filter((f) => f.endsWith('.md'));
271
- expect(mdFiles.length).toBeGreaterThan(0);
272
- });
273
- });
274
-
275
- describe('Test 3.11: MCP servers work out of the box', () => {
276
- it('should configure context7 with correct npx command', async () => {
277
- createTypeScriptPackageJson(tempDir);
278
- initGitRepo(tempDir);
279
-
280
- await runCli(['setup', '--yes'], { cwd: tempDir });
281
-
282
- expect(fileExists(tempDir, '.mcp.json')).toBe(true);
283
-
284
- const mcpConfig = JSON.parse(readTestFile(tempDir, '.mcp.json'));
285
-
286
- // Context7 should use npx with the correct package
287
- expect(mcpConfig.mcpServers.context7).toBeDefined();
288
- expect(mcpConfig.mcpServers.context7.command).toBe('npx');
289
- expect(mcpConfig.mcpServers.context7.args).toContain('@upstash/context7-mcp@latest');
290
- });
291
-
292
- it('should configure playwright with correct npx command', async () => {
293
- createTypeScriptPackageJson(tempDir);
294
- initGitRepo(tempDir);
295
-
296
- await runCli(['setup', '--yes'], { cwd: tempDir });
297
-
298
- const mcpConfig = JSON.parse(readTestFile(tempDir, '.mcp.json'));
299
-
300
- // Playwright should use npx with the correct package
301
- expect(mcpConfig.mcpServers.playwright).toBeDefined();
302
- expect(mcpConfig.mcpServers.playwright.command).toBe('npx');
303
- expect(mcpConfig.mcpServers.playwright.args).toEqual(
304
- expect.arrayContaining([expect.stringContaining('@playwright/mcp')]),
305
- );
306
- });
307
-
308
- it('should have MCP packages that can be resolved by npx', async () => {
309
- // Verify the packages exist on npm (can be resolved)
310
- // This is a lightweight check - just verify npm can find the packages
311
- const { execSync } = await import('node:child_process');
312
-
313
- // Check context7 package exists
314
- try {
315
- execSync('npm view @upstash/context7-mcp version', {
316
- encoding: 'utf-8',
317
- timeout: 10000,
318
- });
319
- } catch {
320
- expect.fail('@upstash/context7-mcp package not found on npm');
321
- }
322
-
323
- // Check playwright package exists
324
- try {
325
- execSync('npm view @playwright/mcp version', {
326
- encoding: 'utf-8',
327
- timeout: 10000,
328
- });
329
- } catch {
330
- expect.fail('@playwright/mcp package not found on npm');
331
- }
332
- });
333
- });
334
- });
@@ -1,189 +0,0 @@
1
- /**
2
- * Test Suite 4: Setup - Linting (Integration Tests)
3
- *
4
- * Tests for ESLint + Prettier configuration.
5
- *
6
- * Note: Unit tests for project type detection (Tests 4.1-4.3) are in
7
- * src/utils/project-detector.test.ts. This file contains only the
8
- * integration tests (Tests 4.4-4.8).
9
- */
10
-
11
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
12
- import { chmodSync } from 'node:fs';
13
- import { join } from 'node:path';
14
- import {
15
- createTempDir,
16
- removeTempDir,
17
- createTypeScriptPackageJson,
18
- runCli,
19
- readTestFile,
20
- fileExists,
21
- initGitRepo,
22
- } from '../helpers';
23
-
24
- describe('Test Suite 4: Setup - Linting (Integration)', () => {
25
- let tempDir: string;
26
-
27
- beforeEach(() => {
28
- tempDir = createTempDir();
29
- });
30
-
31
- afterEach(() => {
32
- removeTempDir(tempDir);
33
- });
34
-
35
- describe('Test 4.4: Creates eslint.config.mjs', () => {
36
- it('should create ESLint flat config', async () => {
37
- createTypeScriptPackageJson(tempDir);
38
- initGitRepo(tempDir);
39
-
40
- await runCli(['setup', '--yes'], { cwd: tempDir });
41
-
42
- expect(fileExists(tempDir, 'eslint.config.mjs')).toBe(true);
43
-
44
- const content = readTestFile(tempDir, 'eslint.config.mjs');
45
- // Should be a valid ESLint flat config
46
- expect(content).toContain('export');
47
- });
48
-
49
- it('should include TypeScript config when detected', async () => {
50
- createTypeScriptPackageJson(tempDir);
51
- initGitRepo(tempDir);
52
-
53
- await runCli(['setup', '--yes'], { cwd: tempDir });
54
-
55
- const content = readTestFile(tempDir, 'eslint.config.mjs');
56
- expect(content).toMatch(/typescript|@typescript-eslint/i);
57
- });
58
- });
59
-
60
- describe('Test 4.5: Creates .prettierrc', () => {
61
- it('should create Prettier config', async () => {
62
- createTypeScriptPackageJson(tempDir);
63
- initGitRepo(tempDir);
64
-
65
- await runCli(['setup', '--yes'], { cwd: tempDir });
66
-
67
- expect(fileExists(tempDir, '.prettierrc')).toBe(true);
68
-
69
- const content = readTestFile(tempDir, '.prettierrc');
70
- // Should be valid JSON
71
- expect(() => JSON.parse(content)).not.toThrow();
72
- });
73
- });
74
-
75
- describe('Test 4.6: Adds lint script to package.json', () => {
76
- it('should add lint script', async () => {
77
- createTypeScriptPackageJson(tempDir);
78
- initGitRepo(tempDir);
79
-
80
- await runCli(['setup', '--yes'], { cwd: tempDir });
81
-
82
- const packageJson = JSON.parse(readTestFile(tempDir, 'package.json'));
83
- expect(packageJson.scripts?.lint).toBe('eslint .');
84
- });
85
-
86
- it('should not overwrite existing lint script', async () => {
87
- createTypeScriptPackageJson(tempDir, {
88
- scripts: {
89
- lint: 'eslint src/',
90
- },
91
- });
92
- initGitRepo(tempDir);
93
-
94
- await runCli(['setup', '--yes'], { cwd: tempDir });
95
-
96
- const packageJson = JSON.parse(readTestFile(tempDir, 'package.json'));
97
- // Original script should be preserved
98
- expect(packageJson.scripts?.lint).toBe('eslint src/');
99
- });
100
- });
101
-
102
- describe('Test 4.7: Adds format script to package.json', () => {
103
- it('should add format script', async () => {
104
- createTypeScriptPackageJson(tempDir);
105
- initGitRepo(tempDir);
106
-
107
- await runCli(['setup', '--yes'], { cwd: tempDir });
108
-
109
- const packageJson = JSON.parse(readTestFile(tempDir, 'package.json'));
110
- expect(packageJson.scripts?.format).toBe('prettier --write .');
111
- });
112
-
113
- it('should not overwrite existing format script', async () => {
114
- createTypeScriptPackageJson(tempDir, {
115
- scripts: {
116
- format: 'prettier --write src/',
117
- },
118
- });
119
- initGitRepo(tempDir);
120
-
121
- await runCli(['setup', '--yes'], { cwd: tempDir });
122
-
123
- const packageJson = JSON.parse(readTestFile(tempDir, 'package.json'));
124
- // Original script should be preserved
125
- expect(packageJson.scripts?.format).toBe('prettier --write src/');
126
- });
127
- });
128
-
129
- describe('Test 4.8: Exit 1 if linting setup fails', () => {
130
- it('should fail with exit 1 when package.json is not writable', async () => {
131
- createTypeScriptPackageJson(tempDir);
132
- initGitRepo(tempDir);
133
-
134
- // Make package.json read-only
135
- chmodSync(join(tempDir, 'package.json'), 0o444);
136
-
137
- const result = await runCli(['setup', '--yes'], { cwd: tempDir });
138
-
139
- // Restore permissions for cleanup
140
- chmodSync(join(tempDir, 'package.json'), 0o644);
141
-
142
- expect(result.exitCode).toBe(1);
143
- expect(result.stderr.toLowerCase()).toMatch(/lint|permission|write|failed|package/i);
144
- });
145
- });
146
-
147
- describe('Test 4.9: Creates markdownlint config', () => {
148
- it('should create .markdownlint.jsonc', async () => {
149
- createTypeScriptPackageJson(tempDir);
150
- initGitRepo(tempDir);
151
-
152
- await runCli(['setup', '--yes'], { cwd: tempDir });
153
-
154
- // Implementation may place in .safeword/ or root
155
- const hasMarkdownlintConfig =
156
- fileExists(tempDir, '.markdownlint.jsonc') ||
157
- fileExists(tempDir, '.safeword/.markdownlint.jsonc');
158
-
159
- expect(hasMarkdownlintConfig).toBe(true);
160
- });
161
- });
162
-
163
- describe('Test 4.10: Adds lint:md script', () => {
164
- it('should add lint:md script to package.json', async () => {
165
- createTypeScriptPackageJson(tempDir);
166
- initGitRepo(tempDir);
167
-
168
- await runCli(['setup', '--yes'], { cwd: tempDir });
169
-
170
- const packageJson = JSON.parse(readTestFile(tempDir, 'package.json'));
171
- expect(packageJson.scripts?.['lint:md']).toBeDefined();
172
- expect(packageJson.scripts?.['lint:md']).toContain('markdownlint');
173
- });
174
- });
175
-
176
- describe('Test 4.11: Adds format:check script', () => {
177
- it('should add format:check script to package.json', async () => {
178
- createTypeScriptPackageJson(tempDir);
179
- initGitRepo(tempDir);
180
-
181
- await runCli(['setup', '--yes'], { cwd: tempDir });
182
-
183
- const packageJson = JSON.parse(readTestFile(tempDir, 'package.json'));
184
- expect(packageJson.scripts?.['format:check']).toBeDefined();
185
- expect(packageJson.scripts?.['format:check']).toContain('prettier');
186
- expect(packageJson.scripts?.['format:check']).toContain('--check');
187
- });
188
- });
189
- });