safeword 0.6.0 → 0.6.1
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-KTLIM6UR.js → check-INXMFCL5.js} +7 -5
- package/dist/{check-KTLIM6UR.js.map → check-INXMFCL5.js.map} +1 -1
- package/dist/chunk-6CVTH67L.js +43 -0
- package/dist/chunk-6CVTH67L.js.map +1 -0
- package/dist/{chunk-KEWQQP4C.js → chunk-75FKNZUM.js} +2 -2
- package/dist/{chunk-NI3Z5XHH.js → chunk-ARIAOK2F.js} +2 -34
- package/dist/chunk-ARIAOK2F.js.map +1 -0
- package/dist/chunk-FRPJITGG.js +35 -0
- package/dist/chunk-FRPJITGG.js.map +1 -0
- package/dist/chunk-IWWBZVHT.js +274 -0
- package/dist/chunk-IWWBZVHT.js.map +1 -0
- package/dist/cli.js +9 -5
- package/dist/cli.js.map +1 -1
- package/dist/{diff-TV24EW3X.js → diff-L7G22MG7.js} +7 -5
- package/dist/{diff-TV24EW3X.js.map → diff-L7G22MG7.js.map} +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/{reset-WJEQTYCX.js → reset-5SRM3P6J.js} +9 -7
- package/dist/reset-5SRM3P6J.js.map +1 -0
- package/dist/{setup-UYCYH5SE.js → setup-65EVU5OT.js} +179 -75
- package/dist/setup-65EVU5OT.js.map +1 -0
- package/dist/sync-4XBMKLXS.js +116 -0
- package/dist/sync-4XBMKLXS.js.map +1 -0
- package/dist/{upgrade-TJZYMNP2.js → upgrade-P3WX3ODU.js} +11 -9
- package/dist/{upgrade-TJZYMNP2.js.map → upgrade-P3WX3ODU.js.map} +1 -1
- package/package.json +2 -1
- package/templates/SAFEWORD.md +4 -4
- package/templates/commands/architecture.md +27 -0
- package/templates/commands/lint.md +15 -54
- package/templates/commands/quality-review.md +16 -13
- package/templates/hooks/post-tool-lint.sh +14 -53
- package/dist/chunk-74FG33MU.js +0 -211
- package/dist/chunk-74FG33MU.js.map +0 -1
- package/dist/chunk-NI3Z5XHH.js.map +0 -1
- package/dist/reset-WJEQTYCX.js.map +0 -1
- package/dist/setup-UYCYH5SE.js.map +0 -1
- package/templates/commands/arch-review.md +0 -28
- package/templates/hooks/git-pre-commit.sh +0 -18
- /package/dist/{chunk-KEWQQP4C.js.map → chunk-75FKNZUM.js.map} +0 -0
- /package/templates/prompts/{arch-review.md → architecture.md} +0 -0
- /package/templates/prompts/{quality-review.md → review.md} +0 -0
package/dist/chunk-74FG33MU.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
exists,
|
|
3
|
-
readFile,
|
|
4
|
-
writeFile
|
|
5
|
-
} from "./chunk-NI3Z5XHH.js";
|
|
6
|
-
|
|
7
|
-
// src/utils/hooks.ts
|
|
8
|
-
function isHookEntry(h) {
|
|
9
|
-
return typeof h === "object" && h !== null && "hooks" in h && Array.isArray(h.hooks);
|
|
10
|
-
}
|
|
11
|
-
function isSafewordHook(h) {
|
|
12
|
-
if (!isHookEntry(h)) return false;
|
|
13
|
-
return h.hooks.some(
|
|
14
|
-
(cmd) => typeof cmd.command === "string" && cmd.command.includes(".safeword")
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
function filterOutSafewordHooks(hooks) {
|
|
18
|
-
return hooks.filter((h) => !isSafewordHook(h));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// src/templates/content.ts
|
|
22
|
-
var AGENTS_MD_LINK = `**\u26A0\uFE0F ALWAYS READ FIRST: @./.safeword/SAFEWORD.md**
|
|
23
|
-
|
|
24
|
-
The SAFEWORD.md file contains core development patterns, workflows, and conventions.
|
|
25
|
-
Read it BEFORE working on any task in this project.
|
|
26
|
-
|
|
27
|
-
---`;
|
|
28
|
-
var PRETTIERRC = `{
|
|
29
|
-
"semi": true,
|
|
30
|
-
"singleQuote": true,
|
|
31
|
-
"tabWidth": 2,
|
|
32
|
-
"trailingComma": "es5",
|
|
33
|
-
"printWidth": 100,
|
|
34
|
-
"endOfLine": "lf"
|
|
35
|
-
}
|
|
36
|
-
`;
|
|
37
|
-
var LINT_STAGED_CONFIG = {
|
|
38
|
-
"*.{js,jsx,ts,tsx,mjs,cjs}": ["eslint --fix", "prettier --write"],
|
|
39
|
-
"*.{vue,svelte,astro}": ["eslint --fix", "prettier --write"],
|
|
40
|
-
"*.{json,css,scss,html,yaml,yml,graphql}": ["prettier --write"],
|
|
41
|
-
"*.md": ["markdownlint-cli2 --fix", "prettier --write"]
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// src/templates/config.ts
|
|
45
|
-
function getEslintConfig(options) {
|
|
46
|
-
const imports = [
|
|
47
|
-
'import js from "@eslint/js";',
|
|
48
|
-
'import eslintConfigPrettier from "eslint-config-prettier";'
|
|
49
|
-
];
|
|
50
|
-
const configs = ["js.configs.recommended"];
|
|
51
|
-
const ignores = ["node_modules/", "dist/", "build/", "coverage/"];
|
|
52
|
-
if (options.typescript) {
|
|
53
|
-
imports.push('import tseslint from "typescript-eslint";');
|
|
54
|
-
configs.push("...tseslint.configs.recommended");
|
|
55
|
-
}
|
|
56
|
-
if (options.react || options.nextjs) {
|
|
57
|
-
imports.push('import react from "eslint-plugin-react";');
|
|
58
|
-
imports.push('import reactHooks from "eslint-plugin-react-hooks";');
|
|
59
|
-
configs.push("react.configs.flat.recommended");
|
|
60
|
-
configs.push('react.configs.flat["jsx-runtime"]');
|
|
61
|
-
configs.push(
|
|
62
|
-
'{ plugins: { "react-hooks": reactHooks }, rules: reactHooks.configs.recommended.rules }'
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
if (options.nextjs) {
|
|
66
|
-
imports.push('import nextPlugin from "@next/eslint-plugin-next";');
|
|
67
|
-
configs.push(
|
|
68
|
-
'{ plugins: { "@next/next": nextPlugin }, rules: nextPlugin.configs.recommended.rules }'
|
|
69
|
-
);
|
|
70
|
-
ignores.push(".next/");
|
|
71
|
-
}
|
|
72
|
-
if (options.astro) {
|
|
73
|
-
imports.push('import eslintPluginAstro from "eslint-plugin-astro";');
|
|
74
|
-
configs.push("...eslintPluginAstro.configs.recommended");
|
|
75
|
-
ignores.push(".astro/");
|
|
76
|
-
}
|
|
77
|
-
if (options.vue) {
|
|
78
|
-
imports.push('import pluginVue from "eslint-plugin-vue";');
|
|
79
|
-
configs.push("...pluginVue.configs['flat/recommended']");
|
|
80
|
-
ignores.push(".nuxt/");
|
|
81
|
-
}
|
|
82
|
-
if (options.svelte) {
|
|
83
|
-
imports.push('import eslintPluginSvelte from "eslint-plugin-svelte";');
|
|
84
|
-
configs.push("...eslintPluginSvelte.configs['flat/recommended']");
|
|
85
|
-
ignores.push(".svelte-kit/");
|
|
86
|
-
}
|
|
87
|
-
configs.push("eslintConfigPrettier");
|
|
88
|
-
return `${imports.join("\n")}
|
|
89
|
-
|
|
90
|
-
export default [
|
|
91
|
-
${configs.join(",\n ")},
|
|
92
|
-
{
|
|
93
|
-
ignores: [${ignores.map((i) => `"${i}"`).join(", ")}],
|
|
94
|
-
},
|
|
95
|
-
];
|
|
96
|
-
`;
|
|
97
|
-
}
|
|
98
|
-
var SETTINGS_HOOKS = {
|
|
99
|
-
SessionStart: [
|
|
100
|
-
{
|
|
101
|
-
hooks: [
|
|
102
|
-
{
|
|
103
|
-
type: "command",
|
|
104
|
-
command: "bash .safeword/hooks/session-verify-agents.sh"
|
|
105
|
-
}
|
|
106
|
-
]
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
hooks: [
|
|
110
|
-
{
|
|
111
|
-
type: "command",
|
|
112
|
-
command: "bash .safeword/hooks/session-version.sh"
|
|
113
|
-
}
|
|
114
|
-
]
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
hooks: [
|
|
118
|
-
{
|
|
119
|
-
type: "command",
|
|
120
|
-
command: "bash .safeword/hooks/session-lint-check.sh"
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
|
-
],
|
|
125
|
-
UserPromptSubmit: [
|
|
126
|
-
{
|
|
127
|
-
hooks: [
|
|
128
|
-
{
|
|
129
|
-
type: "command",
|
|
130
|
-
command: "bash .safeword/hooks/prompt-timestamp.sh"
|
|
131
|
-
}
|
|
132
|
-
]
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
hooks: [
|
|
136
|
-
{
|
|
137
|
-
type: "command",
|
|
138
|
-
command: "bash .safeword/hooks/prompt-questions.sh"
|
|
139
|
-
}
|
|
140
|
-
]
|
|
141
|
-
}
|
|
142
|
-
],
|
|
143
|
-
Stop: [
|
|
144
|
-
{
|
|
145
|
-
hooks: [
|
|
146
|
-
{
|
|
147
|
-
type: "command",
|
|
148
|
-
command: "bash .safeword/hooks/stop-quality.sh"
|
|
149
|
-
}
|
|
150
|
-
]
|
|
151
|
-
}
|
|
152
|
-
],
|
|
153
|
-
PostToolUse: [
|
|
154
|
-
{
|
|
155
|
-
matcher: "Write|Edit|MultiEdit|NotebookEdit",
|
|
156
|
-
hooks: [
|
|
157
|
-
{
|
|
158
|
-
type: "command",
|
|
159
|
-
command: "bash .safeword/hooks/post-tool-lint.sh"
|
|
160
|
-
}
|
|
161
|
-
]
|
|
162
|
-
}
|
|
163
|
-
]
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// src/utils/agents-md.ts
|
|
167
|
-
import { join } from "path";
|
|
168
|
-
var SAFEWORD_LINK_MARKER = "@./.safeword/SAFEWORD.md";
|
|
169
|
-
function ensureAgentsMdLink(cwd) {
|
|
170
|
-
const agentsMdPath = join(cwd, "AGENTS.md");
|
|
171
|
-
if (!exists(agentsMdPath)) {
|
|
172
|
-
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
173
|
-
`);
|
|
174
|
-
return "created";
|
|
175
|
-
}
|
|
176
|
-
const content = readFile(agentsMdPath);
|
|
177
|
-
if (!content.includes(SAFEWORD_LINK_MARKER)) {
|
|
178
|
-
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
179
|
-
|
|
180
|
-
${content}`);
|
|
181
|
-
return "modified";
|
|
182
|
-
}
|
|
183
|
-
return "unchanged";
|
|
184
|
-
}
|
|
185
|
-
function removeAgentsMdLink(cwd) {
|
|
186
|
-
const agentsMdPath = join(cwd, "AGENTS.md");
|
|
187
|
-
if (!exists(agentsMdPath)) return false;
|
|
188
|
-
const content = readFile(agentsMdPath);
|
|
189
|
-
let newContent = content.replace(AGENTS_MD_LINK, "");
|
|
190
|
-
const lines = newContent.split("\n").filter((line) => !line.includes(SAFEWORD_LINK_MARKER));
|
|
191
|
-
while (lines.length > 0 && (lines[0].trim() === "" || lines[0].trim() === "---")) {
|
|
192
|
-
lines.shift();
|
|
193
|
-
}
|
|
194
|
-
newContent = lines.join("\n");
|
|
195
|
-
if (newContent !== content) {
|
|
196
|
-
writeFile(agentsMdPath, newContent);
|
|
197
|
-
return true;
|
|
198
|
-
}
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export {
|
|
203
|
-
filterOutSafewordHooks,
|
|
204
|
-
PRETTIERRC,
|
|
205
|
-
LINT_STAGED_CONFIG,
|
|
206
|
-
getEslintConfig,
|
|
207
|
-
SETTINGS_HOOKS,
|
|
208
|
-
ensureAgentsMdLink,
|
|
209
|
-
removeAgentsMdLink
|
|
210
|
-
};
|
|
211
|
-
//# sourceMappingURL=chunk-74FG33MU.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/hooks.ts","../src/templates/content.ts","../src/templates/config.ts","../src/utils/agents-md.ts"],"sourcesContent":["/**\n * Hook utilities for Claude Code settings\n */\n\ninterface HookCommand {\n type: string;\n command: string;\n}\n\ninterface HookEntry {\n matcher?: string;\n hooks: HookCommand[];\n}\n\n/**\n * Type guard to check if a value is a hook entry with hooks array\n */\nexport function isHookEntry(h: unknown): h is HookEntry {\n return (\n typeof h === 'object' &&\n h !== null &&\n 'hooks' in h &&\n Array.isArray((h as HookEntry).hooks)\n );\n}\n\n/**\n * Check if a hook entry contains a safeword hook (command contains '.safeword')\n */\nexport function isSafewordHook(h: unknown): boolean {\n if (!isHookEntry(h)) return false;\n return h.hooks.some(\n (cmd) => typeof cmd.command === 'string' && cmd.command.includes('.safeword'),\n );\n}\n\n/**\n * Filter out safeword hooks from an array of hook entries\n */\nexport function filterOutSafewordHooks(hooks: unknown[]): unknown[] {\n return hooks.filter((h) => !isSafewordHook(h));\n}\n","/**\n * Content templates - static string content\n *\n * Note: Most templates (SAFEWORD.md, hooks, skills, guides, etc.) are now\n * file-based in the templates/ directory. This file contains only small\n * string constants that are used inline.\n */\n\nexport const AGENTS_MD_LINK = `**⚠️ ALWAYS READ FIRST: @./.safeword/SAFEWORD.md**\n\nThe SAFEWORD.md file contains core development patterns, workflows, and conventions.\nRead it BEFORE working on any task in this project.\n\n---`;\n\nexport const PRETTIERRC = `{\n \"semi\": true,\n \"singleQuote\": true,\n \"tabWidth\": 2,\n \"trailingComma\": \"es5\",\n \"printWidth\": 100,\n \"endOfLine\": \"lf\"\n}\n`;\n\n/**\n * lint-staged configuration for pre-commit hooks\n * Runs linters only on staged files for fast commits\n */\nexport const LINT_STAGED_CONFIG = {\n '*.{js,jsx,ts,tsx,mjs,cjs}': ['eslint --fix', 'prettier --write'],\n '*.{vue,svelte,astro}': ['eslint --fix', 'prettier --write'],\n '*.{json,css,scss,html,yaml,yml,graphql}': ['prettier --write'],\n '*.md': ['markdownlint-cli2 --fix', 'prettier --write'],\n};\n","/**\n * Configuration templates - ESLint config generation and hook settings\n *\n * ESLint flat config (v9+) with eslint-config-prettier to avoid conflicts.\n * See: https://eslint.org/docs/latest/use/configure/configuration-files\n */\n\nexport function getEslintConfig(options: {\n typescript?: boolean;\n react?: boolean;\n nextjs?: boolean;\n astro?: boolean;\n vue?: boolean;\n svelte?: boolean;\n}): string {\n const imports: string[] = [\n 'import js from \"@eslint/js\";',\n 'import eslintConfigPrettier from \"eslint-config-prettier\";',\n ];\n const configs: string[] = ['js.configs.recommended'];\n const ignores: string[] = ['node_modules/', 'dist/', 'build/', 'coverage/'];\n\n if (options.typescript) {\n imports.push('import tseslint from \"typescript-eslint\";');\n configs.push('...tseslint.configs.recommended');\n }\n\n if (options.react || options.nextjs) {\n imports.push('import react from \"eslint-plugin-react\";');\n imports.push('import reactHooks from \"eslint-plugin-react-hooks\";');\n configs.push('react.configs.flat.recommended');\n configs.push('react.configs.flat[\"jsx-runtime\"]');\n configs.push(\n '{ plugins: { \"react-hooks\": reactHooks }, rules: reactHooks.configs.recommended.rules }',\n );\n }\n\n if (options.nextjs) {\n imports.push('import nextPlugin from \"@next/eslint-plugin-next\";');\n configs.push(\n '{ plugins: { \"@next/next\": nextPlugin }, rules: nextPlugin.configs.recommended.rules }',\n );\n ignores.push('.next/');\n }\n\n if (options.astro) {\n imports.push('import eslintPluginAstro from \"eslint-plugin-astro\";');\n configs.push('...eslintPluginAstro.configs.recommended');\n ignores.push('.astro/');\n }\n\n if (options.vue) {\n imports.push('import pluginVue from \"eslint-plugin-vue\";');\n configs.push(\"...pluginVue.configs['flat/recommended']\");\n ignores.push('.nuxt/');\n }\n\n if (options.svelte) {\n imports.push('import eslintPluginSvelte from \"eslint-plugin-svelte\";');\n configs.push(\"...eslintPluginSvelte.configs['flat/recommended']\");\n ignores.push('.svelte-kit/');\n }\n\n // eslint-config-prettier must be last to disable conflicting rules\n configs.push('eslintConfigPrettier');\n\n return `${imports.join('\\n')}\n\nexport default [\n ${configs.join(',\\n ')},\n {\n ignores: [${ignores.map((i) => `\"${i}\"`).join(', ')}],\n },\n];\n`;\n}\n\nexport const SETTINGS_HOOKS = {\n SessionStart: [\n {\n hooks: [\n {\n type: 'command',\n command: 'bash .safeword/hooks/session-verify-agents.sh',\n },\n ],\n },\n {\n hooks: [\n {\n type: 'command',\n command: 'bash .safeword/hooks/session-version.sh',\n },\n ],\n },\n {\n hooks: [\n {\n type: 'command',\n command: 'bash .safeword/hooks/session-lint-check.sh',\n },\n ],\n },\n ],\n UserPromptSubmit: [\n {\n hooks: [\n {\n type: 'command',\n command: 'bash .safeword/hooks/prompt-timestamp.sh',\n },\n ],\n },\n {\n hooks: [\n {\n type: 'command',\n command: 'bash .safeword/hooks/prompt-questions.sh',\n },\n ],\n },\n ],\n Stop: [\n {\n hooks: [\n {\n type: 'command',\n command: 'bash .safeword/hooks/stop-quality.sh',\n },\n ],\n },\n ],\n PostToolUse: [\n {\n matcher: 'Write|Edit|MultiEdit|NotebookEdit',\n hooks: [\n {\n type: 'command',\n command: 'bash .safeword/hooks/post-tool-lint.sh',\n },\n ],\n },\n ],\n};\n","/**\n * AGENTS.md file utilities\n */\n\nimport { join } from 'node:path';\nimport { exists, readFile, writeFile } from './fs.js';\nimport { AGENTS_MD_LINK } from '../templates/index.js';\n\nconst SAFEWORD_LINK_MARKER = '@./.safeword/SAFEWORD.md';\n\n/**\n * Check if AGENTS.md has the safeword link\n */\nexport function hasAgentsMdLink(cwd: string): boolean {\n const agentsMdPath = join(cwd, 'AGENTS.md');\n if (!exists(agentsMdPath)) return false;\n const content = readFile(agentsMdPath);\n return content.includes(SAFEWORD_LINK_MARKER);\n}\n\n/**\n * Ensure AGENTS.md exists and has the safeword link.\n * Returns 'created' | 'modified' | 'unchanged'\n */\nexport function ensureAgentsMdLink(cwd: string): 'created' | 'modified' | 'unchanged' {\n const agentsMdPath = join(cwd, 'AGENTS.md');\n\n if (!exists(agentsMdPath)) {\n writeFile(agentsMdPath, `${AGENTS_MD_LINK}\\n`);\n return 'created';\n }\n\n const content = readFile(agentsMdPath);\n if (!content.includes(SAFEWORD_LINK_MARKER)) {\n writeFile(agentsMdPath, `${AGENTS_MD_LINK}\\n\\n${content}`);\n return 'modified';\n }\n\n return 'unchanged';\n}\n\n/**\n * Remove safeword link block from AGENTS.md.\n * Returns true if link was removed.\n */\nexport function removeAgentsMdLink(cwd: string): boolean {\n const agentsMdPath = join(cwd, 'AGENTS.md');\n if (!exists(agentsMdPath)) return false;\n\n const content = readFile(agentsMdPath);\n\n // Remove the entire AGENTS_MD_LINK block if present\n let newContent = content.replace(AGENTS_MD_LINK, '');\n\n // Also handle legacy single-line format (filter any remaining lines with marker)\n const lines = newContent.split('\\n').filter((line) => !line.includes(SAFEWORD_LINK_MARKER));\n\n // Remove extra blank lines and separators at the start\n while (lines.length > 0 && (lines[0].trim() === '' || lines[0].trim() === '---')) {\n lines.shift();\n }\n\n newContent = lines.join('\\n');\n\n if (newContent !== content) {\n writeFile(agentsMdPath, newContent);\n return true;\n }\n\n return false;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/fs.ts","../src/utils/output.ts"],"sourcesContent":["/**\n * File system utilities for CLI operations\n */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n rmSync,\n readdirSync,\n statSync,\n chmodSync,\n copyFileSync,\n} from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n// Get the directory of this module (for locating templates)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Get path to bundled templates directory.\n * Works in both development (src/) and production (dist/) contexts.\n */\nexport function getTemplatesDir(): string {\n // When running from dist/, __dirname is packages/cli/dist/\n // Templates are at packages/cli/templates/ (one level up)\n const fromDist = join(__dirname, '..', 'templates');\n\n // Fallback path for edge cases\n const fallback = join(__dirname, '..', '..', 'templates');\n\n if (existsSync(fromDist)) return fromDist;\n if (existsSync(fallback)) return fallback;\n\n throw new Error('Templates directory not found');\n}\n\n/**\n * Check if a path exists\n */\nexport function exists(path: string): boolean {\n return existsSync(path);\n}\n\n/**\n * Check if path is a directory\n */\nexport function isDirectory(path: string): boolean {\n return existsSync(path) && statSync(path).isDirectory();\n}\n\n/**\n * Create directory recursively\n */\nexport function ensureDir(path: string): void {\n if (!existsSync(path)) {\n mkdirSync(path, { recursive: true });\n }\n}\n\n/**\n * Read file as string\n */\nexport function readFile(path: string): string {\n return readFileSync(path, 'utf-8');\n}\n\n/**\n * Read file as string, return null if not exists\n */\nexport function readFileSafe(path: string): string | null {\n if (!existsSync(path)) return null;\n return readFileSync(path, 'utf-8');\n}\n\n/**\n * Write file, creating parent directories if needed\n */\nexport function writeFile(path: string, content: string): void {\n ensureDir(dirname(path));\n writeFileSync(path, content);\n}\n\n/**\n * Remove file or directory recursively\n */\nexport function remove(path: string): void {\n if (existsSync(path)) {\n rmSync(path, { recursive: true, force: true });\n }\n}\n\n/**\n * List files in directory\n */\nexport function listDir(path: string): string[] {\n if (!existsSync(path)) return [];\n return readdirSync(path);\n}\n\n/**\n * Copy a single file\n */\nexport function copyFile(src: string, dest: string): void {\n ensureDir(dirname(dest));\n copyFileSync(src, dest);\n}\n\n/**\n * Copy directory recursively\n */\nexport function copyDir(src: string, dest: string): void {\n ensureDir(dest);\n const entries = readdirSync(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = join(src, entry.name);\n const destPath = join(dest, entry.name);\n\n if (entry.isDirectory()) {\n copyDir(srcPath, destPath);\n } else {\n copyFileSync(srcPath, destPath);\n }\n }\n}\n\n/**\n * Make file executable\n */\nexport function makeExecutable(path: string): void {\n chmodSync(path, 0o755);\n}\n\n/**\n * Make all shell scripts in a directory executable\n */\nexport function makeScriptsExecutable(dirPath: string): void {\n if (!existsSync(dirPath)) return;\n for (const file of readdirSync(dirPath)) {\n if (file.endsWith('.sh')) {\n chmodSync(join(dirPath, file), 0o755);\n }\n }\n}\n\n/**\n * Read JSON file\n */\nexport function readJson<T = unknown>(path: string): T | null {\n const content = readFileSafe(path);\n if (!content) return null;\n try {\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\n/**\n * Write JSON file with formatting\n */\nexport function writeJson(path: string, data: unknown): void {\n writeFile(path, JSON.stringify(data, null, 2) + '\\n');\n}\n\n/**\n * Update JSON file, merging with existing content\n */\nexport function updateJson<T extends Record<string, unknown>>(\n path: string,\n updater: (existing: T | null) => T,\n): void {\n const existing = readJson<T>(path);\n const updated = updater(existing);\n writeJson(path, updated);\n}\n","/**\n * Console output utilities for consistent CLI messaging\n */\n\n/**\n * Print info message\n */\nexport function info(message: string): void {\n console.log(message);\n}\n\n/**\n * Print success message\n */\nexport function success(message: string): void {\n console.log(`✓ ${message}`);\n}\n\n/**\n * Print warning message\n */\nexport function warn(message: string): void {\n console.warn(`⚠ ${message}`);\n}\n\n/**\n * Print error message to stderr\n */\nexport function error(message: string): void {\n console.error(`✗ ${message}`);\n}\n\n/**\n * Print a blank line\n */\nexport function blank(): void {\n console.log('');\n}\n\n/**\n * Print a section header\n */\nexport function header(title: string): void {\n console.log(`\\n${title}`);\n console.log('─'.repeat(title.length));\n}\n\n/**\n * Print a list item\n */\nexport function listItem(item: string, indent = 2): void {\n console.log(`${' '.repeat(indent)}• ${item}`);\n}\n\n/**\n * Print key-value pair\n */\nexport function keyValue(key: string, value: string): void {\n console.log(` ${key}: ${value}`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -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 { isGitRepo } from '../utils/git.js';\nimport { filterOutSafewordHooks } from '../utils/hooks.js';\nimport { removeAgentsMdLink } from '../utils/agents-md.js';\n\nexport interface ResetOptions {\n yes?: boolean;\n}\n\nexport async function reset(options: ResetOptions): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n info('Nothing to remove. Project is not configured with safeword.');\n return;\n }\n\n const isNonInteractive = options.yes || !process.stdin.isTTY;\n\n // Confirmation (in interactive mode without --yes)\n if (!isNonInteractive) {\n // In a real implementation, we'd prompt here\n // For now, non-TTY mode auto-confirms\n }\n\n header('Safeword Reset');\n info('Removing safeword configuration...');\n\n const removed: string[] = [];\n\n try {\n // 1. Remove .safeword directory\n if (exists(safewordDir)) {\n remove(safewordDir);\n removed.push('.safeword/');\n success('Removed .safeword directory');\n }\n\n // 2. Remove safeword hooks from .claude/settings.json\n const settingsPath = join(cwd, '.claude', 'settings.json');\n\n if (exists(settingsPath)) {\n info('\\nRemoving hooks from .claude/settings.json...');\n\n interface SettingsJson {\n hooks?: Record<string, unknown[]>;\n [key: string]: unknown;\n }\n\n const settings = readJson<SettingsJson>(settingsPath);\n\n if (settings?.hooks) {\n let modified = false;\n\n for (const [event, hooks] of Object.entries(settings.hooks)) {\n if (Array.isArray(hooks)) {\n const filtered = filterOutSafewordHooks(hooks);\n if (filtered.length !== hooks.length) {\n settings.hooks[event] = filtered;\n modified = true;\n }\n }\n }\n\n if (modified) {\n writeJson(settingsPath, settings);\n removed.push('.claude/settings.json (hooks)');\n success('Removed safeword hooks');\n }\n }\n }\n\n // 3. Remove safeword skills\n const skillsDir = join(cwd, '.claude', 'skills');\n\n if (exists(skillsDir)) {\n info('\\nRemoving safeword skills...');\n\n const skills = listDir(skillsDir);\n for (const skill of skills) {\n if (skill.startsWith('safeword-')) {\n remove(join(skillsDir, skill));\n removed.push(`.claude/skills/${skill}/`);\n }\n }\n\n if (removed.some(r => r.includes('skills'))) {\n success('Removed safeword skills');\n }\n }\n\n // 3.5. Remove safeword slash commands\n const commandsDir = join(cwd, '.claude', 'commands');\n const safewordCommands = ['quality-review.md', 'arch-review.md', 'lint.md'];\n\n if (exists(commandsDir)) {\n info('\\nRemoving safeword commands...');\n\n let commandsRemoved = false;\n for (const cmd of safewordCommands) {\n const cmdPath = join(commandsDir, cmd);\n if (exists(cmdPath)) {\n remove(cmdPath);\n removed.push(`.claude/commands/${cmd}`);\n commandsRemoved = true;\n }\n }\n\n if (commandsRemoved) {\n success('Removed safeword commands');\n }\n }\n\n // 3.6. Remove MCP servers from .mcp.json\n const mcpConfigPath = join(cwd, '.mcp.json');\n\n if (exists(mcpConfigPath)) {\n info('\\nRemoving MCP servers...');\n\n interface McpConfig {\n mcpServers?: Record<string, unknown>;\n [key: string]: unknown;\n }\n\n const mcpConfig = readJson<McpConfig>(mcpConfigPath);\n\n if (mcpConfig?.mcpServers) {\n // Remove safeword MCP servers\n delete mcpConfig.mcpServers.context7;\n delete mcpConfig.mcpServers.playwright;\n\n // If no servers left, remove the file\n if (Object.keys(mcpConfig.mcpServers).length === 0) {\n remove(mcpConfigPath);\n removed.push('.mcp.json');\n } else {\n writeJson(mcpConfigPath, mcpConfig);\n removed.push('.mcp.json (context7, playwright)');\n }\n\n success('Removed MCP servers');\n }\n }\n\n // 4. Remove 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":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/setup.ts","../src/utils/project-detector.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 { 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 // Create ESLint config\n const eslintConfigPath = join(cwd, 'eslint.config.mjs');\n if (!exists(eslintConfigPath)) {\n writeFile(eslintConfigPath, getEslintConfig(projectType));\n created.push('eslint.config.mjs');\n success('Created eslint.config.mjs');\n } else {\n info('eslint.config.mjs already exists');\n }\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 // Add prepare script for Husky (runs on npm install)\n if (!scripts.prepare) {\n scripts.prepare = 'husky';\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-config-prettier',\n 'markdownlint-cli2',\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');\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\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 (err) {\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 runs lint-staged\n const huskyPreCommit = join(huskyDir, 'pre-commit');\n writeFile(huskyPreCommit, 'npx lint-staged\\n');\n\n created.push('.husky/pre-commit');\n success('Configured Husky with lint-staged pre-commit hook');\n } catch (err) {\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: mkdir -p .husky && echo \"npx lint-staged\" > .husky/pre-commit');\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 * Project type detection from package.json\n *\n * Detects frameworks and tools used in the project to configure\n * appropriate linting rules.\n */\n\nexport interface PackageJson {\n name?: string;\n version?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport interface ProjectType {\n typescript: boolean;\n react: boolean;\n nextjs: boolean;\n astro: boolean;\n vue: boolean;\n nuxt: boolean;\n svelte: boolean;\n sveltekit: boolean;\n electron: boolean;\n}\n\n/**\n * Detects project type from package.json contents\n */\nexport function detectProjectType(packageJson: PackageJson): ProjectType {\n const deps = packageJson.dependencies || {};\n const devDeps = packageJson.devDependencies || {};\n const allDeps = { ...deps, ...devDeps };\n\n const hasTypescript = 'typescript' in allDeps;\n const hasReact = 'react' in deps || 'react' in devDeps;\n const hasNextJs = 'next' in deps;\n const hasAstro = 'astro' in deps || 'astro' in devDeps;\n const hasVue = 'vue' in deps || 'vue' in devDeps;\n const hasNuxt = 'nuxt' in deps;\n const hasSvelte = 'svelte' in deps || 'svelte' in devDeps;\n const hasSvelteKit = '@sveltejs/kit' in deps || '@sveltejs/kit' in devDeps;\n const hasElectron = 'electron' in deps || 'electron' in devDeps;\n\n return {\n typescript: hasTypescript,\n react: hasReact || hasNextJs, // Next.js implies React\n nextjs: hasNextJs,\n astro: hasAstro,\n vue: hasVue || hasNuxt, // Nuxt implies Vue\n nuxt: hasNuxt,\n svelte: hasSvelte || hasSvelteKit, // SvelteKit implies Svelte\n sveltekit: hasSvelteKit,\n electron: hasElectron,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Review code changes against project architecture guidelines and layer boundaries
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Architecture Review
|
|
6
|
-
|
|
7
|
-
Review code changes against project architecture guidelines.
|
|
8
|
-
|
|
9
|
-
## When to Use
|
|
10
|
-
|
|
11
|
-
- Before committing significant changes
|
|
12
|
-
- When adding new modules or dependencies
|
|
13
|
-
- When refactoring existing code
|
|
14
|
-
|
|
15
|
-
## What It Does
|
|
16
|
-
|
|
17
|
-
1. Checks code against architecture guide
|
|
18
|
-
2. Verifies layer boundaries
|
|
19
|
-
3. Validates dependency directions
|
|
20
|
-
4. Reports violations
|
|
21
|
-
|
|
22
|
-
## Usage
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
/arch-review
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Then describe the changes to review.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Safeword: Git pre-commit hook
|
|
3
|
-
# Runs linting on staged files before commit
|
|
4
|
-
|
|
5
|
-
# Change to project directory if set
|
|
6
|
-
[ -n "$CLAUDE_PROJECT_DIR" ] && cd "$CLAUDE_PROJECT_DIR"
|
|
7
|
-
|
|
8
|
-
# Run linting if available
|
|
9
|
-
if [ -f "package.json" ] && grep -q '"lint"' package.json; then
|
|
10
|
-
npm run lint --silent || exit 1
|
|
11
|
-
fi
|
|
12
|
-
|
|
13
|
-
# Run markdown linting if available
|
|
14
|
-
if [ -f "package.json" ] && grep -q '"lint:md"' package.json; then
|
|
15
|
-
npm run lint:md --silent || exit 1
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
exit 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|