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.
- package/dist/{check-MLYBKA5Z.js → check-EQ3IJPBM.js} +8 -6
- package/dist/{check-MLYBKA5Z.js.map → check-EQ3IJPBM.js.map} +1 -1
- package/dist/chunk-DYLHQBW3.js +132 -0
- package/dist/chunk-DYLHQBW3.js.map +1 -0
- package/dist/{chunk-UUKED7KU.js → chunk-KL2JTWK6.js} +110 -192
- package/dist/chunk-KL2JTWK6.js.map +1 -0
- package/dist/chunk-NDY7IUE7.js +229 -0
- package/dist/chunk-NDY7IUE7.js.map +1 -0
- package/dist/cli.js +9 -5
- package/dist/cli.js.map +1 -1
- package/dist/{diff-Q44YDA2W.js → diff-6EDSOICP.js} +8 -6
- package/dist/{diff-Q44YDA2W.js.map → diff-6EDSOICP.js.map} +1 -1
- package/dist/{reset-EEFVJ5GB.js → reset-HJBALSYG.js} +11 -8
- package/dist/reset-HJBALSYG.js.map +1 -0
- package/dist/{setup-WZ3F25KX.js → setup-SOWUS7ZI.js} +41 -13
- package/dist/setup-SOWUS7ZI.js.map +1 -0
- package/dist/sync-config-XTMQBCIH.js +14 -0
- package/dist/sync-config-XTMQBCIH.js.map +1 -0
- package/dist/{upgrade-WB7N3GWG.js → upgrade-KJLOX4DD.js} +8 -6
- package/dist/{upgrade-WB7N3GWG.js.map → upgrade-KJLOX4DD.js.map} +1 -1
- package/package.json +2 -2
- package/templates/commands/audit.md +41 -0
- package/templates/commands/lint.md +1 -4
- package/templates/hooks/cursor/after-file-edit.sh +1 -9
- package/templates/hooks/post-tool-lint.sh +1 -9
- package/templates/skills/safeword-debugging/SKILL.md +15 -15
- package/dist/chunk-UUKED7KU.js.map +0 -1
- package/dist/reset-EEFVJ5GB.js.map +0 -1
- package/dist/setup-WZ3F25KX.js.map +0 -1
- package/templates/markdownlint-cli2.jsonc +0 -24
- 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
//
|
|
1096
|
-
"
|
|
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
|
|
1064
|
+
import nodePath2 from "path";
|
|
1112
1065
|
function isGitRepo(cwd) {
|
|
1113
|
-
return exists(
|
|
1066
|
+
return exists(nodePath2.join(cwd, ".git"));
|
|
1114
1067
|
}
|
|
1115
1068
|
|
|
1116
1069
|
// src/utils/context.ts
|
|
1117
|
-
import
|
|
1070
|
+
import nodePath4 from "path";
|
|
1118
1071
|
|
|
1119
1072
|
// src/utils/project-detector.ts
|
|
1120
|
-
import { readdirSync
|
|
1121
|
-
import
|
|
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 =
|
|
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(
|
|
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(
|
|
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-
|
|
1140
|
+
//# sourceMappingURL=chunk-KL2JTWK6.js.map
|