safeword 0.6.3 → 0.6.5
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-PECCGHEA.js → check-OYYSYHFP.js} +41 -23
- package/dist/check-OYYSYHFP.js.map +1 -0
- package/dist/chunk-LNSEDZIW.js +454 -0
- package/dist/chunk-LNSEDZIW.js.map +1 -0
- package/dist/chunk-ZS3Z3Q37.js +729 -0
- package/dist/chunk-ZS3Z3Q37.js.map +1 -0
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/diff-325TIZ63.js +168 -0
- package/dist/diff-325TIZ63.js.map +1 -0
- package/dist/reset-ZGJIKMUW.js +74 -0
- package/dist/reset-ZGJIKMUW.js.map +1 -0
- package/dist/setup-GAMXTFM2.js +103 -0
- package/dist/setup-GAMXTFM2.js.map +1 -0
- package/dist/{sync-4XBMKLXS.js → sync-BFMXZEHM.js} +33 -32
- package/dist/sync-BFMXZEHM.js.map +1 -0
- package/dist/upgrade-X4GREJXN.js +73 -0
- package/dist/upgrade-X4GREJXN.js.map +1 -0
- package/package.json +15 -14
- package/templates/SAFEWORD.md +101 -689
- package/templates/guides/architecture-guide.md +1 -1
- package/templates/guides/cli-reference.md +35 -0
- package/templates/guides/code-philosophy.md +22 -19
- package/templates/guides/context-files-guide.md +2 -2
- package/templates/guides/data-architecture-guide.md +1 -1
- package/templates/guides/design-doc-guide.md +1 -1
- package/templates/guides/{testing-methodology.md → development-workflow.md} +1 -1
- package/templates/guides/learning-extraction.md +1 -1
- package/templates/guides/{llm-instruction-design.md → llm-guide.md} +93 -29
- package/templates/guides/tdd-best-practices.md +2 -2
- package/templates/guides/test-definitions-guide.md +1 -1
- package/templates/guides/user-story-guide.md +1 -1
- package/templates/guides/zombie-process-cleanup.md +1 -1
- package/dist/check-PECCGHEA.js.map +0 -1
- package/dist/chunk-6CVTH67L.js +0 -43
- package/dist/chunk-6CVTH67L.js.map +0 -1
- package/dist/chunk-75FKNZUM.js +0 -15
- package/dist/chunk-75FKNZUM.js.map +0 -1
- package/dist/chunk-ARIAOK2F.js +0 -110
- package/dist/chunk-ARIAOK2F.js.map +0 -1
- package/dist/chunk-FRPJITGG.js +0 -35
- package/dist/chunk-FRPJITGG.js.map +0 -1
- package/dist/chunk-IWWBZVHT.js +0 -274
- package/dist/chunk-IWWBZVHT.js.map +0 -1
- package/dist/diff-ZACVJKOU.js +0 -171
- package/dist/diff-ZACVJKOU.js.map +0 -1
- package/dist/reset-5SRM3P6J.js +0 -145
- package/dist/reset-5SRM3P6J.js.map +0 -1
- package/dist/setup-65EVU5OT.js +0 -437
- package/dist/setup-65EVU5OT.js.map +0 -1
- package/dist/sync-4XBMKLXS.js.map +0 -1
- package/dist/upgrade-P3WX3ODU.js +0 -153
- package/dist/upgrade-P3WX3ODU.js.map +0 -1
- package/templates/guides/llm-prompting.md +0 -102
- /package/templates/prompts/{review.md → quality-review.md} +0 -0
package/dist/reset-5SRM3P6J.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
filterOutSafewordHooks,
|
|
3
|
-
removeAgentsMdLink
|
|
4
|
-
} from "./chunk-IWWBZVHT.js";
|
|
5
|
-
import {
|
|
6
|
-
error,
|
|
7
|
-
header,
|
|
8
|
-
info,
|
|
9
|
-
listItem,
|
|
10
|
-
success
|
|
11
|
-
} from "./chunk-FRPJITGG.js";
|
|
12
|
-
import {
|
|
13
|
-
exists,
|
|
14
|
-
listDir,
|
|
15
|
-
readJson,
|
|
16
|
-
remove,
|
|
17
|
-
writeJson
|
|
18
|
-
} from "./chunk-ARIAOK2F.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 = ["review.md", "architecture.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
|
-
const huskyDir = join(cwd, ".husky");
|
|
112
|
-
if (exists(huskyDir)) {
|
|
113
|
-
info("\nRemoving Husky hooks...");
|
|
114
|
-
remove(huskyDir);
|
|
115
|
-
removed.push(".husky/");
|
|
116
|
-
success("Removed Husky hooks");
|
|
117
|
-
}
|
|
118
|
-
info("\nCleaning AGENTS.md...");
|
|
119
|
-
if (removeAgentsMdLink(cwd)) {
|
|
120
|
-
removed.push("AGENTS.md (link)");
|
|
121
|
-
success("Removed safeword link from AGENTS.md");
|
|
122
|
-
}
|
|
123
|
-
header("Reset Complete");
|
|
124
|
-
if (removed.length > 0) {
|
|
125
|
-
info("\nRemoved:");
|
|
126
|
-
for (const item of removed) {
|
|
127
|
-
listItem(item);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
info("\nPreserved (remove manually if desired):");
|
|
131
|
-
listItem("eslint.config.mjs");
|
|
132
|
-
listItem(".prettierrc");
|
|
133
|
-
listItem(".markdownlint-cli2.jsonc");
|
|
134
|
-
listItem("package.json (scripts, lint-staged config)");
|
|
135
|
-
listItem("devDependencies (eslint, prettier, husky, lint-staged, etc.)");
|
|
136
|
-
success("\nSafeword configuration removed");
|
|
137
|
-
} catch (err) {
|
|
138
|
-
error(`Reset failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
export {
|
|
143
|
-
reset
|
|
144
|
-
};
|
|
145
|
-
//# sourceMappingURL=reset-5SRM3P6J.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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 { 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 = ['review.md', 'architecture.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 Husky directory\n const huskyDir = join(cwd, '.husky');\n if (exists(huskyDir)) {\n info('\\nRemoving Husky hooks...');\n remove(huskyDir);\n removed.push('.husky/');\n success('Removed Husky hooks');\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('.markdownlint-cli2.jsonc');\n listItem('package.json (scripts, lint-staged config)');\n listItem('devDependencies (eslint, prettier, husky, lint-staged, etc.)');\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":[]}
|
package/dist/setup-65EVU5OT.js
DELETED
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isGitRepo
|
|
3
|
-
} from "./chunk-75FKNZUM.js";
|
|
4
|
-
import {
|
|
5
|
-
VERSION
|
|
6
|
-
} from "./chunk-ORQHKDT2.js";
|
|
7
|
-
import {
|
|
8
|
-
LINT_STAGED_CONFIG,
|
|
9
|
-
PRETTIERRC,
|
|
10
|
-
SETTINGS_HOOKS,
|
|
11
|
-
ensureAgentsMdLink,
|
|
12
|
-
filterOutSafewordHooks,
|
|
13
|
-
getEslintConfig
|
|
14
|
-
} from "./chunk-IWWBZVHT.js";
|
|
15
|
-
import {
|
|
16
|
-
error,
|
|
17
|
-
header,
|
|
18
|
-
info,
|
|
19
|
-
listItem,
|
|
20
|
-
success,
|
|
21
|
-
warn
|
|
22
|
-
} from "./chunk-FRPJITGG.js";
|
|
23
|
-
import {
|
|
24
|
-
detectProjectType
|
|
25
|
-
} from "./chunk-6CVTH67L.js";
|
|
26
|
-
import {
|
|
27
|
-
copyDir,
|
|
28
|
-
copyFile,
|
|
29
|
-
ensureDir,
|
|
30
|
-
exists,
|
|
31
|
-
getTemplatesDir,
|
|
32
|
-
makeScriptsExecutable,
|
|
33
|
-
readJson,
|
|
34
|
-
updateJson,
|
|
35
|
-
writeFile,
|
|
36
|
-
writeJson
|
|
37
|
-
} from "./chunk-ARIAOK2F.js";
|
|
38
|
-
|
|
39
|
-
// src/commands/setup.ts
|
|
40
|
-
import { join as join2, basename } from "path";
|
|
41
|
-
|
|
42
|
-
// src/utils/boundaries.ts
|
|
43
|
-
import { join } from "path";
|
|
44
|
-
var ARCHITECTURE_DIRS = [
|
|
45
|
-
"types",
|
|
46
|
-
// Bottom: can be imported by everything
|
|
47
|
-
"utils",
|
|
48
|
-
"lib",
|
|
49
|
-
"hooks",
|
|
50
|
-
"services",
|
|
51
|
-
"components",
|
|
52
|
-
"features",
|
|
53
|
-
"modules",
|
|
54
|
-
"app"
|
|
55
|
-
// Top: can import everything
|
|
56
|
-
];
|
|
57
|
-
var HIERARCHY = {
|
|
58
|
-
types: [],
|
|
59
|
-
// types can't import anything (pure type definitions)
|
|
60
|
-
utils: ["types"],
|
|
61
|
-
lib: ["utils", "types"],
|
|
62
|
-
hooks: ["lib", "utils", "types"],
|
|
63
|
-
services: ["lib", "utils", "types"],
|
|
64
|
-
components: ["hooks", "services", "lib", "utils", "types"],
|
|
65
|
-
features: ["components", "hooks", "services", "lib", "utils", "types"],
|
|
66
|
-
modules: ["components", "hooks", "services", "lib", "utils", "types"],
|
|
67
|
-
app: ["features", "modules", "components", "hooks", "services", "lib", "utils", "types"]
|
|
68
|
-
};
|
|
69
|
-
function detectArchitecture(projectDir) {
|
|
70
|
-
const foundInSrc = [];
|
|
71
|
-
const foundAtRoot = [];
|
|
72
|
-
for (const dir of ARCHITECTURE_DIRS) {
|
|
73
|
-
if (exists(join(projectDir, "src", dir))) {
|
|
74
|
-
foundInSrc.push(dir);
|
|
75
|
-
}
|
|
76
|
-
if (exists(join(projectDir, dir))) {
|
|
77
|
-
foundAtRoot.push(dir);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
const inSrc = foundInSrc.length >= foundAtRoot.length;
|
|
81
|
-
const found = inSrc ? foundInSrc : foundAtRoot;
|
|
82
|
-
return { directories: found, inSrc };
|
|
83
|
-
}
|
|
84
|
-
function generateBoundariesConfig(arch) {
|
|
85
|
-
const prefix = arch.inSrc ? "src/" : "";
|
|
86
|
-
const hasDirectories = arch.directories.length > 0;
|
|
87
|
-
const elements = arch.directories.map((dir) => ` { type: '${dir}', pattern: '${prefix}${dir}/**', mode: 'full' }`).join(",\n");
|
|
88
|
-
const rules = arch.directories.filter((dir) => HIERARCHY[dir].length > 0).map((dir) => {
|
|
89
|
-
const allowed = HIERARCHY[dir].filter((dep) => arch.directories.includes(dep));
|
|
90
|
-
if (allowed.length === 0) return null;
|
|
91
|
-
return ` { from: ['${dir}'], allow: [${allowed.map((d) => `'${d}'`).join(", ")}] }`;
|
|
92
|
-
}).filter(Boolean).join(",\n");
|
|
93
|
-
const detectedInfo = hasDirectories ? `Detected directories: ${arch.directories.join(", ")} (${arch.inSrc ? "in src/" : "at root"})` : "No architecture directories detected yet - add types/, utils/, components/, etc.";
|
|
94
|
-
const elementsContent = elements || "";
|
|
95
|
-
const rulesContent = rules || "";
|
|
96
|
-
return `/**
|
|
97
|
-
* Architecture Boundaries Configuration (AUTO-GENERATED)
|
|
98
|
-
*
|
|
99
|
-
* ${detectedInfo}
|
|
100
|
-
*
|
|
101
|
-
* This enforces import boundaries between architectural layers:
|
|
102
|
-
* - Lower layers (types, utils) cannot import from higher layers (components, features)
|
|
103
|
-
* - Uses 'warn' severity - informative, not blocking
|
|
104
|
-
*
|
|
105
|
-
* Recognized directories (in hierarchy order):
|
|
106
|
-
* types \u2192 utils \u2192 lib \u2192 hooks/services \u2192 components \u2192 features/modules \u2192 app
|
|
107
|
-
*
|
|
108
|
-
* To customize, override in your eslint.config.mjs:
|
|
109
|
-
* rules: { 'boundaries/element-types': ['error', { ... }] }
|
|
110
|
-
*/
|
|
111
|
-
|
|
112
|
-
import boundaries from 'eslint-plugin-boundaries';
|
|
113
|
-
|
|
114
|
-
export default {
|
|
115
|
-
plugins: { boundaries },
|
|
116
|
-
settings: {
|
|
117
|
-
'boundaries/elements': [
|
|
118
|
-
${elementsContent}
|
|
119
|
-
],
|
|
120
|
-
},
|
|
121
|
-
rules: {
|
|
122
|
-
'boundaries/element-types': ['warn', {
|
|
123
|
-
default: 'disallow',
|
|
124
|
-
rules: [
|
|
125
|
-
${rulesContent}
|
|
126
|
-
],
|
|
127
|
-
}],
|
|
128
|
-
'boundaries/no-unknown': 'off', // Allow files outside defined elements
|
|
129
|
-
'boundaries/no-unknown-files': 'off', // Allow non-matching files
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// src/commands/setup.ts
|
|
136
|
-
import { execSync } from "child_process";
|
|
137
|
-
async function setup(options) {
|
|
138
|
-
const cwd = process.cwd();
|
|
139
|
-
const safewordDir = join2(cwd, ".safeword");
|
|
140
|
-
if (exists(safewordDir)) {
|
|
141
|
-
error("Already configured. Run `safeword upgrade` to update.");
|
|
142
|
-
process.exit(1);
|
|
143
|
-
}
|
|
144
|
-
const packageJsonPath = join2(cwd, "package.json");
|
|
145
|
-
let packageJsonCreated = false;
|
|
146
|
-
if (!exists(packageJsonPath)) {
|
|
147
|
-
const dirName = basename(cwd) || "project";
|
|
148
|
-
const defaultPackageJson = {
|
|
149
|
-
name: dirName,
|
|
150
|
-
version: "0.1.0",
|
|
151
|
-
scripts: {}
|
|
152
|
-
};
|
|
153
|
-
writeJson(packageJsonPath, defaultPackageJson);
|
|
154
|
-
packageJsonCreated = true;
|
|
155
|
-
}
|
|
156
|
-
const isNonInteractive = options.yes || !process.stdin.isTTY;
|
|
157
|
-
header("Safeword Setup");
|
|
158
|
-
info(`Version: ${VERSION}`);
|
|
159
|
-
if (packageJsonCreated) {
|
|
160
|
-
info("Created package.json (none found)");
|
|
161
|
-
}
|
|
162
|
-
const created = packageJsonCreated ? ["package.json"] : [];
|
|
163
|
-
const modified = [];
|
|
164
|
-
try {
|
|
165
|
-
const templatesDir = getTemplatesDir();
|
|
166
|
-
info("\nCreating .safeword directory...");
|
|
167
|
-
ensureDir(safewordDir);
|
|
168
|
-
ensureDir(join2(safewordDir, "learnings"));
|
|
169
|
-
ensureDir(join2(safewordDir, "planning", "user-stories"));
|
|
170
|
-
ensureDir(join2(safewordDir, "planning", "design"));
|
|
171
|
-
ensureDir(join2(safewordDir, "tickets", "completed"));
|
|
172
|
-
copyFile(join2(templatesDir, "SAFEWORD.md"), join2(safewordDir, "SAFEWORD.md"));
|
|
173
|
-
writeFile(join2(safewordDir, "version"), VERSION);
|
|
174
|
-
copyDir(join2(templatesDir, "guides"), join2(safewordDir, "guides"));
|
|
175
|
-
copyDir(join2(templatesDir, "doc-templates"), join2(safewordDir, "templates"));
|
|
176
|
-
copyDir(join2(templatesDir, "prompts"), join2(safewordDir, "prompts"));
|
|
177
|
-
copyDir(join2(templatesDir, "lib"), join2(safewordDir, "lib"));
|
|
178
|
-
makeScriptsExecutable(join2(safewordDir, "lib"));
|
|
179
|
-
copyDir(join2(templatesDir, "hooks"), join2(safewordDir, "hooks"));
|
|
180
|
-
makeScriptsExecutable(join2(safewordDir, "hooks"));
|
|
181
|
-
created.push(".safeword/");
|
|
182
|
-
success("Created .safeword directory");
|
|
183
|
-
info("\nConfiguring AGENTS.md...");
|
|
184
|
-
const agentsMdResult = ensureAgentsMdLink(cwd);
|
|
185
|
-
if (agentsMdResult === "created") {
|
|
186
|
-
created.push("AGENTS.md");
|
|
187
|
-
success("Created AGENTS.md");
|
|
188
|
-
} else if (agentsMdResult === "modified") {
|
|
189
|
-
modified.push("AGENTS.md");
|
|
190
|
-
success("Prepended link to AGENTS.md");
|
|
191
|
-
} else {
|
|
192
|
-
info("AGENTS.md already has safeword link");
|
|
193
|
-
}
|
|
194
|
-
info("\nRegistering Claude Code hooks...");
|
|
195
|
-
const claudeDir = join2(cwd, ".claude");
|
|
196
|
-
const settingsPath = join2(claudeDir, "settings.json");
|
|
197
|
-
ensureDir(claudeDir);
|
|
198
|
-
try {
|
|
199
|
-
updateJson(settingsPath, (existing) => {
|
|
200
|
-
const hooks = existing?.hooks ?? {};
|
|
201
|
-
for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {
|
|
202
|
-
const existingHooks = hooks[event] ?? [];
|
|
203
|
-
const nonSafewordHooks = filterOutSafewordHooks(existingHooks);
|
|
204
|
-
hooks[event] = [...nonSafewordHooks, ...newHooks];
|
|
205
|
-
}
|
|
206
|
-
return { ...existing, hooks };
|
|
207
|
-
});
|
|
208
|
-
if (exists(settingsPath)) {
|
|
209
|
-
modified.push(".claude/settings.json");
|
|
210
|
-
} else {
|
|
211
|
-
created.push(".claude/settings.json");
|
|
212
|
-
}
|
|
213
|
-
success("Registered hooks in .claude/settings.json");
|
|
214
|
-
} catch (err) {
|
|
215
|
-
error(`Failed to register hooks: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
info("\nInstalling skills...");
|
|
219
|
-
const skillsDir = join2(claudeDir, "skills");
|
|
220
|
-
copyDir(join2(templatesDir, "skills"), skillsDir);
|
|
221
|
-
created.push(".claude/skills/safeword-quality-reviewer/");
|
|
222
|
-
success("Installed skills");
|
|
223
|
-
info("\nInstalling slash commands...");
|
|
224
|
-
const commandsDir = join2(claudeDir, "commands");
|
|
225
|
-
copyDir(join2(templatesDir, "commands"), commandsDir);
|
|
226
|
-
created.push(".claude/commands/");
|
|
227
|
-
success("Installed slash commands");
|
|
228
|
-
info("\nConfiguring MCP servers...");
|
|
229
|
-
const mcpConfigPath = join2(cwd, ".mcp.json");
|
|
230
|
-
updateJson(mcpConfigPath, (existing) => {
|
|
231
|
-
const mcpServers = existing?.mcpServers ?? {};
|
|
232
|
-
mcpServers.context7 = {
|
|
233
|
-
command: "npx",
|
|
234
|
-
args: ["-y", "@upstash/context7-mcp@latest"]
|
|
235
|
-
};
|
|
236
|
-
mcpServers.playwright = {
|
|
237
|
-
command: "npx",
|
|
238
|
-
args: ["@playwright/mcp@latest"]
|
|
239
|
-
};
|
|
240
|
-
return { ...existing, mcpServers };
|
|
241
|
-
});
|
|
242
|
-
if (exists(mcpConfigPath)) {
|
|
243
|
-
modified.push(".mcp.json");
|
|
244
|
-
} else {
|
|
245
|
-
created.push(".mcp.json");
|
|
246
|
-
}
|
|
247
|
-
success("Configured MCP servers");
|
|
248
|
-
info("\nConfiguring linting...");
|
|
249
|
-
const packageJson = readJson(packageJsonPath);
|
|
250
|
-
if (!packageJson) {
|
|
251
|
-
error("Failed to read package.json");
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
const projectType = detectProjectType(packageJson);
|
|
255
|
-
const architecture = detectArchitecture(cwd);
|
|
256
|
-
const eslintConfigPath = join2(cwd, "eslint.config.mjs");
|
|
257
|
-
if (!exists(eslintConfigPath)) {
|
|
258
|
-
writeFile(eslintConfigPath, getEslintConfig({ boundaries: true }));
|
|
259
|
-
created.push("eslint.config.mjs");
|
|
260
|
-
success("Created eslint.config.mjs (dynamic - adapts to framework changes)");
|
|
261
|
-
} else {
|
|
262
|
-
info("eslint.config.mjs already exists");
|
|
263
|
-
}
|
|
264
|
-
const boundariesConfigPath = join2(safewordDir, "eslint-boundaries.config.mjs");
|
|
265
|
-
writeFile(boundariesConfigPath, generateBoundariesConfig(architecture));
|
|
266
|
-
if (architecture.directories.length > 0) {
|
|
267
|
-
info(`Detected architecture: ${architecture.directories.join(", ")} (${architecture.inSrc ? "in src/" : "at root"})`);
|
|
268
|
-
} else {
|
|
269
|
-
info("No architecture directories detected yet (boundaries ready when you add them)");
|
|
270
|
-
}
|
|
271
|
-
success("Created .safeword/eslint-boundaries.config.mjs");
|
|
272
|
-
const prettierrcPath = join2(cwd, ".prettierrc");
|
|
273
|
-
if (!exists(prettierrcPath)) {
|
|
274
|
-
writeFile(prettierrcPath, PRETTIERRC);
|
|
275
|
-
created.push(".prettierrc");
|
|
276
|
-
success("Created .prettierrc");
|
|
277
|
-
} else {
|
|
278
|
-
info(".prettierrc already exists");
|
|
279
|
-
}
|
|
280
|
-
const markdownlintPath = join2(cwd, ".markdownlint-cli2.jsonc");
|
|
281
|
-
if (!exists(markdownlintPath)) {
|
|
282
|
-
copyFile(join2(templatesDir, "markdownlint-cli2.jsonc"), markdownlintPath);
|
|
283
|
-
created.push(".markdownlint-cli2.jsonc");
|
|
284
|
-
success("Created .markdownlint-cli2.jsonc");
|
|
285
|
-
} else {
|
|
286
|
-
info(".markdownlint-cli2.jsonc already exists");
|
|
287
|
-
}
|
|
288
|
-
try {
|
|
289
|
-
const scripts = packageJson.scripts ?? {};
|
|
290
|
-
let packageJsonModified = false;
|
|
291
|
-
if (!scripts.lint) {
|
|
292
|
-
scripts.lint = "eslint .";
|
|
293
|
-
packageJsonModified = true;
|
|
294
|
-
}
|
|
295
|
-
if (!scripts["lint:md"]) {
|
|
296
|
-
scripts["lint:md"] = 'markdownlint-cli2 "**/*.md" "#node_modules"';
|
|
297
|
-
packageJsonModified = true;
|
|
298
|
-
}
|
|
299
|
-
if (!scripts.format) {
|
|
300
|
-
scripts.format = "prettier --write .";
|
|
301
|
-
packageJsonModified = true;
|
|
302
|
-
}
|
|
303
|
-
if (!scripts["format:check"]) {
|
|
304
|
-
scripts["format:check"] = "prettier --check .";
|
|
305
|
-
packageJsonModified = true;
|
|
306
|
-
}
|
|
307
|
-
if (!scripts.knip) {
|
|
308
|
-
scripts.knip = "knip";
|
|
309
|
-
packageJsonModified = true;
|
|
310
|
-
}
|
|
311
|
-
if (projectType.publishableLibrary && !scripts.publint) {
|
|
312
|
-
scripts.publint = "publint";
|
|
313
|
-
packageJsonModified = true;
|
|
314
|
-
}
|
|
315
|
-
if (!scripts.prepare) {
|
|
316
|
-
scripts.prepare = "husky || true";
|
|
317
|
-
packageJsonModified = true;
|
|
318
|
-
}
|
|
319
|
-
if (!packageJson["lint-staged"]) {
|
|
320
|
-
packageJson["lint-staged"] = LINT_STAGED_CONFIG;
|
|
321
|
-
packageJsonModified = true;
|
|
322
|
-
}
|
|
323
|
-
if (packageJsonModified) {
|
|
324
|
-
packageJson.scripts = scripts;
|
|
325
|
-
writeJson(packageJsonPath, packageJson);
|
|
326
|
-
modified.push("package.json");
|
|
327
|
-
success("Added lint scripts and lint-staged config");
|
|
328
|
-
}
|
|
329
|
-
} catch (err) {
|
|
330
|
-
error(
|
|
331
|
-
`Failed to update package.json: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
332
|
-
);
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
335
|
-
info("\nInstalling linting dependencies...");
|
|
336
|
-
const devDeps = [
|
|
337
|
-
"eslint",
|
|
338
|
-
"prettier",
|
|
339
|
-
"@eslint/js",
|
|
340
|
-
"eslint-plugin-import-x",
|
|
341
|
-
"eslint-plugin-sonarjs",
|
|
342
|
-
"@microsoft/eslint-plugin-sdl",
|
|
343
|
-
"eslint-config-prettier",
|
|
344
|
-
"markdownlint-cli2",
|
|
345
|
-
"knip",
|
|
346
|
-
"husky",
|
|
347
|
-
"lint-staged"
|
|
348
|
-
];
|
|
349
|
-
if (projectType.typescript) {
|
|
350
|
-
devDeps.push("typescript-eslint");
|
|
351
|
-
}
|
|
352
|
-
if (projectType.react || projectType.nextjs) {
|
|
353
|
-
devDeps.push("eslint-plugin-react", "eslint-plugin-react-hooks", "eslint-plugin-jsx-a11y");
|
|
354
|
-
}
|
|
355
|
-
if (projectType.nextjs) {
|
|
356
|
-
devDeps.push("@next/eslint-plugin-next");
|
|
357
|
-
}
|
|
358
|
-
if (projectType.astro) {
|
|
359
|
-
devDeps.push("eslint-plugin-astro");
|
|
360
|
-
}
|
|
361
|
-
if (projectType.vue) {
|
|
362
|
-
devDeps.push("eslint-plugin-vue");
|
|
363
|
-
}
|
|
364
|
-
if (projectType.svelte) {
|
|
365
|
-
devDeps.push("eslint-plugin-svelte");
|
|
366
|
-
}
|
|
367
|
-
devDeps.push("eslint-plugin-boundaries");
|
|
368
|
-
if (projectType.electron) {
|
|
369
|
-
devDeps.push("@electron-toolkit/eslint-config");
|
|
370
|
-
}
|
|
371
|
-
if (projectType.vitest) {
|
|
372
|
-
devDeps.push("@vitest/eslint-plugin");
|
|
373
|
-
}
|
|
374
|
-
devDeps.push("eslint-plugin-playwright");
|
|
375
|
-
if (projectType.tailwind) {
|
|
376
|
-
devDeps.push("prettier-plugin-tailwindcss");
|
|
377
|
-
}
|
|
378
|
-
if (projectType.publishableLibrary) {
|
|
379
|
-
devDeps.push("publint");
|
|
380
|
-
}
|
|
381
|
-
try {
|
|
382
|
-
const installCmd = `npm install -D ${devDeps.join(" ")}`;
|
|
383
|
-
info(`Running: ${installCmd}`);
|
|
384
|
-
execSync(installCmd, { cwd, stdio: "inherit" });
|
|
385
|
-
success("Installed linting dependencies");
|
|
386
|
-
} catch {
|
|
387
|
-
warn("Failed to install dependencies. Run manually:");
|
|
388
|
-
listItem(`npm install -D ${devDeps.join(" ")}`);
|
|
389
|
-
}
|
|
390
|
-
info("\nConfiguring git hooks with Husky...");
|
|
391
|
-
if (isGitRepo(cwd)) {
|
|
392
|
-
try {
|
|
393
|
-
const huskyDir = join2(cwd, ".husky");
|
|
394
|
-
ensureDir(huskyDir);
|
|
395
|
-
const huskyPreCommit = join2(huskyDir, "pre-commit");
|
|
396
|
-
writeFile(huskyPreCommit, "npx safeword sync --quiet --stage\nnpx lint-staged\n");
|
|
397
|
-
makeScriptsExecutable(huskyDir);
|
|
398
|
-
created.push(".husky/pre-commit");
|
|
399
|
-
success("Configured Husky with lint-staged pre-commit hook");
|
|
400
|
-
} catch {
|
|
401
|
-
warn("Failed to setup Husky. Run manually:");
|
|
402
|
-
listItem("mkdir -p .husky");
|
|
403
|
-
listItem('echo "npx lint-staged" > .husky/pre-commit');
|
|
404
|
-
}
|
|
405
|
-
} else if (isNonInteractive) {
|
|
406
|
-
warn("Skipped Husky setup (no git repository)");
|
|
407
|
-
} else {
|
|
408
|
-
warn("Skipped Husky setup (no .git directory)");
|
|
409
|
-
info("Initialize git and run safeword setup again to enable pre-commit hooks");
|
|
410
|
-
}
|
|
411
|
-
header("Setup Complete");
|
|
412
|
-
if (created.length > 0) {
|
|
413
|
-
info("\nCreated:");
|
|
414
|
-
for (const file of created) {
|
|
415
|
-
listItem(file);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
if (modified.length > 0) {
|
|
419
|
-
info("\nModified:");
|
|
420
|
-
for (const file of modified) {
|
|
421
|
-
listItem(file);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
info("\nNext steps:");
|
|
425
|
-
listItem("Run `safeword check` to verify setup");
|
|
426
|
-
listItem("Commit the new files to git");
|
|
427
|
-
success(`
|
|
428
|
-
Safeword ${VERSION} installed successfully!`);
|
|
429
|
-
} catch (err) {
|
|
430
|
-
error(`Setup failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
431
|
-
process.exit(1);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
export {
|
|
435
|
-
setup
|
|
436
|
-
};
|
|
437
|
-
//# sourceMappingURL=setup-65EVU5OT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/setup.ts","../src/utils/boundaries.ts"],"sourcesContent":["/**\n * Setup command - Initialize safeword in a project\n */\n\nimport { join, basename } from 'node:path';\nimport { VERSION } from '../version.js';\nimport {\n exists,\n ensureDir,\n writeFile,\n readJson,\n writeJson,\n updateJson,\n copyDir,\n copyFile,\n getTemplatesDir,\n makeScriptsExecutable,\n} from '../utils/fs.js';\nimport { info, success, warn, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo } from '../utils/git.js';\nimport { detectProjectType } from '../utils/project-detector.js';\nimport { filterOutSafewordHooks } from '../utils/hooks.js';\nimport { ensureAgentsMdLink } from '../utils/agents-md.js';\nimport { PRETTIERRC, LINT_STAGED_CONFIG, getEslintConfig, SETTINGS_HOOKS } from '../templates/index.js';\nimport { detectArchitecture, generateBoundariesConfig } from '../utils/boundaries.js';\nimport { execSync } from 'node:child_process';\n\nexport interface SetupOptions {\n yes?: boolean;\n}\n\ninterface PackageJson {\n name?: string;\n version?: string;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n 'lint-staged'?: Record<string, string[]>;\n}\n\nexport async function setup(options: SetupOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if already configured\n if (exists(safewordDir)) {\n error('Already configured. Run `safeword upgrade` to update.');\n process.exit(1);\n }\n\n // Check for package.json, create if missing\n const packageJsonPath = join(cwd, 'package.json');\n let packageJsonCreated = false;\n if (!exists(packageJsonPath)) {\n const dirName = basename(cwd) || 'project';\n const defaultPackageJson: PackageJson = {\n name: dirName,\n version: '0.1.0',\n scripts: {},\n };\n writeJson(packageJsonPath, defaultPackageJson);\n packageJsonCreated = true;\n }\n\n const isNonInteractive = options.yes || !process.stdin.isTTY;\n\n header('Safeword Setup');\n info(`Version: ${VERSION}`);\n\n if (packageJsonCreated) {\n info('Created package.json (none found)');\n }\n\n // Track created files for summary\n const created: string[] = packageJsonCreated ? ['package.json'] : [];\n const modified: string[] = [];\n\n try {\n const templatesDir = getTemplatesDir();\n\n // 1. Create .safeword directory structure and copy templates\n info('\\nCreating .safeword directory...');\n\n ensureDir(safewordDir);\n ensureDir(join(safewordDir, 'learnings'));\n ensureDir(join(safewordDir, 'planning', 'user-stories'));\n ensureDir(join(safewordDir, 'planning', 'design'));\n ensureDir(join(safewordDir, 'tickets', 'completed'));\n\n // Copy full SAFEWORD.md from templates\n copyFile(join(templatesDir, 'SAFEWORD.md'), join(safewordDir, 'SAFEWORD.md'));\n writeFile(join(safewordDir, 'version'), VERSION);\n\n // Copy methodology guides\n copyDir(join(templatesDir, 'guides'), join(safewordDir, 'guides'));\n\n // Copy document templates (to 'templates' to match links in SAFEWORD.md)\n copyDir(join(templatesDir, 'doc-templates'), join(safewordDir, 'templates'));\n\n // Copy review prompts\n copyDir(join(templatesDir, 'prompts'), join(safewordDir, 'prompts'));\n\n // Copy lib scripts and make executable\n copyDir(join(templatesDir, 'lib'), join(safewordDir, 'lib'));\n makeScriptsExecutable(join(safewordDir, 'lib'));\n\n // Copy hook scripts and make executable\n copyDir(join(templatesDir, 'hooks'), join(safewordDir, 'hooks'));\n makeScriptsExecutable(join(safewordDir, 'hooks'));\n\n created.push('.safeword/');\n success('Created .safeword directory');\n\n // 2. Handle AGENTS.md\n info('\\nConfiguring AGENTS.md...');\n const agentsMdResult = ensureAgentsMdLink(cwd);\n if (agentsMdResult === 'created') {\n created.push('AGENTS.md');\n success('Created AGENTS.md');\n } else if (agentsMdResult === 'modified') {\n modified.push('AGENTS.md');\n success('Prepended link to AGENTS.md');\n } else {\n info('AGENTS.md already has safeword link');\n }\n\n // 3. Register Claude Code hooks\n info('\\nRegistering Claude Code hooks...');\n\n const claudeDir = join(cwd, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n ensureDir(claudeDir);\n\n try {\n updateJson<{ hooks?: Record<string, unknown[]> }>(settingsPath, existing => {\n const hooks = existing?.hooks ?? {};\n\n // Merge hooks, preserving existing non-safeword hooks\n for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {\n const existingHooks = (hooks[event] as unknown[]) ?? [];\n const nonSafewordHooks = filterOutSafewordHooks(existingHooks);\n hooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n\n return { ...existing, hooks };\n });\n\n if (exists(settingsPath)) {\n modified.push('.claude/settings.json');\n } else {\n created.push('.claude/settings.json');\n }\n success('Registered hooks in .claude/settings.json');\n } catch (err) {\n error(`Failed to register hooks: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n\n // 4. Copy skills\n info('\\nInstalling skills...');\n\n const skillsDir = join(claudeDir, 'skills');\n copyDir(join(templatesDir, 'skills'), skillsDir);\n\n created.push('.claude/skills/safeword-quality-reviewer/');\n success('Installed skills');\n\n // 5. Copy slash commands\n info('\\nInstalling slash commands...');\n\n const commandsDir = join(claudeDir, 'commands');\n copyDir(join(templatesDir, 'commands'), commandsDir);\n\n created.push('.claude/commands/');\n success('Installed slash commands');\n\n // 6. Setup MCP servers\n info('\\nConfiguring MCP servers...');\n\n const mcpConfigPath = join(cwd, '.mcp.json');\n\n updateJson<{ mcpServers?: Record<string, unknown> }>(mcpConfigPath, existing => {\n const mcpServers = existing?.mcpServers ?? {};\n\n // Add safeword MCP servers (context7 and playwright)\n mcpServers.context7 = {\n command: 'npx',\n args: ['-y', '@upstash/context7-mcp@latest'],\n };\n mcpServers.playwright = {\n command: 'npx',\n args: ['@playwright/mcp@latest'],\n };\n\n return { ...existing, mcpServers };\n });\n\n if (exists(mcpConfigPath)) {\n modified.push('.mcp.json');\n } else {\n created.push('.mcp.json');\n }\n success('Configured MCP servers');\n\n // 7. Setup linting\n info('\\nConfiguring linting...');\n\n const packageJson = readJson<PackageJson>(packageJsonPath);\n if (!packageJson) {\n error('Failed to read package.json');\n process.exit(1);\n }\n\n const projectType = detectProjectType(packageJson);\n\n // Detect architecture boundaries (always configured, rules depend on detected dirs)\n const architecture = detectArchitecture(cwd);\n\n // Create dynamic ESLint config (detects frameworks from package.json at runtime)\n const eslintConfigPath = join(cwd, 'eslint.config.mjs');\n if (!exists(eslintConfigPath)) {\n writeFile(eslintConfigPath, getEslintConfig({ boundaries: true }));\n created.push('eslint.config.mjs');\n success('Created eslint.config.mjs (dynamic - adapts to framework changes)');\n } else {\n info('eslint.config.mjs already exists');\n }\n\n // Always create boundaries config (rules depend on detected architecture dirs)\n const boundariesConfigPath = join(safewordDir, 'eslint-boundaries.config.mjs');\n writeFile(boundariesConfigPath, generateBoundariesConfig(architecture));\n if (architecture.directories.length > 0) {\n info(`Detected architecture: ${architecture.directories.join(', ')} (${architecture.inSrc ? 'in src/' : 'at root'})`);\n } else {\n info('No architecture directories detected yet (boundaries ready when you add them)');\n }\n success('Created .safeword/eslint-boundaries.config.mjs');\n\n // Create Prettier config\n const prettierrcPath = join(cwd, '.prettierrc');\n if (!exists(prettierrcPath)) {\n writeFile(prettierrcPath, PRETTIERRC);\n created.push('.prettierrc');\n success('Created .prettierrc');\n } else {\n info('.prettierrc already exists');\n }\n\n // Create markdownlint config (using cli2 preferred filename)\n const markdownlintPath = join(cwd, '.markdownlint-cli2.jsonc');\n if (!exists(markdownlintPath)) {\n copyFile(join(templatesDir, 'markdownlint-cli2.jsonc'), markdownlintPath);\n created.push('.markdownlint-cli2.jsonc');\n success('Created .markdownlint-cli2.jsonc');\n } else {\n info('.markdownlint-cli2.jsonc already exists');\n }\n\n // Add scripts and lint-staged config to package.json\n try {\n const scripts = packageJson.scripts ?? {};\n let packageJsonModified = false;\n\n if (!scripts.lint) {\n scripts.lint = 'eslint .';\n packageJsonModified = true;\n }\n\n if (!scripts['lint:md']) {\n scripts['lint:md'] = 'markdownlint-cli2 \"**/*.md\" \"#node_modules\"';\n packageJsonModified = true;\n }\n\n if (!scripts.format) {\n scripts.format = 'prettier --write .';\n packageJsonModified = true;\n }\n\n if (!scripts['format:check']) {\n scripts['format:check'] = 'prettier --check .';\n packageJsonModified = true;\n }\n\n if (!scripts.knip) {\n scripts.knip = 'knip';\n packageJsonModified = true;\n }\n\n // Add publint script for publishable libraries\n if (projectType.publishableLibrary && !scripts.publint) {\n scripts.publint = 'publint';\n packageJsonModified = true;\n }\n\n // Add prepare script for Husky (runs on npm install)\n // The || true fallback prevents npm install --production from failing\n // when husky (a devDependency) isn't installed\n if (!scripts.prepare) {\n scripts.prepare = 'husky || true';\n packageJsonModified = true;\n }\n\n // Add lint-staged config\n if (!packageJson['lint-staged']) {\n packageJson['lint-staged'] = LINT_STAGED_CONFIG;\n packageJsonModified = true;\n }\n\n if (packageJsonModified) {\n packageJson.scripts = scripts;\n writeJson(packageJsonPath, packageJson);\n modified.push('package.json');\n success('Added lint scripts and lint-staged config');\n }\n } catch (err) {\n error(\n `Failed to update package.json: ${err instanceof Error ? err.message : 'Unknown error'}`,\n );\n process.exit(1);\n }\n\n // 8. Install dependencies\n info('\\nInstalling linting dependencies...');\n\n // Build the list of packages to install\n const devDeps: string[] = [\n 'eslint',\n 'prettier',\n '@eslint/js',\n 'eslint-plugin-import-x',\n 'eslint-plugin-sonarjs',\n '@microsoft/eslint-plugin-sdl',\n 'eslint-config-prettier',\n 'markdownlint-cli2',\n 'knip',\n 'husky',\n 'lint-staged',\n ];\n\n if (projectType.typescript) {\n devDeps.push('typescript-eslint');\n }\n if (projectType.react || projectType.nextjs) {\n devDeps.push('eslint-plugin-react', 'eslint-plugin-react-hooks', 'eslint-plugin-jsx-a11y');\n }\n if (projectType.nextjs) {\n devDeps.push('@next/eslint-plugin-next');\n }\n if (projectType.astro) {\n devDeps.push('eslint-plugin-astro');\n }\n if (projectType.vue) {\n devDeps.push('eslint-plugin-vue');\n }\n if (projectType.svelte) {\n devDeps.push('eslint-plugin-svelte');\n }\n // Always install boundaries - configured only when 3+ architecture directories exist\n devDeps.push('eslint-plugin-boundaries');\n if (projectType.electron) {\n devDeps.push('@electron-toolkit/eslint-config');\n }\n if (projectType.vitest) {\n devDeps.push('@vitest/eslint-plugin');\n }\n // Always include Playwright - safeword sets up e2e testing with Playwright\n devDeps.push('eslint-plugin-playwright');\n\n // Tailwind: use official Prettier plugin for class sorting\n if (projectType.tailwind) {\n devDeps.push('prettier-plugin-tailwindcss');\n }\n\n // Publishable libraries: validate package.json for npm publishing\n if (projectType.publishableLibrary) {\n devDeps.push('publint');\n }\n\n try {\n const installCmd = `npm install -D ${devDeps.join(' ')}`;\n info(`Running: ${installCmd}`);\n execSync(installCmd, { cwd, stdio: 'inherit' });\n success('Installed linting dependencies');\n } catch {\n warn('Failed to install dependencies. Run manually:');\n listItem(`npm install -D ${devDeps.join(' ')}`);\n }\n\n // 9. Setup Husky for git hooks (manually, not using husky init which overwrites prepare script)\n info('\\nConfiguring git hooks with Husky...');\n\n if (isGitRepo(cwd)) {\n try {\n // Create .husky directory and pre-commit hook manually\n // (husky init unconditionally sets prepare script, which we don't want)\n const huskyDir = join(cwd, '.husky');\n ensureDir(huskyDir);\n\n // Create pre-commit hook that syncs linting plugins and runs lint-staged\n const huskyPreCommit = join(huskyDir, 'pre-commit');\n writeFile(huskyPreCommit, 'npx safeword sync --quiet --stage\\nnpx lint-staged\\n');\n\n // Make hook executable (required for git hooks on Unix)\n makeScriptsExecutable(huskyDir);\n\n created.push('.husky/pre-commit');\n success('Configured Husky with lint-staged pre-commit hook');\n } catch {\n warn('Failed to setup Husky. Run manually:');\n listItem('mkdir -p .husky');\n listItem('echo \"npx lint-staged\" > .husky/pre-commit');\n }\n } else if (isNonInteractive) {\n warn('Skipped Husky setup (no git repository)');\n } else {\n warn('Skipped Husky setup (no .git directory)');\n info('Initialize git and run safeword setup again to enable pre-commit hooks');\n }\n\n // Print summary\n header('Setup Complete');\n\n if (created.length > 0) {\n info('\\nCreated:');\n for (const file of created) {\n listItem(file);\n }\n }\n\n if (modified.length > 0) {\n info('\\nModified:');\n for (const file of modified) {\n listItem(file);\n }\n }\n\n info('\\nNext steps:');\n listItem('Run `safeword check` to verify setup');\n listItem('Commit the new files to git');\n\n success(`\\nSafeword ${VERSION} installed successfully!`);\n } catch (err) {\n error(`Setup failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n","/**\n * Architecture boundaries detection and config generation\n *\n * Auto-detects common architecture directories and generates\n * eslint-plugin-boundaries config with sensible hierarchy rules.\n */\n\nimport { join } from 'node:path';\nimport { exists } from './fs.js';\n\n/**\n * Architecture directories to detect, ordered from bottom to top of hierarchy.\n * Lower items can be imported by higher items, not vice versa.\n */\nconst ARCHITECTURE_DIRS = [\n 'types', // Bottom: can be imported by everything\n 'utils',\n 'lib',\n 'hooks',\n 'services',\n 'components',\n 'features',\n 'modules',\n 'app', // Top: can import everything\n] as const;\n\ntype ArchDir = (typeof ARCHITECTURE_DIRS)[number];\n\n/**\n * Hierarchy rules: what each layer can import\n * Lower layers have fewer import permissions\n */\nconst HIERARCHY: Record<ArchDir, ArchDir[]> = {\n types: [], // types can't import anything (pure type definitions)\n utils: ['types'],\n lib: ['utils', 'types'],\n hooks: ['lib', 'utils', 'types'],\n services: ['lib', 'utils', 'types'],\n components: ['hooks', 'services', 'lib', 'utils', 'types'],\n features: ['components', 'hooks', 'services', 'lib', 'utils', 'types'],\n modules: ['components', 'hooks', 'services', 'lib', 'utils', 'types'],\n app: ['features', 'modules', 'components', 'hooks', 'services', 'lib', 'utils', 'types'],\n};\n\nexport interface DetectedArchitecture {\n directories: ArchDir[];\n inSrc: boolean; // true if dirs are in src/, false if at root\n}\n\n/**\n * Detects architecture directories in the project\n * Always returns a result (even with 0 directories) - boundaries is always configured\n */\nexport function detectArchitecture(projectDir: string): DetectedArchitecture {\n const foundInSrc: ArchDir[] = [];\n const foundAtRoot: ArchDir[] = [];\n\n for (const dir of ARCHITECTURE_DIRS) {\n if (exists(join(projectDir, 'src', dir))) {\n foundInSrc.push(dir);\n }\n if (exists(join(projectDir, dir))) {\n foundAtRoot.push(dir);\n }\n }\n\n // Prefer src/ location if more dirs found there\n const inSrc = foundInSrc.length >= foundAtRoot.length;\n const found = inSrc ? foundInSrc : foundAtRoot;\n\n return { directories: found, inSrc };\n}\n\n/**\n * Generates the boundaries config file content\n */\nexport function generateBoundariesConfig(arch: DetectedArchitecture): string {\n const prefix = arch.inSrc ? 'src/' : '';\n const hasDirectories = arch.directories.length > 0;\n\n // Generate element definitions with mode: 'full' to match from project root only\n const elements = arch.directories\n .map(dir => ` { type: '${dir}', pattern: '${prefix}${dir}/**', mode: 'full' }`)\n .join(',\\n');\n\n // Generate rules (what each layer can import)\n const rules = arch.directories\n .filter(dir => HIERARCHY[dir].length > 0)\n .map(dir => {\n const allowed = HIERARCHY[dir].filter(dep => arch.directories.includes(dep));\n if (allowed.length === 0) return null;\n return ` { from: ['${dir}'], allow: [${allowed.map(d => `'${d}'`).join(', ')}] }`;\n })\n .filter(Boolean)\n .join(',\\n');\n\n const detectedInfo = hasDirectories\n ? `Detected directories: ${arch.directories.join(', ')} (${arch.inSrc ? 'in src/' : 'at root'})`\n : 'No architecture directories detected yet - add types/, utils/, components/, etc.';\n\n // Build elements array content (empty array if no directories)\n const elementsContent = elements || '';\n const rulesContent = rules || '';\n\n return `/**\n * Architecture Boundaries Configuration (AUTO-GENERATED)\n *\n * ${detectedInfo}\n *\n * This enforces import boundaries between architectural layers:\n * - Lower layers (types, utils) cannot import from higher layers (components, features)\n * - Uses 'warn' severity - informative, not blocking\n *\n * Recognized directories (in hierarchy order):\n * types → utils → lib → hooks/services → components → features/modules → app\n *\n * To customize, override in your eslint.config.mjs:\n * rules: { 'boundaries/element-types': ['error', { ... }] }\n */\n\nimport boundaries from 'eslint-plugin-boundaries';\n\nexport default {\n plugins: { boundaries },\n settings: {\n 'boundaries/elements': [\n${elementsContent}\n ],\n },\n rules: {\n 'boundaries/element-types': ['warn', {\n default: 'disallow',\n rules: [\n${rulesContent}\n ],\n }],\n 'boundaries/no-unknown': 'off', // Allow files outside defined elements\n 'boundaries/no-unknown-files': 'off', // Allow non-matching files\n },\n};\n`;\n}\n"],"mappings":"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","names":["join","join"]}
|