safeword 0.15.13 → 0.16.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-CUCQRUXT.js → check-G4DWNLAO.js} +3 -3
  2. package/dist/{chunk-SO45XRNO.js → chunk-GW6KXWLO.js} +116 -32
  3. package/dist/chunk-GW6KXWLO.js.map +1 -0
  4. package/dist/chunk-SARDBWBX.js +26 -0
  5. package/dist/chunk-SARDBWBX.js.map +1 -0
  6. package/dist/chunk-T55FUI2W.js +266 -0
  7. package/dist/chunk-T55FUI2W.js.map +1 -0
  8. package/dist/{chunk-NXSSFAFS.js → chunk-ULU3SP5Q.js} +40 -2
  9. package/dist/{chunk-NXSSFAFS.js.map → chunk-ULU3SP5Q.js.map} +1 -1
  10. package/dist/cli.js +5 -5
  11. package/dist/{diff-MF42ROSX.js → diff-OQHQHUQQ.js} +2 -2
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.js +1 -1
  14. package/dist/presets/typescript/index.js +1 -1
  15. package/dist/{reset-GOAEEMD6.js → reset-F7ZJ2BYG.js} +2 -2
  16. package/dist/{setup-TISRKM2F.js → setup-HUQUSWCM.js} +26 -9
  17. package/dist/setup-HUQUSWCM.js.map +1 -0
  18. package/dist/{upgrade-MI34U4M6.js → upgrade-E4WVNVGB.js} +7 -23
  19. package/dist/upgrade-E4WVNVGB.js.map +1 -0
  20. package/package.json +24 -5
  21. package/templates/SAFEWORD.md +12 -10
  22. package/templates/guides/cli-reference.md +1 -1
  23. package/templates/hooks/lib/lint.ts +71 -1
  24. package/dist/chunk-SO45XRNO.js.map +0 -1
  25. package/dist/chunk-UCZB27KH.js +0 -124
  26. package/dist/chunk-UCZB27KH.js.map +0 -1
  27. package/dist/setup-TISRKM2F.js.map +0 -1
  28. package/dist/upgrade-MI34U4M6.js.map +0 -1
  29. /package/dist/{check-CUCQRUXT.js.map → check-G4DWNLAO.js.map} +0 -0
  30. /package/dist/{diff-MF42ROSX.js.map → diff-OQHQHUQQ.js.map} +0 -0
  31. /package/dist/{reset-GOAEEMD6.js.map → reset-F7ZJ2BYG.js.map} +0 -0
@@ -3,12 +3,12 @@ import {
3
3
  } from "./chunk-FJYRWU2V.js";
4
4
  import {
5
5
  getMissingPacks
6
- } from "./chunk-UCZB27KH.js";
6
+ } from "./chunk-T55FUI2W.js";
7
7
  import {
8
8
  SAFEWORD_SCHEMA,
9
9
  createProjectContext,
10
10
  reconcile
11
- } from "./chunk-SO45XRNO.js";
11
+ } from "./chunk-GW6KXWLO.js";
12
12
  import "./chunk-PMYWVDOF.js";
13
13
  import {
14
14
  VERSION
@@ -189,4 +189,4 @@ async function check(options) {
189
189
  export {
190
190
  check
191
191
  };
192
- //# sourceMappingURL=check-CUCQRUXT.js.map
192
+ //# sourceMappingURL=check-G4DWNLAO.js.map
@@ -51,7 +51,7 @@ function detectRootPackage(cwd) {
51
51
  const content = readFileSafe(pyprojectPath);
52
52
  if (content) {
53
53
  const nameMatch = /^name\s*=\s*"([^"]+)"/m.exec(content);
54
- if (nameMatch) {
54
+ if (nameMatch?.[1]) {
55
55
  return nameMatch[1].replaceAll("-", "_");
56
56
  }
57
57
  }
@@ -524,14 +524,18 @@ function computePackagesToInstall(schema, projectType, installedDevelopmentDeps)
524
524
  ...schema.packages.base,
525
525
  ...getConditionalPackages(schema.packages.conditional, projectType)
526
526
  ];
527
- return needed.filter((pkg) => !(pkg in installedDevelopmentDeps));
527
+ return needed.filter((pkg) => !(stripVersionSpecifier(pkg) in installedDevelopmentDeps));
528
528
  }
529
529
  function computePackagesToRemove(schema, projectType, installedDevelopmentDeps) {
530
530
  const safewordPackages = [
531
531
  ...schema.packages.base,
532
532
  ...getConditionalPackages(schema.packages.conditional, projectType)
533
533
  ];
534
- return safewordPackages.filter((pkg) => pkg in installedDevelopmentDeps);
534
+ return safewordPackages.filter((pkg) => stripVersionSpecifier(pkg) in installedDevelopmentDeps);
535
+ }
536
+ function stripVersionSpecifier(pkg) {
537
+ const atIndex = pkg.startsWith("@") ? pkg.indexOf("@", 1) : pkg.indexOf("@");
538
+ return atIndex === -1 ? pkg : pkg.slice(0, atIndex);
535
539
  }
536
540
  function executeJsonMerge(cwd, path2, definition, ctx) {
537
541
  const fullPath = nodePath2.join(cwd, path2);
@@ -644,7 +648,7 @@ var MCP_SERVERS = {
644
648
  };
645
649
 
646
650
  // src/packs/golang/files.ts
647
- import { existsSync as existsSync2, readFileSync } from "fs";
651
+ import { readFileSync } from "fs";
648
652
  import nodePath3 from "path";
649
653
  import YAML from "yaml";
650
654
  var GOLANGCI_CONFIG_CORE = `version: "2"
@@ -677,7 +681,7 @@ function generateSafewordGolangciConfig(existingConfig, cwd) {
677
681
  return getSafewordGolangciStandalone();
678
682
  }
679
683
  const configPath = nodePath3.join(cwd, existingConfig);
680
- if (!existsSync2(configPath)) {
684
+ if (!exists(configPath)) {
681
685
  return getSafewordGolangciStandalone();
682
686
  }
683
687
  try {
@@ -878,6 +882,49 @@ var pythonManagedFiles = {
878
882
  }
879
883
  };
880
884
 
885
+ // src/packs/rust/files.ts
886
+ function generateClippyConfig() {
887
+ return `# Generated by safeword - DO NOT EDIT
888
+ # Configure lint levels in Cargo.toml [lints.clippy] section
889
+
890
+ # Complexity constraints (match ESLint/ruff philosophy)
891
+ cognitive-complexity-threshold = 10
892
+ too-many-arguments-threshold = 5
893
+
894
+ # Stricter thresholds for LLM code
895
+ too-many-lines-threshold = 100
896
+ type-complexity-threshold = 250
897
+ `;
898
+ }
899
+ function generateRustfmtConfig() {
900
+ return `# Generated by safeword - DO NOT EDIT
901
+ # Uses only stable options (works on all Rust toolchains)
902
+
903
+ edition = "2021"
904
+ max_width = 100
905
+ tab_spaces = 4
906
+ newline_style = "Unix"
907
+ reorder_imports = true
908
+ use_field_init_shorthand = true
909
+ `;
910
+ }
911
+ var rustOwnedFiles = {
912
+ ".safeword/clippy.toml": {
913
+ generator: (ctx) => ctx.languages?.rust ? generateClippyConfig() : void 0
914
+ },
915
+ ".safeword/rustfmt.toml": {
916
+ generator: (ctx) => ctx.languages?.rust ? generateRustfmtConfig() : void 0
917
+ }
918
+ };
919
+ var rustManagedFiles = {
920
+ "clippy.toml": {
921
+ generator: (ctx) => ctx.languages?.rust && !ctx.projectType.existingClippyConfig ? generateClippyConfig() : void 0
922
+ },
923
+ "rustfmt.toml": {
924
+ generator: (ctx) => ctx.languages?.rust && !ctx.projectType.existingRustfmtConfig ? generateRustfmtConfig() : void 0
925
+ }
926
+ };
927
+
881
928
  // src/templates/config.ts
882
929
  function getPrettierConfig(hasExistingFormatter2) {
883
930
  if (hasExistingFormatter2) {
@@ -1103,8 +1150,8 @@ ${prettier.configEntry}
1103
1150
  `;
1104
1151
  }
1105
1152
  var CURSOR_HOOKS = {
1106
- afterFileEdit: [{ command: "bun ../.safeword/hooks/cursor/after-file-edit.ts" }],
1107
- stop: [{ command: "bun ../.safeword/hooks/cursor/stop.ts" }]
1153
+ afterFileEdit: [{ command: "bun ./.safeword/hooks/cursor/after-file-edit.ts" }],
1154
+ stop: [{ command: "bun ./.safeword/hooks/cursor/stop.ts" }]
1108
1155
  };
1109
1156
  var SETTINGS_HOOKS = {
1110
1157
  SessionStart: [
@@ -1208,7 +1255,7 @@ function addKnipIgnoreDependencies(config, ctx) {
1208
1255
  }
1209
1256
  function getKnipConfig(ctx) {
1210
1257
  const config = {
1211
- ignore: [".safeword/**"],
1258
+ ignore: [".safeword/**", ".safeword-project/**"],
1212
1259
  ignoreDependencies: ["safeword", "dependency-cruiser"]
1213
1260
  };
1214
1261
  addKnipIgnores(config, ctx);
@@ -1461,10 +1508,11 @@ var typescriptJsonMerges = {
1461
1508
  "biome.json": BIOME_JSON_MERGE,
1462
1509
  "biome.jsonc": BIOME_JSON_MERGE
1463
1510
  };
1511
+ var ESLINT_PACKAGE = "eslint@^9";
1464
1512
  var typescriptPackages = {
1465
1513
  base: [
1466
1514
  // Core tools (always needed for JS/TS)
1467
- "eslint",
1515
+ ESLINT_PACKAGE,
1468
1516
  // Safeword (bundles eslint-config-prettier + all ESLint plugins)
1469
1517
  "safeword",
1470
1518
  // Architecture and dead code tools (used by /audit)
@@ -1556,7 +1604,7 @@ var SAFEWORD_SCHEMA = {
1556
1604
  sharedDirs: [".claude", ".claude/skills", ".claude/commands"],
1557
1605
  // Created on setup but NOT deleted on reset (preserves user data)
1558
1606
  preservedDirs: [
1559
- ".safeword/learnings",
1607
+ ".safeword-project/learnings",
1560
1608
  ".safeword/logs",
1561
1609
  ".safeword-project/tickets",
1562
1610
  ".safeword-project/tickets/completed",
@@ -1660,6 +1708,7 @@ var SAFEWORD_SCHEMA = {
1660
1708
  ...typescriptOwnedFiles,
1661
1709
  ...pythonOwnedFiles,
1662
1710
  ...golangOwnedFiles,
1711
+ ...rustOwnedFiles,
1663
1712
  // Hooks shared library - TypeScript with Bun runtime
1664
1713
  ".safeword/hooks/lib/lint.ts": { template: "hooks/lib/lint.ts" },
1665
1714
  ".safeword/hooks/lib/quality.ts": { template: "hooks/lib/quality.ts" },
@@ -1826,7 +1875,7 @@ var SAFEWORD_SCHEMA = {
1826
1875
  ".cursor/rules/bdd-splitting.mdc": {
1827
1876
  template: "cursor/rules/bdd-splitting.mdc"
1828
1877
  },
1829
- // Cursor commands (8 files - same as Claude)
1878
+ // Cursor commands (7 files - same as Claude)
1830
1879
  ".cursor/commands/bdd.md": { template: "commands/bdd.md" },
1831
1880
  ".cursor/commands/done.md": { template: "commands/done.md" },
1832
1881
  ".cursor/commands/audit.md": { template: "commands/audit.md" },
@@ -1851,7 +1900,9 @@ var SAFEWORD_SCHEMA = {
1851
1900
  // Python managed files (ruff.toml, mypy.ini, .importlinter)
1852
1901
  ...pythonManagedFiles,
1853
1902
  // Go managed files (.golangci.yml)
1854
- ...golangManagedFiles
1903
+ ...golangManagedFiles,
1904
+ // Rust managed files (clippy.toml, rustfmt.toml)
1905
+ ...rustManagedFiles
1855
1906
  },
1856
1907
  // JSON files where we merge specific keys
1857
1908
  jsonMerges: {
@@ -1941,7 +1992,7 @@ var SAFEWORD_SCHEMA = {
1941
1992
  };
1942
1993
 
1943
1994
  // src/utils/project-detector.ts
1944
- import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2 } from "fs";
1995
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
1945
1996
  import nodePath4 from "path";
1946
1997
  var {
1947
1998
  TAILWIND_PACKAGES,
@@ -1952,6 +2003,9 @@ var {
1952
2003
  var PYPROJECT_TOML = "pyproject.toml";
1953
2004
  var REQUIREMENTS_TXT = "requirements.txt";
1954
2005
  var GO_MOD = "go.mod";
2006
+ var CARGO_TOML = "Cargo.toml";
2007
+ var CLIPPY_CONFIG_FILES = ["clippy.toml", ".clippy.toml"];
2008
+ var RUSTFMT_CONFIG_FILES = ["rustfmt.toml", ".rustfmt.toml"];
1955
2009
  var ESLINT_CONFIG_FILES = [
1956
2010
  "eslint.config.mjs",
1957
2011
  "eslint.config.js",
@@ -1969,14 +2023,16 @@ var GOLANGCI_CONFIG_FILES = [
1969
2023
  ".golangci.json"
1970
2024
  ];
1971
2025
  function detectLanguages(cwd) {
1972
- const hasPackageJson = existsSync3(nodePath4.join(cwd, "package.json"));
1973
- const hasPyproject = existsSync3(nodePath4.join(cwd, PYPROJECT_TOML));
1974
- const hasRequirements = existsSync3(nodePath4.join(cwd, REQUIREMENTS_TXT));
1975
- const hasGoModule = existsSync3(nodePath4.join(cwd, GO_MOD));
2026
+ const hasPackageJson = existsSync2(nodePath4.join(cwd, "package.json"));
2027
+ const hasPyproject = existsSync2(nodePath4.join(cwd, PYPROJECT_TOML));
2028
+ const hasRequirements = existsSync2(nodePath4.join(cwd, REQUIREMENTS_TXT));
2029
+ const hasGoModule = existsSync2(nodePath4.join(cwd, GO_MOD));
2030
+ const hasCargoToml = existsSync2(nodePath4.join(cwd, CARGO_TOML));
1976
2031
  return {
1977
2032
  javascript: hasPackageJson,
1978
2033
  python: hasPyproject || hasRequirements,
1979
- golang: hasGoModule
2034
+ golang: hasGoModule,
2035
+ rust: hasCargoToml
1980
2036
  };
1981
2037
  }
1982
2038
  function hasShellScripts(cwd, maxDepth = 4) {
@@ -2001,16 +2057,16 @@ function hasShellScripts(cwd, maxDepth = 4) {
2001
2057
  }
2002
2058
  function findExistingEslintConfig(cwd) {
2003
2059
  for (const config of ESLINT_CONFIG_FILES) {
2004
- if (existsSync3(nodePath4.join(cwd, config))) {
2060
+ if (existsSync2(nodePath4.join(cwd, config))) {
2005
2061
  return config;
2006
2062
  }
2007
2063
  }
2008
2064
  return void 0;
2009
2065
  }
2010
2066
  function findExistingRuffConfig(cwd) {
2011
- if (existsSync3(nodePath4.join(cwd, "ruff.toml"))) return "ruff.toml";
2067
+ if (existsSync2(nodePath4.join(cwd, "ruff.toml"))) return "ruff.toml";
2012
2068
  const pyprojectPath = nodePath4.join(cwd, PYPROJECT_TOML);
2013
- if (!existsSync3(pyprojectPath)) return void 0;
2069
+ if (!existsSync2(pyprojectPath)) return void 0;
2014
2070
  try {
2015
2071
  const content = readFileSync2(pyprojectPath, "utf8");
2016
2072
  return content.includes("[tool.ruff]") ? "pyproject.toml" : void 0;
@@ -2019,10 +2075,10 @@ function findExistingRuffConfig(cwd) {
2019
2075
  }
2020
2076
  }
2021
2077
  function hasExistingMypyConfig(cwd) {
2022
- if (existsSync3(nodePath4.join(cwd, "mypy.ini"))) return true;
2023
- if (existsSync3(nodePath4.join(cwd, ".mypy.ini"))) return true;
2078
+ if (existsSync2(nodePath4.join(cwd, "mypy.ini"))) return true;
2079
+ if (existsSync2(nodePath4.join(cwd, ".mypy.ini"))) return true;
2024
2080
  const pyprojectPath = nodePath4.join(cwd, PYPROJECT_TOML);
2025
- if (!existsSync3(pyprojectPath)) return false;
2081
+ if (!existsSync2(pyprojectPath)) return false;
2026
2082
  try {
2027
2083
  const content = readFileSync2(pyprojectPath, "utf8");
2028
2084
  return content.includes("[tool.mypy]");
@@ -2031,9 +2087,9 @@ function hasExistingMypyConfig(cwd) {
2031
2087
  }
2032
2088
  }
2033
2089
  function hasExistingImportLinterConfig(cwd) {
2034
- if (existsSync3(nodePath4.join(cwd, ".importlinter"))) return true;
2090
+ if (existsSync2(nodePath4.join(cwd, ".importlinter"))) return true;
2035
2091
  const pyprojectPath = nodePath4.join(cwd, PYPROJECT_TOML);
2036
- if (!existsSync3(pyprojectPath)) return false;
2092
+ if (!existsSync2(pyprojectPath)) return false;
2037
2093
  try {
2038
2094
  const content = readFileSync2(pyprojectPath, "utf8");
2039
2095
  return content.includes("[tool.importlinter]");
@@ -2043,7 +2099,23 @@ function hasExistingImportLinterConfig(cwd) {
2043
2099
  }
2044
2100
  function findExistingGolangciConfig(cwd) {
2045
2101
  for (const config of GOLANGCI_CONFIG_FILES) {
2046
- if (existsSync3(nodePath4.join(cwd, config))) {
2102
+ if (existsSync2(nodePath4.join(cwd, config))) {
2103
+ return config;
2104
+ }
2105
+ }
2106
+ return void 0;
2107
+ }
2108
+ function findExistingClippyConfig(cwd) {
2109
+ for (const config of CLIPPY_CONFIG_FILES) {
2110
+ if (existsSync2(nodePath4.join(cwd, config))) {
2111
+ return config;
2112
+ }
2113
+ }
2114
+ return void 0;
2115
+ }
2116
+ function findExistingRustfmtConfig(cwd) {
2117
+ for (const config of RUSTFMT_CONFIG_FILES) {
2118
+ if (existsSync2(nodePath4.join(cwd, config))) {
2047
2119
  return config;
2048
2120
  }
2049
2121
  }
@@ -2066,7 +2138,7 @@ function detectPublishable(packageJson) {
2066
2138
  const hasEntryPoints = !!(packageJson.main || packageJson.module || packageJson.exports);
2067
2139
  return hasEntryPoints && packageJson.private !== true;
2068
2140
  }
2069
- function detectExistingTooling(cwd, scripts) {
2141
+ function detectCoreTooling(cwd, scripts) {
2070
2142
  const eslintConfig = cwd ? findExistingEslintConfig(cwd) : void 0;
2071
2143
  return {
2072
2144
  existingLinter: hasExistingLinter(scripts),
@@ -2075,8 +2147,20 @@ function detectExistingTooling(cwd, scripts) {
2075
2147
  legacyEslint: eslintConfig?.startsWith(".eslintrc") ?? false,
2076
2148
  existingRuffConfig: cwd ? findExistingRuffConfig(cwd) : void 0,
2077
2149
  existingMypyConfig: cwd ? hasExistingMypyConfig(cwd) : false,
2078
- existingImportLinterConfig: cwd ? hasExistingImportLinterConfig(cwd) : false,
2079
- existingGolangciConfig: cwd ? findExistingGolangciConfig(cwd) : void 0
2150
+ existingImportLinterConfig: cwd ? hasExistingImportLinterConfig(cwd) : false
2151
+ };
2152
+ }
2153
+ function detectSystemsTooling(cwd) {
2154
+ return {
2155
+ existingGolangciConfig: cwd ? findExistingGolangciConfig(cwd) : void 0,
2156
+ existingClippyConfig: cwd ? findExistingClippyConfig(cwd) : void 0,
2157
+ existingRustfmtConfig: cwd ? findExistingRustfmtConfig(cwd) : void 0
2158
+ };
2159
+ }
2160
+ function detectExistingTooling(cwd, scripts) {
2161
+ return {
2162
+ ...detectCoreTooling(cwd, scripts),
2163
+ ...detectSystemsTooling(cwd)
2080
2164
  };
2081
2165
  }
2082
2166
  function detectProjectType(packageJson, cwd) {
@@ -2115,6 +2199,7 @@ function createProjectContext(cwd) {
2115
2199
  }
2116
2200
 
2117
2201
  export {
2202
+ isGitRepo,
2118
2203
  detectPythonLayers,
2119
2204
  hasRuffDependency,
2120
2205
  detectPythonPackageManager,
@@ -2126,8 +2211,7 @@ export {
2126
2211
  getUninstallCommand,
2127
2212
  installDependencies,
2128
2213
  SAFEWORD_SCHEMA,
2129
- isGitRepo,
2130
2214
  detectLanguages,
2131
2215
  createProjectContext
2132
2216
  };
2133
- //# sourceMappingURL=chunk-SO45XRNO.js.map
2217
+ //# sourceMappingURL=chunk-GW6KXWLO.js.map