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
@@ -0,0 +1,166 @@
1
+ import {
2
+ VERSION
3
+ } from "./chunk-ORQHKDT2.js";
4
+ import {
5
+ error,
6
+ exists,
7
+ getTemplatesDir,
8
+ header,
9
+ info,
10
+ listItem,
11
+ readFileSafe,
12
+ success
13
+ } from "./chunk-GZRQL3SX.js";
14
+
15
+ // src/commands/diff.ts
16
+ import { join } from "path";
17
+ function createUnifiedDiff(oldContent, newContent, filename) {
18
+ const oldLines = oldContent.split("\n");
19
+ const newLines = newContent.split("\n");
20
+ const lines = [];
21
+ lines.push(`--- a/${filename}`);
22
+ lines.push(`+++ b/${filename}`);
23
+ let hasChanges = false;
24
+ const maxLines = Math.max(oldLines.length, newLines.length);
25
+ for (let i = 0; i < maxLines; i++) {
26
+ const oldLine = oldLines[i];
27
+ const newLine = newLines[i];
28
+ if (oldLine === newLine) {
29
+ lines.push(` ${oldLine ?? ""}`);
30
+ } else {
31
+ hasChanges = true;
32
+ if (oldLine !== void 0) {
33
+ lines.push(`-${oldLine}`);
34
+ }
35
+ if (newLine !== void 0) {
36
+ lines.push(`+${newLine}`);
37
+ }
38
+ }
39
+ }
40
+ if (!hasChanges) {
41
+ return "";
42
+ }
43
+ lines.splice(2, 0, `@@ -1,${oldLines.length} +1,${newLines.length} @@`);
44
+ return lines.join("\n");
45
+ }
46
+ function getFileDiffs(cwd) {
47
+ const templatesDir = getTemplatesDir();
48
+ const diffs = [];
49
+ const files = [
50
+ { templatePath: "SAFEWORD.md", installPath: ".safeword/SAFEWORD.md" },
51
+ { templatePath: "hooks/agents-md-check.sh", installPath: ".safeword/hooks/agents-md-check.sh" },
52
+ { templatePath: "hooks/pre-commit.sh", installPath: ".safeword/hooks/pre-commit.sh" },
53
+ { templatePath: "hooks/post-tool.sh", installPath: ".safeword/hooks/post-tool.sh" },
54
+ { templatePath: "hooks/inject-timestamp.sh", installPath: ".safeword/hooks/inject-timestamp.sh" },
55
+ { templatePath: "skills/safeword-quality-reviewer/SKILL.md", installPath: ".claude/skills/safeword-quality-reviewer/SKILL.md" }
56
+ ];
57
+ const versionPath = join(cwd, ".safeword/version");
58
+ const currentVersion = readFileSafe(versionPath);
59
+ if (currentVersion === null) {
60
+ diffs.push({ path: ".safeword/version", status: "added", newContent: VERSION });
61
+ } else if (currentVersion.trim() !== VERSION) {
62
+ diffs.push({ path: ".safeword/version", status: "modified", currentContent: currentVersion, newContent: VERSION });
63
+ } else {
64
+ diffs.push({ path: ".safeword/version", status: "unchanged", currentContent: currentVersion, newContent: VERSION });
65
+ }
66
+ for (const file of files) {
67
+ const templateFullPath = join(templatesDir, file.templatePath);
68
+ const installFullPath = join(cwd, file.installPath);
69
+ const newContent = readFileSafe(templateFullPath);
70
+ if (newContent === null) continue;
71
+ const currentContent = readFileSafe(installFullPath);
72
+ if (currentContent === null) {
73
+ diffs.push({
74
+ path: file.installPath,
75
+ status: "added",
76
+ newContent
77
+ });
78
+ } else if (currentContent.trim() !== newContent.trim()) {
79
+ diffs.push({
80
+ path: file.installPath,
81
+ status: "modified",
82
+ currentContent,
83
+ newContent
84
+ });
85
+ } else {
86
+ diffs.push({
87
+ path: file.installPath,
88
+ status: "unchanged",
89
+ currentContent,
90
+ newContent
91
+ });
92
+ }
93
+ }
94
+ return diffs;
95
+ }
96
+ async function diff(options) {
97
+ const cwd = process.cwd();
98
+ const safewordDir = join(cwd, ".safeword");
99
+ if (!exists(safewordDir)) {
100
+ error("Not configured. Run `safeword setup` first.");
101
+ process.exit(1);
102
+ }
103
+ const versionPath = join(safewordDir, "version");
104
+ const projectVersion = readFileSafe(versionPath)?.trim() ?? "unknown";
105
+ header("Safeword Diff");
106
+ info(`Changes from v${projectVersion} \u2192 v${VERSION}`);
107
+ const diffs = getFileDiffs(cwd);
108
+ const added = diffs.filter((d) => d.status === "added");
109
+ const modified = diffs.filter((d) => d.status === "modified");
110
+ const unchanged = diffs.filter((d) => d.status === "unchanged");
111
+ info(
112
+ `
113
+ Summary: ${added.length} added, ${modified.length} modified, ${unchanged.length} unchanged`
114
+ );
115
+ if (added.length > 0) {
116
+ info("\nAdded:");
117
+ for (const file of added) {
118
+ listItem(file.path);
119
+ }
120
+ }
121
+ if (modified.length > 0) {
122
+ info("\nModified:");
123
+ for (const file of modified) {
124
+ listItem(file.path);
125
+ }
126
+ }
127
+ if (unchanged.length > 0) {
128
+ info("\nUnchanged:");
129
+ for (const file of unchanged) {
130
+ listItem(file.path);
131
+ }
132
+ }
133
+ if (options.verbose) {
134
+ header("Detailed Changes");
135
+ for (const file of modified) {
136
+ if (file.currentContent) {
137
+ info(`
138
+ ${file.path}:`);
139
+ const diffOutput = createUnifiedDiff(file.currentContent, file.newContent, file.path);
140
+ if (diffOutput) {
141
+ console.log(diffOutput);
142
+ }
143
+ }
144
+ }
145
+ for (const file of added) {
146
+ info(`
147
+ ${file.path}: (new file)`);
148
+ const lines = file.newContent.split("\n").slice(0, 10);
149
+ for (const line of lines) {
150
+ console.log(`+${line}`);
151
+ }
152
+ if (file.newContent.split("\n").length > 10) {
153
+ console.log("... (truncated)");
154
+ }
155
+ }
156
+ }
157
+ if (added.length === 0 && modified.length === 0) {
158
+ success("\nNo changes needed - configuration is up to date");
159
+ } else {
160
+ info("\nRun `safeword upgrade` to apply these changes");
161
+ }
162
+ }
163
+ export {
164
+ diff
165
+ };
166
+ //# sourceMappingURL=diff-Y6QTAW4O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/diff.ts"],"sourcesContent":["/**\n * Diff command - Preview changes that would be made by upgrade\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, readFileSafe, getTemplatesDir } from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\n\nexport interface DiffOptions {\n verbose?: boolean;\n}\n\ninterface FileDiff {\n path: string;\n status: 'added' | 'modified' | 'unchanged';\n currentContent?: string;\n newContent: string;\n}\n\n/**\n * Create a unified diff between two strings\n */\nfunction createUnifiedDiff(oldContent: string, newContent: string, filename: string): string {\n const oldLines = oldContent.split('\\n');\n const newLines = newContent.split('\\n');\n\n const lines: string[] = [];\n lines.push(`--- a/${filename}`);\n lines.push(`+++ b/${filename}`);\n\n // Simple diff - show all changes\n // A real implementation would use a proper diff algorithm\n let hasChanges = false;\n\n const maxLines = Math.max(oldLines.length, newLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const oldLine = oldLines[i];\n const newLine = newLines[i];\n\n if (oldLine === newLine) {\n lines.push(` ${oldLine ?? ''}`);\n } else {\n hasChanges = true;\n if (oldLine !== undefined) {\n lines.push(`-${oldLine}`);\n }\n if (newLine !== undefined) {\n lines.push(`+${newLine}`);\n }\n }\n }\n\n if (!hasChanges) {\n return '';\n }\n\n // Add context marker\n lines.splice(2, 0, `@@ -1,${oldLines.length} +1,${newLines.length} @@`);\n\n return lines.join('\\n');\n}\n\n/**\n * Get all files that would be changed by upgrade\n */\nfunction getFileDiffs(cwd: string): FileDiff[] {\n const templatesDir = getTemplatesDir();\n const diffs: FileDiff[] = [];\n\n // Define files to check (template source -> install destination)\n const files: Array<{ templatePath: string; installPath: string }> = [\n { templatePath: 'SAFEWORD.md', installPath: '.safeword/SAFEWORD.md' },\n { templatePath: 'hooks/agents-md-check.sh', installPath: '.safeword/hooks/agents-md-check.sh' },\n { templatePath: 'hooks/pre-commit.sh', installPath: '.safeword/hooks/pre-commit.sh' },\n { templatePath: 'hooks/post-tool.sh', installPath: '.safeword/hooks/post-tool.sh' },\n { templatePath: 'hooks/inject-timestamp.sh', installPath: '.safeword/hooks/inject-timestamp.sh' },\n { templatePath: 'skills/safeword-quality-reviewer/SKILL.md', installPath: '.claude/skills/safeword-quality-reviewer/SKILL.md' },\n ];\n\n // Add version file (not from templates)\n const versionPath = join(cwd, '.safeword/version');\n const currentVersion = readFileSafe(versionPath);\n if (currentVersion === null) {\n diffs.push({ path: '.safeword/version', status: 'added', newContent: VERSION });\n } else if (currentVersion.trim() !== VERSION) {\n diffs.push({ path: '.safeword/version', status: 'modified', currentContent: currentVersion, newContent: VERSION });\n } else {\n diffs.push({ path: '.safeword/version', status: 'unchanged', currentContent: currentVersion, newContent: VERSION });\n }\n\n for (const file of files) {\n const templateFullPath = join(templatesDir, file.templatePath);\n const installFullPath = join(cwd, file.installPath);\n\n const newContent = readFileSafe(templateFullPath);\n if (newContent === null) continue; // Skip if template doesn't exist\n\n const currentContent = readFileSafe(installFullPath);\n\n if (currentContent === null) {\n diffs.push({\n path: file.installPath,\n status: 'added',\n newContent,\n });\n } else if (currentContent.trim() !== newContent.trim()) {\n diffs.push({\n path: file.installPath,\n status: 'modified',\n currentContent,\n newContent,\n });\n } else {\n diffs.push({\n path: file.installPath,\n status: 'unchanged',\n currentContent,\n newContent,\n });\n }\n }\n\n return diffs;\n}\n\nexport async function diff(options: DiffOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? 'unknown';\n\n header('Safeword Diff');\n info(`Changes from v${projectVersion} → v${VERSION}`);\n\n const diffs = getFileDiffs(cwd);\n\n const added = diffs.filter(d => d.status === 'added');\n const modified = diffs.filter(d => d.status === 'modified');\n const unchanged = diffs.filter(d => d.status === 'unchanged');\n\n // Summary\n info(\n `\\nSummary: ${added.length} added, ${modified.length} modified, ${unchanged.length} unchanged`,\n );\n\n // List by category\n if (added.length > 0) {\n info('\\nAdded:');\n for (const file of added) {\n listItem(file.path);\n }\n }\n\n if (modified.length > 0) {\n info('\\nModified:');\n for (const file of modified) {\n listItem(file.path);\n }\n }\n\n if (unchanged.length > 0) {\n info('\\nUnchanged:');\n for (const file of unchanged) {\n listItem(file.path);\n }\n }\n\n // Verbose output - show actual diffs\n if (options.verbose) {\n header('Detailed Changes');\n\n for (const file of modified) {\n if (file.currentContent) {\n info(`\\n${file.path}:`);\n const diffOutput = createUnifiedDiff(file.currentContent, file.newContent, file.path);\n if (diffOutput) {\n console.log(diffOutput);\n }\n }\n }\n\n for (const file of added) {\n info(`\\n${file.path}: (new file)`);\n const lines = file.newContent.split('\\n').slice(0, 10);\n for (const line of lines) {\n console.log(`+${line}`);\n }\n if (file.newContent.split('\\n').length > 10) {\n console.log('... (truncated)');\n }\n }\n }\n\n if (added.length === 0 && modified.length === 0) {\n success('\\nNo changes needed - configuration is up to date');\n } else {\n info('\\nRun `safeword upgrade` to apply these changes');\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,11 @@
1
+ declare const VERSION: string;
2
+
3
+ interface ProjectType {
4
+ typescript: boolean;
5
+ react: boolean;
6
+ nextjs: boolean;
7
+ astro: boolean;
8
+ electron: boolean;
9
+ }
10
+
11
+ export { type ProjectType, VERSION };
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ VERSION
3
+ } from "./chunk-ORQHKDT2.js";
4
+ export {
5
+ VERSION
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,143 @@
1
+ import {
2
+ filterOutSafewordHooks,
3
+ isGitRepo,
4
+ removeAgentsMdLink,
5
+ removeGitHook
6
+ } from "./chunk-2XWIUEQK.js";
7
+ import {
8
+ error,
9
+ exists,
10
+ header,
11
+ info,
12
+ listDir,
13
+ listItem,
14
+ readJson,
15
+ remove,
16
+ success,
17
+ writeJson
18
+ } from "./chunk-GZRQL3SX.js";
19
+
20
+ // src/commands/reset.ts
21
+ import { join } from "path";
22
+ async function reset(options) {
23
+ const cwd = process.cwd();
24
+ const safewordDir = join(cwd, ".safeword");
25
+ if (!exists(safewordDir)) {
26
+ info("Nothing to remove. Project is not configured with safeword.");
27
+ return;
28
+ }
29
+ const isNonInteractive = options.yes || !process.stdin.isTTY;
30
+ if (!isNonInteractive) {
31
+ }
32
+ header("Safeword Reset");
33
+ info("Removing safeword configuration...");
34
+ const removed = [];
35
+ try {
36
+ if (exists(safewordDir)) {
37
+ remove(safewordDir);
38
+ removed.push(".safeword/");
39
+ success("Removed .safeword directory");
40
+ }
41
+ const settingsPath = join(cwd, ".claude", "settings.json");
42
+ if (exists(settingsPath)) {
43
+ info("\nRemoving hooks from .claude/settings.json...");
44
+ const settings = readJson(settingsPath);
45
+ if (settings?.hooks) {
46
+ let modified = false;
47
+ for (const [event, hooks] of Object.entries(settings.hooks)) {
48
+ if (Array.isArray(hooks)) {
49
+ const filtered = filterOutSafewordHooks(hooks);
50
+ if (filtered.length !== hooks.length) {
51
+ settings.hooks[event] = filtered;
52
+ modified = true;
53
+ }
54
+ }
55
+ }
56
+ if (modified) {
57
+ writeJson(settingsPath, settings);
58
+ removed.push(".claude/settings.json (hooks)");
59
+ success("Removed safeword hooks");
60
+ }
61
+ }
62
+ }
63
+ const skillsDir = join(cwd, ".claude", "skills");
64
+ if (exists(skillsDir)) {
65
+ info("\nRemoving safeword skills...");
66
+ const skills = listDir(skillsDir);
67
+ for (const skill of skills) {
68
+ if (skill.startsWith("safeword-")) {
69
+ remove(join(skillsDir, skill));
70
+ removed.push(`.claude/skills/${skill}/`);
71
+ }
72
+ }
73
+ if (removed.some((r) => r.includes("skills"))) {
74
+ success("Removed safeword skills");
75
+ }
76
+ }
77
+ const commandsDir = join(cwd, ".claude", "commands");
78
+ const safewordCommands = ["quality-review.md", "arch-review.md", "lint.md"];
79
+ if (exists(commandsDir)) {
80
+ info("\nRemoving safeword commands...");
81
+ let commandsRemoved = false;
82
+ for (const cmd of safewordCommands) {
83
+ const cmdPath = join(commandsDir, cmd);
84
+ if (exists(cmdPath)) {
85
+ remove(cmdPath);
86
+ removed.push(`.claude/commands/${cmd}`);
87
+ commandsRemoved = true;
88
+ }
89
+ }
90
+ if (commandsRemoved) {
91
+ success("Removed safeword commands");
92
+ }
93
+ }
94
+ const mcpConfigPath = join(cwd, ".mcp.json");
95
+ if (exists(mcpConfigPath)) {
96
+ info("\nRemoving MCP servers...");
97
+ const mcpConfig = readJson(mcpConfigPath);
98
+ if (mcpConfig?.mcpServers) {
99
+ delete mcpConfig.mcpServers.context7;
100
+ delete mcpConfig.mcpServers.playwright;
101
+ if (Object.keys(mcpConfig.mcpServers).length === 0) {
102
+ remove(mcpConfigPath);
103
+ removed.push(".mcp.json");
104
+ } else {
105
+ writeJson(mcpConfigPath, mcpConfig);
106
+ removed.push(".mcp.json (context7, playwright)");
107
+ }
108
+ success("Removed MCP servers");
109
+ }
110
+ }
111
+ if (isGitRepo(cwd)) {
112
+ info("\nRemoving git hook markers...");
113
+ removeGitHook(cwd);
114
+ removed.push(".git/hooks/pre-commit (markers)");
115
+ success("Removed git hook markers");
116
+ }
117
+ info("\nCleaning AGENTS.md...");
118
+ if (removeAgentsMdLink(cwd)) {
119
+ removed.push("AGENTS.md (link)");
120
+ success("Removed safeword link from AGENTS.md");
121
+ }
122
+ header("Reset Complete");
123
+ if (removed.length > 0) {
124
+ info("\nRemoved:");
125
+ for (const item of removed) {
126
+ listItem(item);
127
+ }
128
+ }
129
+ info("\nPreserved (remove manually if desired):");
130
+ listItem("eslint.config.mjs");
131
+ listItem(".prettierrc");
132
+ listItem("package.json lint/format scripts");
133
+ listItem("ESLint/Prettier devDependencies");
134
+ success("\nSafeword configuration removed");
135
+ } catch (err) {
136
+ error(`Reset failed: ${err instanceof Error ? err.message : "Unknown error"}`);
137
+ process.exit(1);
138
+ }
139
+ }
140
+ export {
141
+ reset
142
+ };
143
+ //# sourceMappingURL=reset-3ACTIYYE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/reset.ts"],"sourcesContent":["/**\n * Reset command - Remove safeword configuration from project\n */\n\nimport { join } from 'node:path';\nimport { exists, remove, readJson, writeJson, listDir } from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo, removeGitHook } from '../utils/git.js';\nimport { filterOutSafewordHooks } from '../utils/hooks.js';\nimport { removeAgentsMdLink } from '../utils/agents-md.js';\n\nexport interface ResetOptions {\n yes?: boolean;\n}\n\nexport async function reset(options: ResetOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n info('Nothing to remove. Project is not configured with safeword.');\n return;\n }\n\n const isNonInteractive = options.yes || !process.stdin.isTTY;\n\n // Confirmation (in interactive mode without --yes)\n if (!isNonInteractive) {\n // In a real implementation, we'd prompt here\n // For now, non-TTY mode auto-confirms\n }\n\n header('Safeword Reset');\n info('Removing safeword configuration...');\n\n const removed: string[] = [];\n\n try {\n // 1. Remove .safeword directory\n if (exists(safewordDir)) {\n remove(safewordDir);\n removed.push('.safeword/');\n success('Removed .safeword directory');\n }\n\n // 2. Remove safeword hooks from .claude/settings.json\n const settingsPath = join(cwd, '.claude', 'settings.json');\n\n if (exists(settingsPath)) {\n info('\\nRemoving hooks from .claude/settings.json...');\n\n interface SettingsJson {\n hooks?: Record<string, unknown[]>;\n [key: string]: unknown;\n }\n\n const settings = readJson<SettingsJson>(settingsPath);\n\n if (settings?.hooks) {\n let modified = false;\n\n for (const [event, hooks] of Object.entries(settings.hooks)) {\n if (Array.isArray(hooks)) {\n const filtered = filterOutSafewordHooks(hooks);\n if (filtered.length !== hooks.length) {\n settings.hooks[event] = filtered;\n modified = true;\n }\n }\n }\n\n if (modified) {\n writeJson(settingsPath, settings);\n removed.push('.claude/settings.json (hooks)');\n success('Removed safeword hooks');\n }\n }\n }\n\n // 3. Remove safeword skills\n const skillsDir = join(cwd, '.claude', 'skills');\n\n if (exists(skillsDir)) {\n info('\\nRemoving safeword skills...');\n\n const skills = listDir(skillsDir);\n for (const skill of skills) {\n if (skill.startsWith('safeword-')) {\n remove(join(skillsDir, skill));\n removed.push(`.claude/skills/${skill}/`);\n }\n }\n\n if (removed.some(r => r.includes('skills'))) {\n success('Removed safeword skills');\n }\n }\n\n // 3.5. Remove safeword slash commands\n const commandsDir = join(cwd, '.claude', 'commands');\n const safewordCommands = ['quality-review.md', 'arch-review.md', 'lint.md'];\n\n if (exists(commandsDir)) {\n info('\\nRemoving safeword commands...');\n\n let commandsRemoved = false;\n for (const cmd of safewordCommands) {\n const cmdPath = join(commandsDir, cmd);\n if (exists(cmdPath)) {\n remove(cmdPath);\n removed.push(`.claude/commands/${cmd}`);\n commandsRemoved = true;\n }\n }\n\n if (commandsRemoved) {\n success('Removed safeword commands');\n }\n }\n\n // 3.6. Remove MCP servers from .mcp.json\n const mcpConfigPath = join(cwd, '.mcp.json');\n\n if (exists(mcpConfigPath)) {\n info('\\nRemoving MCP servers...');\n\n interface McpConfig {\n mcpServers?: Record<string, unknown>;\n [key: string]: unknown;\n }\n\n const mcpConfig = readJson<McpConfig>(mcpConfigPath);\n\n if (mcpConfig?.mcpServers) {\n // Remove safeword MCP servers\n delete mcpConfig.mcpServers.context7;\n delete mcpConfig.mcpServers.playwright;\n\n // If no servers left, remove the file\n if (Object.keys(mcpConfig.mcpServers).length === 0) {\n remove(mcpConfigPath);\n removed.push('.mcp.json');\n } else {\n writeJson(mcpConfigPath, mcpConfig);\n removed.push('.mcp.json (context7, playwright)');\n }\n\n success('Removed MCP servers');\n }\n }\n\n // 4. Remove git hook markers\n if (isGitRepo(cwd)) {\n info('\\nRemoving git hook markers...');\n removeGitHook(cwd);\n removed.push('.git/hooks/pre-commit (markers)');\n success('Removed git hook markers');\n }\n\n // 5. Remove link from AGENTS.md\n info('\\nCleaning AGENTS.md...');\n if (removeAgentsMdLink(cwd)) {\n removed.push('AGENTS.md (link)');\n success('Removed safeword link from AGENTS.md');\n }\n\n // Print summary\n header('Reset Complete');\n\n if (removed.length > 0) {\n info('\\nRemoved:');\n for (const item of removed) {\n listItem(item);\n }\n }\n\n // Note about preserved linting\n info('\\nPreserved (remove manually if desired):');\n listItem('eslint.config.mjs');\n listItem('.prettierrc');\n listItem('package.json lint/format scripts');\n listItem('ESLint/Prettier devDependencies');\n\n success('\\nSafeword configuration removed');\n } catch (err) {\n error(`Reset failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,276 @@
1
+ import {
2
+ VERSION
3
+ } from "./chunk-ORQHKDT2.js";
4
+ import {
5
+ PRETTIERRC,
6
+ SETTINGS_HOOKS,
7
+ ensureAgentsMdLink,
8
+ filterOutSafewordHooks,
9
+ getEslintConfig,
10
+ installGitHook,
11
+ isGitRepo
12
+ } from "./chunk-2XWIUEQK.js";
13
+ import {
14
+ copyDir,
15
+ copyFile,
16
+ ensureDir,
17
+ error,
18
+ exists,
19
+ getTemplatesDir,
20
+ header,
21
+ info,
22
+ listItem,
23
+ makeScriptsExecutable,
24
+ readJson,
25
+ success,
26
+ updateJson,
27
+ warn,
28
+ writeFile,
29
+ writeJson
30
+ } from "./chunk-GZRQL3SX.js";
31
+
32
+ // src/commands/setup.ts
33
+ import { join } from "path";
34
+
35
+ // src/utils/project-detector.ts
36
+ function detectProjectType(packageJson) {
37
+ const deps = packageJson.dependencies || {};
38
+ const devDeps = packageJson.devDependencies || {};
39
+ const allDeps = { ...deps, ...devDeps };
40
+ const hasTypescript = "typescript" in allDeps;
41
+ const hasReact = "react" in deps || "react" in devDeps;
42
+ const hasNextJs = "next" in deps;
43
+ const hasAstro = "astro" in deps || "astro" in devDeps;
44
+ const hasElectron = "electron" in deps || "electron" in devDeps;
45
+ return {
46
+ typescript: hasTypescript,
47
+ react: hasReact || hasNextJs,
48
+ // Next.js implies React
49
+ nextjs: hasNextJs,
50
+ astro: hasAstro,
51
+ electron: hasElectron
52
+ };
53
+ }
54
+
55
+ // src/commands/setup.ts
56
+ async function setup(options) {
57
+ const cwd = process.cwd();
58
+ const safewordDir = join(cwd, ".safeword");
59
+ if (exists(safewordDir)) {
60
+ error("Already configured. Run `safeword upgrade` to update.");
61
+ process.exit(1);
62
+ }
63
+ const packageJsonPath = join(cwd, "package.json");
64
+ let packageJsonCreated = false;
65
+ if (!exists(packageJsonPath)) {
66
+ const dirName = cwd.split("/").pop() || "project";
67
+ const defaultPackageJson = {
68
+ name: dirName,
69
+ version: "0.1.0",
70
+ scripts: {}
71
+ };
72
+ writeJson(packageJsonPath, defaultPackageJson);
73
+ packageJsonCreated = true;
74
+ }
75
+ const isNonInteractive = options.yes || !process.stdin.isTTY;
76
+ header("Safeword Setup");
77
+ info(`Version: ${VERSION}`);
78
+ if (packageJsonCreated) {
79
+ info("Created package.json (none found)");
80
+ }
81
+ const created = packageJsonCreated ? ["package.json"] : [];
82
+ const modified = [];
83
+ try {
84
+ const templatesDir = getTemplatesDir();
85
+ info("\nCreating .safeword directory...");
86
+ ensureDir(safewordDir);
87
+ ensureDir(join(safewordDir, "learnings"));
88
+ ensureDir(join(safewordDir, "planning", "user-stories"));
89
+ ensureDir(join(safewordDir, "planning", "design"));
90
+ ensureDir(join(safewordDir, "tickets", "completed"));
91
+ copyFile(join(templatesDir, "SAFEWORD.md"), join(safewordDir, "SAFEWORD.md"));
92
+ writeFile(join(safewordDir, "version"), VERSION);
93
+ copyDir(join(templatesDir, "guides"), join(safewordDir, "guides"));
94
+ copyDir(join(templatesDir, "doc-templates"), join(safewordDir, "templates"));
95
+ copyDir(join(templatesDir, "prompts"), join(safewordDir, "prompts"));
96
+ copyDir(join(templatesDir, "lib"), join(safewordDir, "lib"));
97
+ makeScriptsExecutable(join(safewordDir, "lib"));
98
+ copyDir(join(templatesDir, "hooks"), join(safewordDir, "hooks"));
99
+ makeScriptsExecutable(join(safewordDir, "hooks"));
100
+ created.push(".safeword/");
101
+ success("Created .safeword directory");
102
+ info("\nConfiguring AGENTS.md...");
103
+ const agentsMdResult = ensureAgentsMdLink(cwd);
104
+ if (agentsMdResult === "created") {
105
+ created.push("AGENTS.md");
106
+ success("Created AGENTS.md");
107
+ } else if (agentsMdResult === "modified") {
108
+ modified.push("AGENTS.md");
109
+ success("Prepended link to AGENTS.md");
110
+ } else {
111
+ info("AGENTS.md already has safeword link");
112
+ }
113
+ info("\nRegistering Claude Code hooks...");
114
+ const claudeDir = join(cwd, ".claude");
115
+ const settingsPath = join(claudeDir, "settings.json");
116
+ ensureDir(claudeDir);
117
+ try {
118
+ updateJson(settingsPath, (existing) => {
119
+ const hooks = existing?.hooks ?? {};
120
+ for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {
121
+ const existingHooks = hooks[event] ?? [];
122
+ const nonSafewordHooks = filterOutSafewordHooks(existingHooks);
123
+ hooks[event] = [...nonSafewordHooks, ...newHooks];
124
+ }
125
+ return { ...existing, hooks };
126
+ });
127
+ if (exists(settingsPath)) {
128
+ modified.push(".claude/settings.json");
129
+ } else {
130
+ created.push(".claude/settings.json");
131
+ }
132
+ success("Registered hooks in .claude/settings.json");
133
+ } catch (err) {
134
+ error(`Failed to register hooks: ${err instanceof Error ? err.message : "Unknown error"}`);
135
+ process.exit(1);
136
+ }
137
+ info("\nInstalling skills...");
138
+ const skillsDir = join(claudeDir, "skills");
139
+ copyDir(join(templatesDir, "skills"), skillsDir);
140
+ created.push(".claude/skills/safeword-quality-reviewer/");
141
+ success("Installed skills");
142
+ info("\nInstalling slash commands...");
143
+ const commandsDir = join(claudeDir, "commands");
144
+ copyDir(join(templatesDir, "commands"), commandsDir);
145
+ created.push(".claude/commands/");
146
+ success("Installed slash commands");
147
+ info("\nConfiguring MCP servers...");
148
+ const mcpConfigPath = join(cwd, ".mcp.json");
149
+ updateJson(mcpConfigPath, (existing) => {
150
+ const mcpServers = existing?.mcpServers ?? {};
151
+ mcpServers.context7 = {
152
+ command: "npx",
153
+ args: ["-y", "@upstash/context7-mcp@latest"]
154
+ };
155
+ mcpServers.playwright = {
156
+ command: "npx",
157
+ args: ["@playwright/mcp@latest"]
158
+ };
159
+ return { ...existing, mcpServers };
160
+ });
161
+ if (exists(mcpConfigPath)) {
162
+ modified.push(".mcp.json");
163
+ } else {
164
+ created.push(".mcp.json");
165
+ }
166
+ success("Configured MCP servers");
167
+ info("\nConfiguring linting...");
168
+ const packageJson = readJson(packageJsonPath);
169
+ if (!packageJson) {
170
+ error("Failed to read package.json");
171
+ process.exit(1);
172
+ }
173
+ const projectType = detectProjectType(packageJson);
174
+ const eslintConfigPath = join(cwd, "eslint.config.mjs");
175
+ if (!exists(eslintConfigPath)) {
176
+ writeFile(eslintConfigPath, getEslintConfig(projectType));
177
+ created.push("eslint.config.mjs");
178
+ success("Created eslint.config.mjs");
179
+ } else {
180
+ info("eslint.config.mjs already exists");
181
+ }
182
+ const prettierrcPath = join(cwd, ".prettierrc");
183
+ if (!exists(prettierrcPath)) {
184
+ writeFile(prettierrcPath, PRETTIERRC);
185
+ created.push(".prettierrc");
186
+ success("Created .prettierrc");
187
+ } else {
188
+ info(".prettierrc already exists");
189
+ }
190
+ const markdownlintPath = join(cwd, ".markdownlint.jsonc");
191
+ if (!exists(markdownlintPath)) {
192
+ copyFile(join(templatesDir, "markdownlint.jsonc"), markdownlintPath);
193
+ created.push(".markdownlint.jsonc");
194
+ success("Created .markdownlint.jsonc");
195
+ } else {
196
+ info(".markdownlint.jsonc already exists");
197
+ }
198
+ try {
199
+ const scripts = packageJson.scripts ?? {};
200
+ let scriptsModified = false;
201
+ if (!scripts.lint) {
202
+ scripts.lint = "eslint .";
203
+ scriptsModified = true;
204
+ }
205
+ if (!scripts["lint:md"]) {
206
+ scripts["lint:md"] = 'markdownlint-cli2 "**/*.md" "#node_modules"';
207
+ scriptsModified = true;
208
+ }
209
+ if (!scripts.format) {
210
+ scripts.format = "prettier --write .";
211
+ scriptsModified = true;
212
+ }
213
+ if (!scripts["format:check"]) {
214
+ scripts["format:check"] = "prettier --check .";
215
+ scriptsModified = true;
216
+ }
217
+ if (scriptsModified) {
218
+ packageJson.scripts = scripts;
219
+ writeJson(packageJsonPath, packageJson);
220
+ modified.push("package.json");
221
+ success("Added lint and format scripts");
222
+ }
223
+ } catch (err) {
224
+ error(
225
+ `Failed to update package.json: ${err instanceof Error ? err.message : "Unknown error"}`
226
+ );
227
+ process.exit(1);
228
+ }
229
+ info("\nConfiguring git...");
230
+ if (isGitRepo(cwd)) {
231
+ installGitHook(cwd);
232
+ modified.push(".git/hooks/pre-commit");
233
+ success("Installed git pre-commit hook");
234
+ } else if (isNonInteractive) {
235
+ warn("Skipped git initialization (non-interactive mode)");
236
+ warn("Git hooks not installed (no repository)");
237
+ } else {
238
+ warn("Skipped git initialization (no .git directory)");
239
+ warn("Git hooks not installed (no repository)");
240
+ }
241
+ info("\nNote: Install linting dependencies manually:");
242
+ listItem("npm install -D eslint prettier @eslint/js");
243
+ if (projectType.typescript) {
244
+ listItem("npm install -D typescript-eslint");
245
+ }
246
+ if (projectType.react) {
247
+ listItem("npm install -D eslint-plugin-react eslint-plugin-react-hooks");
248
+ }
249
+ header("Setup Complete");
250
+ if (created.length > 0) {
251
+ info("\nCreated:");
252
+ for (const file of created) {
253
+ listItem(file);
254
+ }
255
+ }
256
+ if (modified.length > 0) {
257
+ info("\nModified:");
258
+ for (const file of modified) {
259
+ listItem(file);
260
+ }
261
+ }
262
+ info("\nNext steps:");
263
+ listItem("Install linting dependencies (see above)");
264
+ listItem("Run `safeword check` to verify setup");
265
+ listItem("Commit the new files to git");
266
+ success(`
267
+ Safeword ${VERSION} installed successfully!`);
268
+ } catch (err) {
269
+ error(`Setup failed: ${err instanceof Error ? err.message : "Unknown error"}`);
270
+ process.exit(1);
271
+ }
272
+ }
273
+ export {
274
+ setup
275
+ };
276
+ //# sourceMappingURL=setup-AIL5RL45.js.map