safeword 0.9.0 → 0.11.0

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 (31) hide show
  1. package/dist/{check-MLYBKA5Z.js → check-EQ3IJPBM.js} +8 -6
  2. package/dist/{check-MLYBKA5Z.js.map → check-EQ3IJPBM.js.map} +1 -1
  3. package/dist/chunk-DYLHQBW3.js +132 -0
  4. package/dist/chunk-DYLHQBW3.js.map +1 -0
  5. package/dist/{chunk-UUKED7KU.js → chunk-KL2JTWK6.js} +110 -192
  6. package/dist/chunk-KL2JTWK6.js.map +1 -0
  7. package/dist/chunk-NDY7IUE7.js +229 -0
  8. package/dist/chunk-NDY7IUE7.js.map +1 -0
  9. package/dist/cli.js +9 -5
  10. package/dist/cli.js.map +1 -1
  11. package/dist/{diff-Q44YDA2W.js → diff-6EDSOICP.js} +8 -6
  12. package/dist/{diff-Q44YDA2W.js.map → diff-6EDSOICP.js.map} +1 -1
  13. package/dist/{reset-EEFVJ5GB.js → reset-HJBALSYG.js} +11 -8
  14. package/dist/reset-HJBALSYG.js.map +1 -0
  15. package/dist/{setup-WZ3F25KX.js → setup-SOWUS7ZI.js} +41 -13
  16. package/dist/setup-SOWUS7ZI.js.map +1 -0
  17. package/dist/sync-config-XTMQBCIH.js +14 -0
  18. package/dist/sync-config-XTMQBCIH.js.map +1 -0
  19. package/dist/{upgrade-WB7N3GWG.js → upgrade-KJLOX4DD.js} +8 -6
  20. package/dist/{upgrade-WB7N3GWG.js.map → upgrade-KJLOX4DD.js.map} +1 -1
  21. package/package.json +2 -2
  22. package/templates/commands/audit.md +41 -0
  23. package/templates/commands/lint.md +1 -4
  24. package/templates/hooks/cursor/after-file-edit.sh +1 -9
  25. package/templates/hooks/post-tool-lint.sh +1 -9
  26. package/templates/skills/safeword-debugging/SKILL.md +15 -15
  27. package/dist/chunk-UUKED7KU.js.map +0 -1
  28. package/dist/reset-EEFVJ5GB.js.map +0 -1
  29. package/dist/setup-WZ3F25KX.js.map +0 -1
  30. package/templates/markdownlint-cli2.jsonc +0 -24
  31. package/templates/scripts/lint-md.sh +0 -16
@@ -1,95 +1,22 @@
1
1
  import {
2
2
  VERSION
3
3
  } from "./chunk-ORQHKDT2.js";
4
-
5
- // src/utils/fs.ts
6
4
  import {
7
- chmodSync,
8
- existsSync,
9
- mkdirSync,
10
- readdirSync,
11
- readFileSync,
12
- rmdirSync,
13
- rmSync,
14
- writeFileSync
15
- } from "fs";
16
- import nodePath from "path";
17
- import { fileURLToPath } from "url";
18
- var __dirname = nodePath.dirname(fileURLToPath(import.meta.url));
19
- function getTemplatesDirectory() {
20
- const knownTemplateFile = "SAFEWORD.md";
21
- const candidates = [
22
- nodePath.join(__dirname, "..", "templates"),
23
- // From dist/ (flat bundled)
24
- nodePath.join(__dirname, "..", "..", "templates"),
25
- // From src/utils/ or dist/utils/
26
- nodePath.join(__dirname, "templates")
27
- // Direct sibling (unlikely but safe)
28
- ];
29
- for (const candidate of candidates) {
30
- if (existsSync(nodePath.join(candidate, knownTemplateFile))) {
31
- return candidate;
32
- }
33
- }
34
- throw new Error("Templates directory not found");
35
- }
36
- function exists(path) {
37
- return existsSync(path);
38
- }
39
- function ensureDirectory(path) {
40
- if (!existsSync(path)) {
41
- mkdirSync(path, { recursive: true });
42
- }
43
- }
44
- function readFile(path) {
45
- return readFileSync(path, "utf8");
46
- }
47
- function readFileSafe(path) {
48
- if (!existsSync(path)) return void 0;
49
- return readFileSync(path, "utf8");
50
- }
51
- function writeFile(path, content) {
52
- ensureDirectory(nodePath.dirname(path));
53
- writeFileSync(path, content);
54
- }
55
- function remove(path) {
56
- if (existsSync(path)) {
57
- rmSync(path, { recursive: true, force: true });
58
- }
59
- }
60
- function removeIfEmpty(path) {
61
- if (!existsSync(path)) return false;
62
- try {
63
- rmdirSync(path);
64
- return true;
65
- } catch {
66
- return false;
67
- }
68
- }
69
- function makeScriptsExecutable(dirPath) {
70
- if (!existsSync(dirPath)) return;
71
- for (const file of readdirSync(dirPath)) {
72
- if (file.endsWith(".sh")) {
73
- chmodSync(nodePath.join(dirPath, file), 493);
74
- }
75
- }
76
- }
77
- function readJson(path) {
78
- const content = readFileSafe(path);
79
- if (!content) return void 0;
80
- try {
81
- return JSON.parse(content);
82
- } catch {
83
- return void 0;
84
- }
85
- }
86
- function writeJson(path, data) {
87
- writeFile(path, `${JSON.stringify(data, void 0, 2)}
88
- `);
89
- }
5
+ ensureDirectory,
6
+ exists,
7
+ getTemplatesDirectory,
8
+ makeScriptsExecutable,
9
+ readFile,
10
+ readFileSafe,
11
+ readJson,
12
+ remove,
13
+ removeIfEmpty,
14
+ writeFile,
15
+ writeJson
16
+ } from "./chunk-DYLHQBW3.js";
90
17
 
91
18
  // src/reconcile.ts
92
- import nodePath2 from "path";
19
+ import nodePath from "path";
93
20
  var HUSKY_DIR = ".husky";
94
21
  function shouldSkipForNonGit(path, isGitRepo2) {
95
22
  return path.startsWith(HUSKY_DIR) && !isGitRepo2;
@@ -99,7 +26,7 @@ function planMissingDirectories(directories, cwd, isGitRepo2) {
99
26
  const created = [];
100
27
  for (const dir of directories) {
101
28
  if (shouldSkipForNonGit(dir, isGitRepo2)) continue;
102
- if (!exists(nodePath2.join(cwd, dir))) {
29
+ if (!exists(nodePath.join(cwd, dir))) {
103
30
  actions.push({ type: "mkdir", path: dir });
104
31
  created.push(dir);
105
32
  }
@@ -110,7 +37,7 @@ function planTextPatches(patches, cwd, isGitRepo2) {
110
37
  const actions = [];
111
38
  for (const [filePath, definition] of Object.entries(patches)) {
112
39
  if (shouldSkipForNonGit(filePath, isGitRepo2)) continue;
113
- const content = readFileSafe(nodePath2.join(cwd, filePath)) ?? "";
40
+ const content = readFileSafe(nodePath.join(cwd, filePath)) ?? "";
114
41
  if (!content.includes(definition.marker)) {
115
42
  actions.push({ type: "text-patch", path: filePath, definition });
116
43
  }
@@ -132,7 +59,7 @@ function planManagedFileWrites(files, ctx) {
132
59
  const actions = [];
133
60
  const created = [];
134
61
  for (const [filePath, definition] of Object.entries(files)) {
135
- if (exists(nodePath2.join(ctx.cwd, filePath))) continue;
62
+ if (exists(nodePath.join(ctx.cwd, filePath))) continue;
136
63
  const content = resolveFileContent(definition, ctx);
137
64
  actions.push({ type: "write", path: filePath, content });
138
65
  created.push(filePath);
@@ -145,7 +72,7 @@ function planTextPatchesWithCreation(patches, ctx) {
145
72
  for (const [filePath, definition] of Object.entries(patches)) {
146
73
  if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;
147
74
  actions.push({ type: "text-patch", path: filePath, definition });
148
- if (definition.createIfMissing && !exists(nodePath2.join(ctx.cwd, filePath))) {
75
+ if (definition.createIfMissing && !exists(nodePath.join(ctx.cwd, filePath))) {
149
76
  created.push(filePath);
150
77
  }
151
78
  }
@@ -155,7 +82,7 @@ function planExistingDirectoriesRemoval(directories, cwd) {
155
82
  const actions = [];
156
83
  const removed = [];
157
84
  for (const dir of directories) {
158
- if (exists(nodePath2.join(cwd, dir))) {
85
+ if (exists(nodePath.join(cwd, dir))) {
159
86
  actions.push({ type: "rmdir", path: dir });
160
87
  removed.push(dir);
161
88
  }
@@ -166,7 +93,7 @@ function planExistingFilesRemoval(files, cwd) {
166
93
  const actions = [];
167
94
  const removed = [];
168
95
  for (const filePath of files) {
169
- if (exists(nodePath2.join(cwd, filePath))) {
96
+ if (exists(nodePath.join(cwd, filePath))) {
170
97
  actions.push({ type: "rm", path: filePath });
171
98
  removed.push(filePath);
172
99
  }
@@ -210,7 +137,7 @@ function planDeprecatedFilesRemoval(deprecatedFiles, cwd) {
210
137
  const actions = [];
211
138
  const removed = [];
212
139
  for (const filePath of deprecatedFiles) {
213
- if (exists(nodePath2.join(cwd, filePath))) {
140
+ if (exists(nodePath.join(cwd, filePath))) {
214
141
  actions.push({ type: "rm", path: filePath });
215
142
  removed.push(filePath);
216
143
  }
@@ -272,7 +199,7 @@ function computeUpgradePlan(schema, ctx) {
272
199
  wouldCreate.push(...missingDirectories.created);
273
200
  for (const [filePath, definition] of Object.entries(schema.ownedFiles)) {
274
201
  if (shouldSkipForNonGit(filePath, ctx.isGitRepo)) continue;
275
- const fullPath = nodePath2.join(ctx.cwd, filePath);
202
+ const fullPath = nodePath.join(ctx.cwd, filePath);
276
203
  const newContent = resolveFileContent(definition, ctx);
277
204
  if (!fileNeedsUpdate(fullPath, newContent)) continue;
278
205
  actions.push({ type: "write", path: filePath, content: newContent });
@@ -283,7 +210,7 @@ function computeUpgradePlan(schema, ctx) {
283
210
  }
284
211
  }
285
212
  for (const [filePath, definition] of Object.entries(schema.managedFiles)) {
286
- const fullPath = nodePath2.join(ctx.cwd, filePath);
213
+ const fullPath = nodePath.join(ctx.cwd, filePath);
287
214
  const newContent = resolveFileContent(definition, ctx);
288
215
  if (!exists(fullPath)) {
289
216
  actions.push({ type: "write", path: filePath, content: newContent });
@@ -341,7 +268,7 @@ function computeUninstallPlan(schema, ctx, full) {
341
268
  actions.push({ type: "json-unmerge", path: filePath, definition });
342
269
  }
343
270
  for (const [filePath, definition] of Object.entries(schema.textPatches)) {
344
- const fullPath = nodePath2.join(ctx.cwd, filePath);
271
+ const fullPath = nodePath.join(ctx.cwd, filePath);
345
272
  if (exists(fullPath)) {
346
273
  const content = readFileSafe(fullPath) ?? "";
347
274
  if (content.includes(definition.marker)) {
@@ -382,17 +309,17 @@ function executePlan(plan, ctx) {
382
309
  }
383
310
  function executeChmod(cwd, paths) {
384
311
  for (const path of paths) {
385
- const fullPath = nodePath2.join(cwd, path);
312
+ const fullPath = nodePath.join(cwd, path);
386
313
  if (exists(fullPath)) makeScriptsExecutable(fullPath);
387
314
  }
388
315
  }
389
316
  function executeRmdir(cwd, path, result) {
390
- if (removeIfEmpty(nodePath2.join(cwd, path))) result.removed.push(path);
317
+ if (removeIfEmpty(nodePath.join(cwd, path))) result.removed.push(path);
391
318
  }
392
319
  function executeAction(action, ctx, result) {
393
320
  switch (action.type) {
394
321
  case "mkdir":
395
- ensureDirectory(nodePath2.join(ctx.cwd, action.path));
322
+ ensureDirectory(nodePath.join(ctx.cwd, action.path));
396
323
  result.created.push(action.path);
397
324
  break;
398
325
  case "rmdir":
@@ -402,7 +329,7 @@ function executeAction(action, ctx, result) {
402
329
  executeWrite(ctx.cwd, action.path, action.content, result);
403
330
  break;
404
331
  case "rm":
405
- remove(nodePath2.join(ctx.cwd, action.path));
332
+ remove(nodePath.join(ctx.cwd, action.path));
406
333
  result.removed.push(action.path);
407
334
  break;
408
335
  case "chmod":
@@ -423,7 +350,7 @@ function executeAction(action, ctx, result) {
423
350
  }
424
351
  }
425
352
  function executeWrite(cwd, path, content, result) {
426
- const fullPath = nodePath2.join(cwd, path);
353
+ const fullPath = nodePath.join(cwd, path);
427
354
  const existed = exists(fullPath);
428
355
  writeFile(fullPath, content);
429
356
  (existed ? result.updated : result.created).push(path);
@@ -431,7 +358,7 @@ function executeWrite(cwd, path, content, result) {
431
358
  function resolveFileContent(definition, ctx) {
432
359
  if (definition.template) {
433
360
  const templatesDirectory = getTemplatesDirectory();
434
- return readFile(nodePath2.join(templatesDirectory, definition.template));
361
+ return readFile(nodePath.join(templatesDirectory, definition.template));
435
362
  }
436
363
  if (definition.content) {
437
364
  return typeof definition.content === "function" ? definition.content() : definition.content;
@@ -469,14 +396,14 @@ function computePackagesToRemove(schema, projectType, installedDevelopmentDeps)
469
396
  return safewordPackages.filter((pkg) => pkg in installedDevelopmentDeps);
470
397
  }
471
398
  function executeJsonMerge(cwd, path, definition, ctx) {
472
- const fullPath = nodePath2.join(cwd, path);
399
+ const fullPath = nodePath.join(cwd, path);
473
400
  const existing = readJson(fullPath) ?? {};
474
401
  const merged = definition.merge(existing, ctx);
475
402
  if (JSON.stringify(existing) === JSON.stringify(merged)) return;
476
403
  writeJson(fullPath, merged);
477
404
  }
478
405
  function executeJsonUnmerge(cwd, path, definition) {
479
- const fullPath = nodePath2.join(cwd, path);
406
+ const fullPath = nodePath.join(cwd, path);
480
407
  if (!exists(fullPath)) return;
481
408
  const existing = readJson(fullPath);
482
409
  if (!existing) return;
@@ -491,14 +418,14 @@ function executeJsonUnmerge(cwd, path, definition) {
491
418
  writeJson(fullPath, unmerged);
492
419
  }
493
420
  function executeTextPatch(cwd, path, definition) {
494
- const fullPath = nodePath2.join(cwd, path);
421
+ const fullPath = nodePath.join(cwd, path);
495
422
  let content = readFileSafe(fullPath) ?? "";
496
423
  if (content.includes(definition.marker)) return;
497
424
  content = definition.operation === "prepend" ? definition.content + content : content + definition.content;
498
425
  writeFile(fullPath, content);
499
426
  }
500
427
  function executeTextUnpatch(cwd, path, definition) {
501
- const fullPath = nodePath2.join(cwd, path);
428
+ const fullPath = nodePath.join(cwd, path);
502
429
  const content = readFileSafe(fullPath);
503
430
  if (!content) return;
504
431
  let unpatched = content.replace(definition.content, "");
@@ -549,13 +476,26 @@ const configs = [
549
476
  ...baseConfig,
550
477
  ];
551
478
 
552
- // Add test configs if testing frameworks detected
479
+ // Add configs for detected tools/frameworks
553
480
  if (deps["vitest"]) {
554
481
  configs.push(...safeword.configs.vitest);
555
482
  }
556
483
  if (deps["playwright"] || deps["@playwright/test"]) {
557
484
  configs.push(...safeword.configs.playwright);
558
485
  }
486
+ if (deps["tailwindcss"]) {
487
+ configs.push(...safeword.configs.tailwind);
488
+ }
489
+ const tanstackQueryPackages = [
490
+ "@tanstack/react-query",
491
+ "@tanstack/vue-query",
492
+ "@tanstack/solid-query",
493
+ "@tanstack/svelte-query",
494
+ "@tanstack/angular-query-experimental",
495
+ ];
496
+ if (tanstackQueryPackages.some(pkg => deps[pkg])) {
497
+ configs.push(...safeword.configs.tanstackQuery);
498
+ }
559
499
 
560
500
  // eslint-config-prettier must be last to disable conflicting rules
561
501
  configs.push(eslintConfigPrettier);
@@ -642,28 +582,6 @@ The SAFEWORD.md file contains core development patterns, workflows, and conventi
642
582
  Read it BEFORE working on any task in this project.
643
583
 
644
584
  ---`;
645
- function getPrettierConfig(projectType) {
646
- const config = {
647
- semi: true,
648
- singleQuote: true,
649
- tabWidth: 2,
650
- trailingComma: "all",
651
- printWidth: 100,
652
- endOfLine: "lf",
653
- useTabs: false,
654
- bracketSpacing: true,
655
- arrowParens: "avoid"
656
- };
657
- const plugins = [];
658
- if (projectType.astro) plugins.push("prettier-plugin-astro");
659
- if (projectType.shell) plugins.push("prettier-plugin-sh");
660
- if (projectType.tailwind) plugins.push("prettier-plugin-tailwindcss");
661
- if (plugins.length > 0) {
662
- config.plugins = plugins;
663
- }
664
- return `${JSON.stringify(config, void 0, 2)}
665
- `;
666
- }
667
585
 
668
586
  // src/utils/hooks.ts
669
587
  function isHookEntry(h) {
@@ -730,9 +648,12 @@ var SAFEWORD_SCHEMA = {
730
648
  ".safeword/guides/user-story-guide.md",
731
649
  ".safeword/guides/test-definitions-guide.md",
732
650
  // Boundaries config now project-specific (v0.9.0)
733
- ".safeword/eslint-boundaries.config.mjs"
651
+ ".safeword/eslint-boundaries.config.mjs",
652
+ // Markdown linting removed (v0.10.0)
653
+ ".markdownlint-cli2.jsonc",
654
+ ".safeword/scripts/lint-md.sh"
734
655
  ],
735
- // Packages to uninstall on upgrade (consolidated into eslint-plugin-safeword v0.9.0)
656
+ // Packages to uninstall on upgrade (now bundled in eslint-plugin-safeword)
736
657
  deprecatedPackages: [
737
658
  // Individual ESLint plugins now bundled in eslint-plugin-safeword
738
659
  "@eslint/js",
@@ -815,13 +736,12 @@ var SAFEWORD_SCHEMA = {
815
736
  // Prompts (2 files)
816
737
  ".safeword/prompts/architecture.md": { template: "prompts/architecture.md" },
817
738
  ".safeword/prompts/quality-review.md": { template: "prompts/quality-review.md" },
818
- // Scripts (4 files)
739
+ // Scripts (3 files)
819
740
  ".safeword/scripts/bisect-test-pollution.sh": { template: "scripts/bisect-test-pollution.sh" },
820
741
  ".safeword/scripts/bisect-zombie-processes.sh": {
821
742
  template: "scripts/bisect-zombie-processes.sh"
822
743
  },
823
744
  ".safeword/scripts/cleanup-zombies.sh": { template: "scripts/cleanup-zombies.sh" },
824
- ".safeword/scripts/lint-md.sh": { template: "scripts/lint-md.sh" },
825
745
  // Claude skills and commands (9 files)
826
746
  ".claude/skills/safeword-brainstorming/SKILL.md": {
827
747
  template: "skills/safeword-brainstorming/SKILL.md"
@@ -842,6 +762,7 @@ var SAFEWORD_SCHEMA = {
842
762
  template: "skills/safeword-writing-plans/SKILL.md"
843
763
  },
844
764
  ".claude/commands/architecture.md": { template: "commands/architecture.md" },
765
+ ".claude/commands/audit.md": { template: "commands/audit.md" },
845
766
  ".claude/commands/cleanup-zombies.md": { template: "commands/cleanup-zombies.md" },
846
767
  ".claude/commands/lint.md": { template: "commands/lint.md" },
847
768
  ".claude/commands/quality-review.md": { template: "commands/quality-review.md" },
@@ -865,8 +786,9 @@ var SAFEWORD_SCHEMA = {
865
786
  ".cursor/rules/safeword-writing-plans.mdc": {
866
787
  template: "cursor/rules/safeword-writing-plans.mdc"
867
788
  },
868
- // Cursor commands (4 files - same as Claude)
789
+ // Cursor commands (5 files - same as Claude)
869
790
  ".cursor/commands/architecture.md": { template: "commands/architecture.md" },
791
+ ".cursor/commands/audit.md": { template: "commands/audit.md" },
870
792
  ".cursor/commands/cleanup-zombies.md": { template: "commands/cleanup-zombies.md" },
871
793
  ".cursor/commands/lint.md": { template: "commands/lint.md" },
872
794
  ".cursor/commands/quality-review.md": { template: "commands/quality-review.md" },
@@ -879,8 +801,6 @@ var SAFEWORD_SCHEMA = {
879
801
  "eslint.config.mjs": {
880
802
  generator: () => getEslintConfig()
881
803
  },
882
- ".prettierrc": { generator: (ctx) => getPrettierConfig(ctx.projectType) },
883
- ".markdownlint-cli2.jsonc": { template: "markdownlint-cli2.jsonc" },
884
804
  // Minimal tsconfig for ESLint type-checked linting (only if missing)
885
805
  "tsconfig.json": {
886
806
  generator: (ctx) => {
@@ -905,18 +825,23 @@ var SAFEWORD_SCHEMA = {
905
825
  2
906
826
  );
907
827
  }
828
+ },
829
+ // Knip config for dead code detection (used by /audit)
830
+ "knip.json": {
831
+ generator: () => JSON.stringify(
832
+ {
833
+ ignore: [".safeword/**"],
834
+ ignoreDependencies: ["eslint-plugin-safeword"]
835
+ },
836
+ void 0,
837
+ 2
838
+ )
908
839
  }
909
840
  },
910
841
  // JSON files where we merge specific keys
911
842
  jsonMerges: {
912
843
  "package.json": {
913
- keys: [
914
- "scripts.lint",
915
- "scripts.lint:md",
916
- "scripts.format",
917
- "scripts.format:check",
918
- "scripts.knip"
919
- ],
844
+ keys: ["scripts.lint", "scripts.format", "scripts.format:check", "scripts.knip"],
920
845
  conditionalKeys: {
921
846
  publishableLibrary: ["scripts.publint"],
922
847
  shell: ["scripts.lint:sh"]
@@ -925,7 +850,6 @@ var SAFEWORD_SCHEMA = {
925
850
  const scripts = { ...existing.scripts };
926
851
  const result = { ...existing };
927
852
  if (!scripts.lint) scripts.lint = "eslint .";
928
- if (!scripts["lint:md"]) scripts["lint:md"] = 'markdownlint-cli2 "**/*.md" "#node_modules"';
929
853
  if (!scripts.format) scripts.format = "prettier --write .";
930
854
  if (!scripts["format:check"]) scripts["format:check"] = "prettier --check .";
931
855
  if (!scripts.knip) scripts.knip = "knip";
@@ -941,7 +865,6 @@ var SAFEWORD_SCHEMA = {
941
865
  unmerge: (existing) => {
942
866
  const result = { ...existing };
943
867
  const scripts = { ...existing.scripts };
944
- delete scripts["lint:md"];
945
868
  delete scripts["lint:sh"];
946
869
  delete scripts["format:check"];
947
870
  delete scripts.knip;
@@ -1066,6 +989,36 @@ var SAFEWORD_SCHEMA = {
1066
989
  }
1067
990
  return result;
1068
991
  }
992
+ },
993
+ ".prettierrc": {
994
+ keys: ["plugins"],
995
+ merge: (existing, ctx) => {
996
+ const result = { ...existing };
997
+ if (result.semi === void 0) result.semi = true;
998
+ if (result.singleQuote === void 0) result.singleQuote = true;
999
+ if (result.tabWidth === void 0) result.tabWidth = 2;
1000
+ if (result.trailingComma === void 0) result.trailingComma = "all";
1001
+ if (result.printWidth === void 0) result.printWidth = 100;
1002
+ if (result.endOfLine === void 0) result.endOfLine = "lf";
1003
+ if (result.useTabs === void 0) result.useTabs = false;
1004
+ if (result.bracketSpacing === void 0) result.bracketSpacing = true;
1005
+ if (result.arrowParens === void 0) result.arrowParens = "avoid";
1006
+ const plugins = [];
1007
+ if (ctx.projectType.astro) plugins.push("prettier-plugin-astro");
1008
+ if (ctx.projectType.shell) plugins.push("prettier-plugin-sh");
1009
+ if (ctx.projectType.tailwind) plugins.push("prettier-plugin-tailwindcss");
1010
+ if (plugins.length > 0) {
1011
+ result.plugins = plugins;
1012
+ } else {
1013
+ delete result.plugins;
1014
+ }
1015
+ return result;
1016
+ },
1017
+ unmerge: (existing) => {
1018
+ const result = { ...existing };
1019
+ delete result.plugins;
1020
+ return result;
1021
+ }
1069
1022
  }
1070
1023
  },
1071
1024
  // Text files where we patch specific content
@@ -1092,8 +1045,8 @@ var SAFEWORD_SCHEMA = {
1092
1045
  "prettier",
1093
1046
  // Safeword plugin (bundles eslint-config-prettier + all ESLint plugins)
1094
1047
  "eslint-plugin-safeword",
1095
- // Non-ESLint tools
1096
- "markdownlint-cli2",
1048
+ // Architecture and dead code tools (used by /audit)
1049
+ "dependency-cruiser",
1097
1050
  "knip"
1098
1051
  ],
1099
1052
  conditional: {
@@ -1108,28 +1061,28 @@ var SAFEWORD_SCHEMA = {
1108
1061
  };
1109
1062
 
1110
1063
  // src/utils/git.ts
1111
- import nodePath3 from "path";
1064
+ import nodePath2 from "path";
1112
1065
  function isGitRepo(cwd) {
1113
- return exists(nodePath3.join(cwd, ".git"));
1066
+ return exists(nodePath2.join(cwd, ".git"));
1114
1067
  }
1115
1068
 
1116
1069
  // src/utils/context.ts
1117
- import nodePath5 from "path";
1070
+ import nodePath4 from "path";
1118
1071
 
1119
1072
  // src/utils/project-detector.ts
1120
- import { readdirSync as readdirSync2 } from "fs";
1121
- import nodePath4 from "path";
1073
+ import { readdirSync } from "fs";
1074
+ import nodePath3 from "path";
1122
1075
  function hasShellScripts(cwd, maxDepth = 4) {
1123
1076
  const excludeDirectories = /* @__PURE__ */ new Set(["node_modules", ".git", ".safeword"]);
1124
1077
  function scan(dir, depth) {
1125
1078
  if (depth > maxDepth) return false;
1126
1079
  try {
1127
- const entries = readdirSync2(dir, { withFileTypes: true });
1080
+ const entries = readdirSync(dir, { withFileTypes: true });
1128
1081
  for (const entry of entries) {
1129
1082
  if (entry.isFile() && entry.name.endsWith(".sh")) {
1130
1083
  return true;
1131
1084
  }
1132
- if (entry.isDirectory() && !excludeDirectories.has(entry.name) && scan(nodePath4.join(dir, entry.name), depth + 1)) {
1085
+ if (entry.isDirectory() && !excludeDirectories.has(entry.name) && scan(nodePath3.join(dir, entry.name), depth + 1)) {
1133
1086
  return true;
1134
1087
  }
1135
1088
  }
@@ -1169,7 +1122,7 @@ function detectProjectType(packageJson, cwd) {
1169
1122
 
1170
1123
  // src/utils/context.ts
1171
1124
  function createProjectContext(cwd) {
1172
- const packageJson = readJson(nodePath5.join(cwd, "package.json"));
1125
+ const packageJson = readJson(nodePath4.join(cwd, "package.json"));
1173
1126
  return {
1174
1127
  cwd,
1175
1128
  projectType: detectProjectType(packageJson ?? {}, cwd),
@@ -1178,45 +1131,10 @@ function createProjectContext(cwd) {
1178
1131
  };
1179
1132
  }
1180
1133
 
1181
- // src/utils/output.ts
1182
- function info(message) {
1183
- console.log(message);
1184
- }
1185
- function success(message) {
1186
- console.log(`\u2713 ${message}`);
1187
- }
1188
- function warn(message) {
1189
- console.warn(`\u26A0 ${message}`);
1190
- }
1191
- function error(message) {
1192
- console.error(`\u2717 ${message}`);
1193
- }
1194
- function header(title) {
1195
- console.log(`
1196
- ${title}`);
1197
- console.log("\u2500".repeat(title.length));
1198
- }
1199
- function listItem(item, indent = 2) {
1200
- console.log(`${" ".repeat(indent)}\u2022 ${item}`);
1201
- }
1202
- function keyValue(key, value) {
1203
- console.log(` ${key}: ${value}`);
1204
- }
1205
-
1206
1134
  export {
1207
- exists,
1208
- readFileSafe,
1209
- writeJson,
1210
1135
  reconcile,
1211
1136
  SAFEWORD_SCHEMA,
1212
1137
  isGitRepo,
1213
- createProjectContext,
1214
- info,
1215
- success,
1216
- warn,
1217
- error,
1218
- header,
1219
- listItem,
1220
- keyValue
1138
+ createProjectContext
1221
1139
  };
1222
- //# sourceMappingURL=chunk-UUKED7KU.js.map
1140
+ //# sourceMappingURL=chunk-KL2JTWK6.js.map