safeword 0.2.2 → 0.2.4

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/.claude/commands/arch-review.md +32 -0
  2. package/.claude/commands/lint.md +6 -0
  3. package/.claude/commands/quality-review.md +13 -0
  4. package/.claude/commands/setup-linting.md +6 -0
  5. package/.claude/hooks/auto-lint.sh +6 -0
  6. package/.claude/hooks/auto-quality-review.sh +170 -0
  7. package/.claude/hooks/check-linting-sync.sh +17 -0
  8. package/.claude/hooks/inject-timestamp.sh +6 -0
  9. package/.claude/hooks/question-protocol.sh +12 -0
  10. package/.claude/hooks/run-linters.sh +8 -0
  11. package/.claude/hooks/run-quality-review.sh +76 -0
  12. package/.claude/hooks/version-check.sh +10 -0
  13. package/.claude/mcp/README.md +96 -0
  14. package/.claude/mcp/arcade.sample.json +9 -0
  15. package/.claude/mcp/context7.sample.json +7 -0
  16. package/.claude/mcp/playwright.sample.json +7 -0
  17. package/.claude/settings.json +62 -0
  18. package/.claude/skills/quality-reviewer/SKILL.md +190 -0
  19. package/.claude/skills/safeword-quality-reviewer/SKILL.md +13 -0
  20. package/.env.arcade.example +4 -0
  21. package/.env.example +11 -0
  22. package/.gitmodules +4 -0
  23. package/.safeword/SAFEWORD.md +33 -0
  24. package/.safeword/eslint/eslint-base.mjs +101 -0
  25. package/.safeword/guides/architecture-guide.md +404 -0
  26. package/.safeword/guides/code-philosophy.md +174 -0
  27. package/.safeword/guides/context-files-guide.md +405 -0
  28. package/.safeword/guides/data-architecture-guide.md +183 -0
  29. package/.safeword/guides/design-doc-guide.md +165 -0
  30. package/.safeword/guides/learning-extraction.md +515 -0
  31. package/.safeword/guides/llm-instruction-design.md +239 -0
  32. package/.safeword/guides/llm-prompting.md +95 -0
  33. package/.safeword/guides/tdd-best-practices.md +570 -0
  34. package/.safeword/guides/test-definitions-guide.md +243 -0
  35. package/.safeword/guides/testing-methodology.md +573 -0
  36. package/.safeword/guides/user-story-guide.md +237 -0
  37. package/.safeword/guides/zombie-process-cleanup.md +214 -0
  38. package/{templates → .safeword}/hooks/agents-md-check.sh +0 -0
  39. package/{templates → .safeword}/hooks/post-tool.sh +0 -0
  40. package/{templates → .safeword}/hooks/pre-commit.sh +0 -0
  41. package/.safeword/planning/002-user-story-quality-evaluation.md +1840 -0
  42. package/.safeword/planning/003-langsmith-eval-setup-prompt.md +363 -0
  43. package/.safeword/planning/004-llm-eval-test-cases.md +3226 -0
  44. package/.safeword/planning/005-architecture-enforcement-system.md +169 -0
  45. package/.safeword/planning/006-reactive-fix-prevention-research.md +135 -0
  46. package/.safeword/planning/011-cli-ux-vision.md +330 -0
  47. package/.safeword/planning/012-project-structure-cleanup.md +154 -0
  48. package/.safeword/planning/README.md +39 -0
  49. package/.safeword/planning/automation-plan-v2.md +1225 -0
  50. package/.safeword/planning/automation-plan-v3.md +1291 -0
  51. package/.safeword/planning/automation-plan.md +3058 -0
  52. package/.safeword/planning/design/005-cli-implementation.md +343 -0
  53. package/.safeword/planning/design/013-cli-self-contained-templates.md +596 -0
  54. package/.safeword/planning/design/013a-eslint-plugin-suite.md +256 -0
  55. package/.safeword/planning/design/013b-implementation-snippets.md +385 -0
  56. package/.safeword/planning/design/013c-config-isolation-strategy.md +242 -0
  57. package/.safeword/planning/design/code-philosophy-improvements.md +60 -0
  58. package/.safeword/planning/mcp-analysis.md +545 -0
  59. package/.safeword/planning/phase2-subagents-vs-skills-analysis.md +451 -0
  60. package/.safeword/planning/settings-improvements.md +970 -0
  61. package/.safeword/planning/test-definitions/005-cli-implementation.md +1301 -0
  62. package/.safeword/planning/test-definitions/cli-self-contained-templates.md +205 -0
  63. package/.safeword/planning/user-stories/001-guides-review-user-stories.md +1381 -0
  64. package/.safeword/planning/user-stories/003-reactive-fix-prevention.md +132 -0
  65. package/.safeword/planning/user-stories/004-technical-constraints.md +86 -0
  66. package/.safeword/planning/user-stories/005-cli-implementation.md +311 -0
  67. package/.safeword/planning/user-stories/cli-self-contained-templates.md +172 -0
  68. package/.safeword/planning/versioned-distribution.md +740 -0
  69. package/.safeword/prompts/arch-review.md +43 -0
  70. package/.safeword/prompts/quality-review.md +11 -0
  71. package/.safeword/scripts/arch-review.sh +235 -0
  72. package/.safeword/scripts/check-linting-sync.sh +58 -0
  73. package/.safeword/scripts/setup-linting.sh +559 -0
  74. package/.safeword/templates/architecture-template.md +136 -0
  75. package/.safeword/templates/ci/architecture-check.yml +79 -0
  76. package/.safeword/templates/design-doc-template.md +127 -0
  77. package/.safeword/templates/test-definitions-feature.md +100 -0
  78. package/.safeword/templates/ticket-template.md +74 -0
  79. package/.safeword/templates/user-stories-template.md +82 -0
  80. package/.safeword/tickets/001-guides-review-user-stories.md +83 -0
  81. package/.safeword/tickets/002-architecture-enforcement.md +211 -0
  82. package/.safeword/tickets/003-reactive-fix-prevention.md +57 -0
  83. package/.safeword/tickets/004-technical-constraints-in-user-stories.md +39 -0
  84. package/.safeword/tickets/005-cli-implementation.md +248 -0
  85. package/.safeword/tickets/006-flesh-out-skills.md +43 -0
  86. package/.safeword/tickets/007-flesh-out-questioning.md +44 -0
  87. package/.safeword/tickets/008-upgrade-questioning.md +58 -0
  88. package/.safeword/tickets/009-naming-conventions.md +41 -0
  89. package/.safeword/tickets/010-safeword-md-cleanup.md +34 -0
  90. package/.safeword/tickets/011-cursor-setup.md +86 -0
  91. package/.safeword/tickets/README.md +73 -0
  92. package/.safeword/version +1 -0
  93. package/AGENTS.md +59 -0
  94. package/CLAUDE.md +12 -0
  95. package/README.md +347 -0
  96. package/docs/001-cli-implementation-plan.md +856 -0
  97. package/docs/elite-dx-implementation-plan.md +1034 -0
  98. package/framework/README.md +131 -0
  99. package/framework/mcp/README.md +96 -0
  100. package/framework/mcp/arcade.sample.json +8 -0
  101. package/framework/mcp/context7.sample.json +6 -0
  102. package/framework/mcp/playwright.sample.json +6 -0
  103. package/framework/scripts/arch-review.sh +235 -0
  104. package/framework/scripts/check-linting-sync.sh +58 -0
  105. package/framework/scripts/load-env.sh +49 -0
  106. package/framework/scripts/setup-claude.sh +223 -0
  107. package/framework/scripts/setup-linting.sh +559 -0
  108. package/framework/scripts/setup-quality.sh +477 -0
  109. package/framework/scripts/setup-safeword.sh +550 -0
  110. package/framework/templates/ci/architecture-check.yml +78 -0
  111. package/learnings/ai-sdk-v5-breaking-changes.md +178 -0
  112. package/learnings/e2e-test-zombie-processes.md +231 -0
  113. package/learnings/milkdown-crepe-editor-property.md +96 -0
  114. package/learnings/prosemirror-fragment-traversal.md +119 -0
  115. package/package.json +19 -43
  116. package/packages/cli/AGENTS.md +1 -0
  117. package/packages/cli/ARCHITECTURE.md +279 -0
  118. package/packages/cli/package.json +51 -0
  119. package/packages/cli/src/cli.ts +63 -0
  120. package/packages/cli/src/commands/check.ts +166 -0
  121. package/packages/cli/src/commands/diff.ts +209 -0
  122. package/packages/cli/src/commands/reset.ts +190 -0
  123. package/packages/cli/src/commands/setup.ts +325 -0
  124. package/packages/cli/src/commands/upgrade.ts +163 -0
  125. package/packages/cli/src/index.ts +3 -0
  126. package/packages/cli/src/templates/config.ts +58 -0
  127. package/packages/cli/src/templates/content.ts +18 -0
  128. package/packages/cli/src/templates/index.ts +12 -0
  129. package/packages/cli/src/utils/agents-md.ts +66 -0
  130. package/packages/cli/src/utils/fs.ts +179 -0
  131. package/packages/cli/src/utils/git.ts +124 -0
  132. package/packages/cli/src/utils/hooks.ts +29 -0
  133. package/packages/cli/src/utils/output.ts +60 -0
  134. package/packages/cli/src/utils/project-detector.test.ts +185 -0
  135. package/packages/cli/src/utils/project-detector.ts +44 -0
  136. package/packages/cli/src/utils/version.ts +28 -0
  137. package/packages/cli/src/version.ts +6 -0
  138. package/packages/cli/templates/SAFEWORD.md +776 -0
  139. package/packages/cli/templates/doc-templates/architecture-template.md +136 -0
  140. package/packages/cli/templates/doc-templates/design-doc-template.md +134 -0
  141. package/packages/cli/templates/doc-templates/test-definitions-feature.md +131 -0
  142. package/packages/cli/templates/doc-templates/ticket-template.md +82 -0
  143. package/packages/cli/templates/doc-templates/user-stories-template.md +92 -0
  144. package/packages/cli/templates/guides/architecture-guide.md +423 -0
  145. package/packages/cli/templates/guides/code-philosophy.md +195 -0
  146. package/packages/cli/templates/guides/context-files-guide.md +457 -0
  147. package/packages/cli/templates/guides/data-architecture-guide.md +200 -0
  148. package/packages/cli/templates/guides/design-doc-guide.md +171 -0
  149. package/packages/cli/templates/guides/learning-extraction.md +552 -0
  150. package/packages/cli/templates/guides/llm-instruction-design.md +248 -0
  151. package/packages/cli/templates/guides/llm-prompting.md +102 -0
  152. package/packages/cli/templates/guides/tdd-best-practices.md +615 -0
  153. package/packages/cli/templates/guides/test-definitions-guide.md +334 -0
  154. package/packages/cli/templates/guides/testing-methodology.md +618 -0
  155. package/packages/cli/templates/guides/user-story-guide.md +256 -0
  156. package/packages/cli/templates/guides/zombie-process-cleanup.md +219 -0
  157. package/packages/cli/templates/hooks/agents-md-check.sh +27 -0
  158. package/packages/cli/templates/hooks/post-tool.sh +4 -0
  159. package/packages/cli/templates/hooks/pre-commit.sh +10 -0
  160. package/packages/cli/templates/prompts/arch-review.md +43 -0
  161. package/packages/cli/templates/prompts/quality-review.md +10 -0
  162. package/packages/cli/templates/skills/safeword-quality-reviewer/SKILL.md +207 -0
  163. package/packages/cli/tests/commands/check.test.ts +129 -0
  164. package/packages/cli/tests/commands/cli.test.ts +89 -0
  165. package/packages/cli/tests/commands/diff.test.ts +115 -0
  166. package/packages/cli/tests/commands/reset.test.ts +310 -0
  167. package/packages/cli/tests/commands/self-healing.test.ts +170 -0
  168. package/packages/cli/tests/commands/setup-blocking.test.ts +71 -0
  169. package/packages/cli/tests/commands/setup-core.test.ts +135 -0
  170. package/packages/cli/tests/commands/setup-git.test.ts +139 -0
  171. package/packages/cli/tests/commands/setup-hooks.test.ts +334 -0
  172. package/packages/cli/tests/commands/setup-linting.test.ts +189 -0
  173. package/packages/cli/tests/commands/setup-noninteractive.test.ts +80 -0
  174. package/packages/cli/tests/commands/setup-templates.test.ts +181 -0
  175. package/packages/cli/tests/commands/upgrade.test.ts +215 -0
  176. package/packages/cli/tests/helpers.ts +243 -0
  177. package/packages/cli/tests/npm-package.test.ts +83 -0
  178. package/packages/cli/tests/technical-constraints.test.ts +96 -0
  179. package/packages/cli/tsconfig.json +25 -0
  180. package/packages/cli/tsup.config.ts +11 -0
  181. package/packages/cli/vitest.config.ts +23 -0
  182. package/promptfoo.yaml +3270 -0
  183. package/dist/check-M73LGONJ.js +0 -129
  184. package/dist/check-M73LGONJ.js.map +0 -1
  185. package/dist/chunk-2XWIUEQK.js +0 -190
  186. package/dist/chunk-2XWIUEQK.js.map +0 -1
  187. package/dist/chunk-GZRQL3SX.js +0 -146
  188. package/dist/chunk-GZRQL3SX.js.map +0 -1
  189. package/dist/chunk-V5G6BGOK.js +0 -26
  190. package/dist/chunk-V5G6BGOK.js.map +0 -1
  191. package/dist/chunk-W66Z3C5H.js +0 -21
  192. package/dist/chunk-W66Z3C5H.js.map +0 -1
  193. package/dist/cli.d.ts +0 -1
  194. package/dist/cli.js +0 -34
  195. package/dist/cli.js.map +0 -1
  196. package/dist/diff-FSFDCBL5.js +0 -166
  197. package/dist/diff-FSFDCBL5.js.map +0 -1
  198. package/dist/index.d.ts +0 -11
  199. package/dist/index.js +0 -7
  200. package/dist/index.js.map +0 -1
  201. package/dist/reset-3ACTIYYE.js +0 -143
  202. package/dist/reset-3ACTIYYE.js.map +0 -1
  203. package/dist/setup-MKVVQTVA.js +0 -266
  204. package/dist/setup-MKVVQTVA.js.map +0 -1
  205. package/dist/upgrade-FQOL6AF5.js +0 -134
  206. package/dist/upgrade-FQOL6AF5.js.map +0 -1
  207. /package/{templates → framework}/SAFEWORD.md +0 -0
  208. /package/{templates → framework}/guides/architecture-guide.md +0 -0
  209. /package/{templates → framework}/guides/code-philosophy.md +0 -0
  210. /package/{templates → framework}/guides/context-files-guide.md +0 -0
  211. /package/{templates → framework}/guides/data-architecture-guide.md +0 -0
  212. /package/{templates → framework}/guides/design-doc-guide.md +0 -0
  213. /package/{templates → framework}/guides/learning-extraction.md +0 -0
  214. /package/{templates → framework}/guides/llm-instruction-design.md +0 -0
  215. /package/{templates → framework}/guides/llm-prompting.md +0 -0
  216. /package/{templates → framework}/guides/tdd-best-practices.md +0 -0
  217. /package/{templates → framework}/guides/test-definitions-guide.md +0 -0
  218. /package/{templates → framework}/guides/testing-methodology.md +0 -0
  219. /package/{templates → framework}/guides/user-story-guide.md +0 -0
  220. /package/{templates → framework}/guides/zombie-process-cleanup.md +0 -0
  221. /package/{templates → framework}/prompts/arch-review.md +0 -0
  222. /package/{templates → framework}/prompts/quality-review.md +0 -0
  223. /package/{templates/skills/safeword-quality-reviewer → framework/skills/quality-reviewer}/SKILL.md +0 -0
  224. /package/{templates/doc-templates → framework/templates}/architecture-template.md +0 -0
  225. /package/{templates/doc-templates → framework/templates}/design-doc-template.md +0 -0
  226. /package/{templates/doc-templates → framework/templates}/test-definitions-feature.md +0 -0
  227. /package/{templates/doc-templates → framework/templates}/ticket-template.md +0 -0
  228. /package/{templates/doc-templates → framework/templates}/user-stories-template.md +0 -0
  229. /package/{templates → packages/cli/templates}/commands/arch-review.md +0 -0
  230. /package/{templates → packages/cli/templates}/commands/lint.md +0 -0
  231. /package/{templates → packages/cli/templates}/commands/quality-review.md +0 -0
  232. /package/{templates → packages/cli/templates}/hooks/inject-timestamp.sh +0 -0
  233. /package/{templates → packages/cli/templates}/lib/common.sh +0 -0
  234. /package/{templates → packages/cli/templates}/lib/jq-fallback.sh +0 -0
  235. /package/{templates → packages/cli/templates}/markdownlint.jsonc +0 -0
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Diff command - Preview changes that would be made by upgrade
3
+ */
4
+
5
+ import { join } from 'node:path';
6
+ import { VERSION } from '../version.js';
7
+ import { exists, readFileSafe, getTemplatesDir } from '../utils/fs.js';
8
+ import { info, success, error, header, listItem } from '../utils/output.js';
9
+
10
+ export interface DiffOptions {
11
+ verbose?: boolean;
12
+ }
13
+
14
+ interface FileDiff {
15
+ path: string;
16
+ status: 'added' | 'modified' | 'unchanged';
17
+ currentContent?: string;
18
+ newContent: string;
19
+ }
20
+
21
+ /**
22
+ * Create a unified diff between two strings
23
+ */
24
+ function createUnifiedDiff(oldContent: string, newContent: string, filename: string): string {
25
+ const oldLines = oldContent.split('\n');
26
+ const newLines = newContent.split('\n');
27
+
28
+ const lines: string[] = [];
29
+ lines.push(`--- a/${filename}`);
30
+ lines.push(`+++ b/${filename}`);
31
+
32
+ // Simple diff - show all changes
33
+ // A real implementation would use a proper diff algorithm
34
+ let hasChanges = false;
35
+
36
+ const maxLines = Math.max(oldLines.length, newLines.length);
37
+
38
+ for (let i = 0; i < maxLines; i++) {
39
+ const oldLine = oldLines[i];
40
+ const newLine = newLines[i];
41
+
42
+ if (oldLine === newLine) {
43
+ lines.push(` ${oldLine ?? ''}`);
44
+ } else {
45
+ hasChanges = true;
46
+ if (oldLine !== undefined) {
47
+ lines.push(`-${oldLine}`);
48
+ }
49
+ if (newLine !== undefined) {
50
+ lines.push(`+${newLine}`);
51
+ }
52
+ }
53
+ }
54
+
55
+ if (!hasChanges) {
56
+ return '';
57
+ }
58
+
59
+ // Add context marker
60
+ lines.splice(2, 0, `@@ -1,${oldLines.length} +1,${newLines.length} @@`);
61
+
62
+ return lines.join('\n');
63
+ }
64
+
65
+ /**
66
+ * Get all files that would be changed by upgrade
67
+ */
68
+ function getFileDiffs(cwd: string): FileDiff[] {
69
+ const templatesDir = getTemplatesDir();
70
+ const diffs: FileDiff[] = [];
71
+
72
+ // Define files to check (template source -> install destination)
73
+ const files: Array<{ templatePath: string; installPath: string }> = [
74
+ { templatePath: 'SAFEWORD.md', installPath: '.safeword/SAFEWORD.md' },
75
+ { templatePath: 'hooks/agents-md-check.sh', installPath: '.safeword/hooks/agents-md-check.sh' },
76
+ { templatePath: 'hooks/pre-commit.sh', installPath: '.safeword/hooks/pre-commit.sh' },
77
+ { templatePath: 'hooks/post-tool.sh', installPath: '.safeword/hooks/post-tool.sh' },
78
+ { templatePath: 'hooks/inject-timestamp.sh', installPath: '.safeword/hooks/inject-timestamp.sh' },
79
+ { templatePath: 'skills/safeword-quality-reviewer/SKILL.md', installPath: '.claude/skills/safeword-quality-reviewer/SKILL.md' },
80
+ ];
81
+
82
+ // Add version file (not from templates)
83
+ const versionPath = join(cwd, '.safeword/version');
84
+ const currentVersion = readFileSafe(versionPath);
85
+ if (currentVersion === null) {
86
+ diffs.push({ path: '.safeword/version', status: 'added', newContent: VERSION });
87
+ } else if (currentVersion.trim() !== VERSION) {
88
+ diffs.push({ path: '.safeword/version', status: 'modified', currentContent: currentVersion, newContent: VERSION });
89
+ } else {
90
+ diffs.push({ path: '.safeword/version', status: 'unchanged', currentContent: currentVersion, newContent: VERSION });
91
+ }
92
+
93
+ for (const file of files) {
94
+ const templateFullPath = join(templatesDir, file.templatePath);
95
+ const installFullPath = join(cwd, file.installPath);
96
+
97
+ const newContent = readFileSafe(templateFullPath);
98
+ if (newContent === null) continue; // Skip if template doesn't exist
99
+
100
+ const currentContent = readFileSafe(installFullPath);
101
+
102
+ if (currentContent === null) {
103
+ diffs.push({
104
+ path: file.installPath,
105
+ status: 'added',
106
+ newContent,
107
+ });
108
+ } else if (currentContent.trim() !== newContent.trim()) {
109
+ diffs.push({
110
+ path: file.installPath,
111
+ status: 'modified',
112
+ currentContent,
113
+ newContent,
114
+ });
115
+ } else {
116
+ diffs.push({
117
+ path: file.installPath,
118
+ status: 'unchanged',
119
+ currentContent,
120
+ newContent,
121
+ });
122
+ }
123
+ }
124
+
125
+ return diffs;
126
+ }
127
+
128
+ export async function diff(options: DiffOptions): Promise<void> {
129
+ const cwd = process.cwd();
130
+ const safewordDir = join(cwd, '.safeword');
131
+
132
+ // Check if configured
133
+ if (!exists(safewordDir)) {
134
+ error('Not configured. Run `safeword setup` first.');
135
+ process.exit(1);
136
+ }
137
+
138
+ // Read project version
139
+ const versionPath = join(safewordDir, 'version');
140
+ const projectVersion = readFileSafe(versionPath)?.trim() ?? 'unknown';
141
+
142
+ header('Safeword Diff');
143
+ info(`Changes from v${projectVersion} → v${VERSION}`);
144
+
145
+ const diffs = getFileDiffs(cwd);
146
+
147
+ const added = diffs.filter(d => d.status === 'added');
148
+ const modified = diffs.filter(d => d.status === 'modified');
149
+ const unchanged = diffs.filter(d => d.status === 'unchanged');
150
+
151
+ // Summary
152
+ info(
153
+ `\nSummary: ${added.length} added, ${modified.length} modified, ${unchanged.length} unchanged`,
154
+ );
155
+
156
+ // List by category
157
+ if (added.length > 0) {
158
+ info('\nAdded:');
159
+ for (const file of added) {
160
+ listItem(file.path);
161
+ }
162
+ }
163
+
164
+ if (modified.length > 0) {
165
+ info('\nModified:');
166
+ for (const file of modified) {
167
+ listItem(file.path);
168
+ }
169
+ }
170
+
171
+ if (unchanged.length > 0) {
172
+ info('\nUnchanged:');
173
+ for (const file of unchanged) {
174
+ listItem(file.path);
175
+ }
176
+ }
177
+
178
+ // Verbose output - show actual diffs
179
+ if (options.verbose) {
180
+ header('Detailed Changes');
181
+
182
+ for (const file of modified) {
183
+ if (file.currentContent) {
184
+ info(`\n${file.path}:`);
185
+ const diffOutput = createUnifiedDiff(file.currentContent, file.newContent, file.path);
186
+ if (diffOutput) {
187
+ console.log(diffOutput);
188
+ }
189
+ }
190
+ }
191
+
192
+ for (const file of added) {
193
+ info(`\n${file.path}: (new file)`);
194
+ const lines = file.newContent.split('\n').slice(0, 10);
195
+ for (const line of lines) {
196
+ console.log(`+${line}`);
197
+ }
198
+ if (file.newContent.split('\n').length > 10) {
199
+ console.log('... (truncated)');
200
+ }
201
+ }
202
+ }
203
+
204
+ if (added.length === 0 && modified.length === 0) {
205
+ success('\nNo changes needed - configuration is up to date');
206
+ } else {
207
+ info('\nRun `safeword upgrade` to apply these changes');
208
+ }
209
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Reset command - Remove safeword configuration from project
3
+ */
4
+
5
+ import { join } from 'node:path';
6
+ import { exists, remove, readJson, writeJson, listDir } from '../utils/fs.js';
7
+ import { info, success, error, header, listItem } from '../utils/output.js';
8
+ import { isGitRepo, removeGitHook } from '../utils/git.js';
9
+ import { filterOutSafewordHooks } from '../utils/hooks.js';
10
+ import { removeAgentsMdLink } from '../utils/agents-md.js';
11
+
12
+ export interface ResetOptions {
13
+ yes?: boolean;
14
+ }
15
+
16
+ export async function reset(options: ResetOptions): Promise<void> {
17
+ const cwd = process.cwd();
18
+ const safewordDir = join(cwd, '.safeword');
19
+
20
+ // Check if configured
21
+ if (!exists(safewordDir)) {
22
+ info('Nothing to remove. Project is not configured with safeword.');
23
+ return;
24
+ }
25
+
26
+ const isNonInteractive = options.yes || !process.stdin.isTTY;
27
+
28
+ // Confirmation (in interactive mode without --yes)
29
+ if (!isNonInteractive) {
30
+ // In a real implementation, we'd prompt here
31
+ // For now, non-TTY mode auto-confirms
32
+ }
33
+
34
+ header('Safeword Reset');
35
+ info('Removing safeword configuration...');
36
+
37
+ const removed: string[] = [];
38
+
39
+ try {
40
+ // 1. Remove .safeword directory
41
+ if (exists(safewordDir)) {
42
+ remove(safewordDir);
43
+ removed.push('.safeword/');
44
+ success('Removed .safeword directory');
45
+ }
46
+
47
+ // 2. Remove safeword hooks from .claude/settings.json
48
+ const settingsPath = join(cwd, '.claude', 'settings.json');
49
+
50
+ if (exists(settingsPath)) {
51
+ info('\nRemoving hooks from .claude/settings.json...');
52
+
53
+ interface SettingsJson {
54
+ hooks?: Record<string, unknown[]>;
55
+ [key: string]: unknown;
56
+ }
57
+
58
+ const settings = readJson<SettingsJson>(settingsPath);
59
+
60
+ if (settings?.hooks) {
61
+ let modified = false;
62
+
63
+ for (const [event, hooks] of Object.entries(settings.hooks)) {
64
+ if (Array.isArray(hooks)) {
65
+ const filtered = filterOutSafewordHooks(hooks);
66
+ if (filtered.length !== hooks.length) {
67
+ settings.hooks[event] = filtered;
68
+ modified = true;
69
+ }
70
+ }
71
+ }
72
+
73
+ if (modified) {
74
+ writeJson(settingsPath, settings);
75
+ removed.push('.claude/settings.json (hooks)');
76
+ success('Removed safeword hooks');
77
+ }
78
+ }
79
+ }
80
+
81
+ // 3. Remove safeword skills
82
+ const skillsDir = join(cwd, '.claude', 'skills');
83
+
84
+ if (exists(skillsDir)) {
85
+ info('\nRemoving safeword skills...');
86
+
87
+ const skills = listDir(skillsDir);
88
+ for (const skill of skills) {
89
+ if (skill.startsWith('safeword-')) {
90
+ remove(join(skillsDir, skill));
91
+ removed.push(`.claude/skills/${skill}/`);
92
+ }
93
+ }
94
+
95
+ if (removed.some(r => r.includes('skills'))) {
96
+ success('Removed safeword skills');
97
+ }
98
+ }
99
+
100
+ // 3.5. Remove safeword slash commands
101
+ const commandsDir = join(cwd, '.claude', 'commands');
102
+ const safewordCommands = ['quality-review.md', 'arch-review.md', 'lint.md'];
103
+
104
+ if (exists(commandsDir)) {
105
+ info('\nRemoving safeword commands...');
106
+
107
+ let commandsRemoved = false;
108
+ for (const cmd of safewordCommands) {
109
+ const cmdPath = join(commandsDir, cmd);
110
+ if (exists(cmdPath)) {
111
+ remove(cmdPath);
112
+ removed.push(`.claude/commands/${cmd}`);
113
+ commandsRemoved = true;
114
+ }
115
+ }
116
+
117
+ if (commandsRemoved) {
118
+ success('Removed safeword commands');
119
+ }
120
+ }
121
+
122
+ // 3.6. Remove MCP servers from .mcp.json
123
+ const mcpConfigPath = join(cwd, '.mcp.json');
124
+
125
+ if (exists(mcpConfigPath)) {
126
+ info('\nRemoving MCP servers...');
127
+
128
+ interface McpConfig {
129
+ mcpServers?: Record<string, unknown>;
130
+ [key: string]: unknown;
131
+ }
132
+
133
+ const mcpConfig = readJson<McpConfig>(mcpConfigPath);
134
+
135
+ if (mcpConfig?.mcpServers) {
136
+ // Remove safeword MCP servers
137
+ delete mcpConfig.mcpServers.context7;
138
+ delete mcpConfig.mcpServers.playwright;
139
+
140
+ // If no servers left, remove the file
141
+ if (Object.keys(mcpConfig.mcpServers).length === 0) {
142
+ remove(mcpConfigPath);
143
+ removed.push('.mcp.json');
144
+ } else {
145
+ writeJson(mcpConfigPath, mcpConfig);
146
+ removed.push('.mcp.json (context7, playwright)');
147
+ }
148
+
149
+ success('Removed MCP servers');
150
+ }
151
+ }
152
+
153
+ // 4. Remove git hook markers
154
+ if (isGitRepo(cwd)) {
155
+ info('\nRemoving git hook markers...');
156
+ removeGitHook(cwd);
157
+ removed.push('.git/hooks/pre-commit (markers)');
158
+ success('Removed git hook markers');
159
+ }
160
+
161
+ // 5. Remove link from AGENTS.md
162
+ info('\nCleaning AGENTS.md...');
163
+ if (removeAgentsMdLink(cwd)) {
164
+ removed.push('AGENTS.md (link)');
165
+ success('Removed safeword link from AGENTS.md');
166
+ }
167
+
168
+ // Print summary
169
+ header('Reset Complete');
170
+
171
+ if (removed.length > 0) {
172
+ info('\nRemoved:');
173
+ for (const item of removed) {
174
+ listItem(item);
175
+ }
176
+ }
177
+
178
+ // Note about preserved linting
179
+ info('\nPreserved (remove manually if desired):');
180
+ listItem('eslint.config.mjs');
181
+ listItem('.prettierrc');
182
+ listItem('package.json lint/format scripts');
183
+ listItem('ESLint/Prettier devDependencies');
184
+
185
+ success('\nSafeword configuration removed');
186
+ } catch (err) {
187
+ error(`Reset failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
188
+ process.exit(1);
189
+ }
190
+ }