safeword 0.1.0 → 0.2.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-J6DFVBCE.js → check-US6EQLNS.js} +3 -3
- package/dist/check-US6EQLNS.js.map +1 -0
- package/dist/chunk-2XWIUEQK.js +190 -0
- package/dist/chunk-2XWIUEQK.js.map +1 -0
- package/dist/{chunk-UQMQ64CB.js → chunk-GZRQL3SX.js} +41 -2
- package/dist/chunk-GZRQL3SX.js.map +1 -0
- package/dist/chunk-Z2SOGTNJ.js +7 -0
- package/dist/{chunk-WWQ4YRZN.js.map → chunk-Z2SOGTNJ.js.map} +1 -1
- package/dist/cli.js +6 -6
- package/dist/{diff-U4IELWRL.js → diff-72ZUEZ6A.js} +32 -29
- package/dist/diff-72ZUEZ6A.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{reset-XETOHTCK.js → reset-3ACTIYYE.js} +44 -27
- package/dist/reset-3ACTIYYE.js.map +1 -0
- package/dist/{setup-CLDCHROZ.js → setup-TSFCHD2D.js} +77 -48
- package/dist/setup-TSFCHD2D.js.map +1 -0
- package/dist/{upgrade-DOKWRK7J.js → upgrade-XDPQFSMC.js} +38 -50
- package/dist/upgrade-XDPQFSMC.js.map +1 -0
- package/package.json +1 -1
- package/templates/SAFEWORD.md +776 -0
- package/templates/commands/arch-review.md +24 -0
- package/templates/commands/lint.md +11 -0
- package/templates/commands/quality-review.md +23 -0
- package/templates/doc-templates/architecture-template.md +136 -0
- package/templates/doc-templates/design-doc-template.md +134 -0
- package/templates/doc-templates/test-definitions-feature.md +131 -0
- package/templates/doc-templates/user-stories-template.md +92 -0
- package/templates/guides/architecture-guide.md +423 -0
- package/templates/guides/code-philosophy.md +195 -0
- package/templates/guides/context-files-guide.md +457 -0
- package/templates/guides/data-architecture-guide.md +200 -0
- package/templates/guides/design-doc-guide.md +171 -0
- package/templates/guides/learning-extraction.md +552 -0
- package/templates/guides/llm-instruction-design.md +248 -0
- package/templates/guides/llm-prompting.md +102 -0
- package/templates/guides/tdd-best-practices.md +615 -0
- package/templates/guides/test-definitions-guide.md +334 -0
- package/templates/guides/testing-methodology.md +618 -0
- package/templates/guides/user-story-guide.md +256 -0
- package/templates/guides/zombie-process-cleanup.md +219 -0
- package/templates/hooks/agents-md-check.sh +27 -0
- package/templates/hooks/inject-timestamp.sh +2 -3
- package/templates/hooks/post-tool.sh +4 -0
- package/templates/hooks/pre-commit.sh +10 -0
- package/templates/lib/common.sh +26 -0
- package/templates/lib/jq-fallback.sh +20 -0
- package/templates/markdownlint.jsonc +25 -0
- package/templates/prompts/arch-review.md +43 -0
- package/templates/prompts/quality-review.md +10 -0
- package/templates/skills/safeword-quality-reviewer/SKILL.md +207 -0
- package/dist/check-J6DFVBCE.js.map +0 -1
- package/dist/chunk-24OB57NJ.js +0 -78
- package/dist/chunk-24OB57NJ.js.map +0 -1
- package/dist/chunk-DB4CMUFD.js +0 -157
- package/dist/chunk-DB4CMUFD.js.map +0 -1
- package/dist/chunk-UQMQ64CB.js.map +0 -1
- package/dist/chunk-WWQ4YRZN.js +0 -7
- package/dist/diff-U4IELWRL.js.map +0 -1
- package/dist/reset-XETOHTCK.js.map +0 -1
- package/dist/setup-CLDCHROZ.js.map +0 -1
- package/dist/upgrade-DOKWRK7J.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
filterOutSafewordHooks,
|
|
2
3
|
isGitRepo,
|
|
4
|
+
removeAgentsMdLink,
|
|
3
5
|
removeGitHook
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2XWIUEQK.js";
|
|
5
7
|
import {
|
|
6
8
|
error,
|
|
7
9
|
exists,
|
|
@@ -9,13 +11,11 @@ import {
|
|
|
9
11
|
info,
|
|
10
12
|
listDir,
|
|
11
13
|
listItem,
|
|
12
|
-
readFile,
|
|
13
14
|
readJson,
|
|
14
15
|
remove,
|
|
15
16
|
success,
|
|
16
|
-
writeFile,
|
|
17
17
|
writeJson
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-GZRQL3SX.js";
|
|
19
19
|
|
|
20
20
|
// src/commands/reset.ts
|
|
21
21
|
import { join } from "path";
|
|
@@ -46,9 +46,7 @@ async function reset(options) {
|
|
|
46
46
|
let modified = false;
|
|
47
47
|
for (const [event, hooks] of Object.entries(settings.hooks)) {
|
|
48
48
|
if (Array.isArray(hooks)) {
|
|
49
|
-
const filtered = hooks
|
|
50
|
-
(h) => !(typeof h === "object" && h !== null && "command" in h && typeof h.command === "string" && h.command.includes(".safeword"))
|
|
51
|
-
);
|
|
49
|
+
const filtered = filterOutSafewordHooks(hooks);
|
|
52
50
|
if (filtered.length !== hooks.length) {
|
|
53
51
|
settings.hooks[event] = filtered;
|
|
54
52
|
modified = true;
|
|
@@ -76,31 +74,50 @@ async function reset(options) {
|
|
|
76
74
|
success("Removed safeword skills");
|
|
77
75
|
}
|
|
78
76
|
}
|
|
77
|
+
const commandsDir = join(cwd, ".claude", "commands");
|
|
78
|
+
const safewordCommands = ["quality-review.md", "arch-review.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
|
+
}
|
|
79
111
|
if (isGitRepo(cwd)) {
|
|
80
112
|
info("\nRemoving git hook markers...");
|
|
81
113
|
removeGitHook(cwd);
|
|
82
114
|
removed.push(".git/hooks/pre-commit (markers)");
|
|
83
115
|
success("Removed git hook markers");
|
|
84
116
|
}
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const lines = content.split("\n");
|
|
90
|
-
const filteredLines = lines.filter((line) => !line.includes("@./.safeword/SAFEWORD.md"));
|
|
91
|
-
while (filteredLines.length > 0 && filteredLines[0].trim() === "") {
|
|
92
|
-
filteredLines.shift();
|
|
93
|
-
}
|
|
94
|
-
const newContent = filteredLines.join("\n");
|
|
95
|
-
if (newContent !== content) {
|
|
96
|
-
if (newContent.trim() === "") {
|
|
97
|
-
writeFile(agentsMdPath, newContent);
|
|
98
|
-
} else {
|
|
99
|
-
writeFile(agentsMdPath, newContent);
|
|
100
|
-
}
|
|
101
|
-
removed.push("AGENTS.md (link)");
|
|
102
|
-
success("Removed safeword link from AGENTS.md");
|
|
103
|
-
}
|
|
117
|
+
info("\nCleaning AGENTS.md...");
|
|
118
|
+
if (removeAgentsMdLink(cwd)) {
|
|
119
|
+
removed.push("AGENTS.md (link)");
|
|
120
|
+
success("Removed safeword link from AGENTS.md");
|
|
104
121
|
}
|
|
105
122
|
header("Reset Complete");
|
|
106
123
|
if (removed.length > 0) {
|
|
@@ -123,4 +140,4 @@ async function reset(options) {
|
|
|
123
140
|
export {
|
|
124
141
|
reset
|
|
125
142
|
};
|
|
126
|
-
//# sourceMappingURL=reset-
|
|
143
|
+
//# sourceMappingURL=reset-3ACTIYYE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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, removeGitHook } 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 git hook markers\n if (isGitRepo(cwd)) {\n info('\\nRemoving git hook markers...');\n removeGitHook(cwd);\n removed.push('.git/hooks/pre-commit (markers)');\n success('Removed git hook markers');\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('package.json lint/format scripts');\n listItem('ESLint/Prettier devDependencies');\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,37 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AGENTS_MD_LINK,
|
|
3
|
-
HOOK_AGENTS_CHECK,
|
|
4
|
-
HOOK_POST_TOOL,
|
|
5
|
-
HOOK_PRE_COMMIT,
|
|
6
|
-
PRETTIERRC,
|
|
7
|
-
SAFEWORD_MD,
|
|
8
|
-
SETTINGS_HOOKS,
|
|
9
|
-
SKILL_QUALITY_REVIEWER,
|
|
10
|
-
getEslintConfig
|
|
11
|
-
} from "./chunk-DB4CMUFD.js";
|
|
12
1
|
import {
|
|
13
2
|
VERSION
|
|
14
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Z2SOGTNJ.js";
|
|
15
4
|
import {
|
|
5
|
+
PRETTIERRC,
|
|
6
|
+
SETTINGS_HOOKS,
|
|
7
|
+
ensureAgentsMdLink,
|
|
8
|
+
filterOutSafewordHooks,
|
|
9
|
+
getEslintConfig,
|
|
16
10
|
installGitHook,
|
|
17
11
|
isGitRepo
|
|
18
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-2XWIUEQK.js";
|
|
19
13
|
import {
|
|
14
|
+
copyDir,
|
|
15
|
+
copyFile,
|
|
20
16
|
ensureDir,
|
|
21
17
|
error,
|
|
22
18
|
exists,
|
|
19
|
+
getTemplatesDir,
|
|
23
20
|
header,
|
|
24
21
|
info,
|
|
25
22
|
listItem,
|
|
26
|
-
|
|
27
|
-
readFile,
|
|
23
|
+
makeScriptsExecutable,
|
|
28
24
|
readJson,
|
|
29
25
|
success,
|
|
30
26
|
updateJson,
|
|
31
27
|
warn,
|
|
32
28
|
writeFile,
|
|
33
29
|
writeJson
|
|
34
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GZRQL3SX.js";
|
|
35
31
|
|
|
36
32
|
// src/commands/setup.ts
|
|
37
33
|
import { join } from "path";
|
|
@@ -75,39 +71,34 @@ async function setup(options) {
|
|
|
75
71
|
const created = [];
|
|
76
72
|
const modified = [];
|
|
77
73
|
try {
|
|
74
|
+
const templatesDir = getTemplatesDir();
|
|
78
75
|
info("\nCreating .safeword directory...");
|
|
79
76
|
ensureDir(safewordDir);
|
|
80
|
-
ensureDir(join(safewordDir, "
|
|
81
|
-
ensureDir(join(safewordDir, "
|
|
82
|
-
ensureDir(join(safewordDir, "
|
|
83
|
-
|
|
77
|
+
ensureDir(join(safewordDir, "learnings"));
|
|
78
|
+
ensureDir(join(safewordDir, "planning", "user-stories"));
|
|
79
|
+
ensureDir(join(safewordDir, "planning", "design"));
|
|
80
|
+
ensureDir(join(safewordDir, "tickets", "completed"));
|
|
81
|
+
copyFile(join(templatesDir, "SAFEWORD.md"), join(safewordDir, "SAFEWORD.md"));
|
|
84
82
|
writeFile(join(safewordDir, "version"), VERSION);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
copyDir(join(templatesDir, "guides"), join(safewordDir, "guides"));
|
|
84
|
+
copyDir(join(templatesDir, "doc-templates"), join(safewordDir, "templates"));
|
|
85
|
+
copyDir(join(templatesDir, "prompts"), join(safewordDir, "prompts"));
|
|
86
|
+
copyDir(join(templatesDir, "lib"), join(safewordDir, "lib"));
|
|
87
|
+
makeScriptsExecutable(join(safewordDir, "lib"));
|
|
88
|
+
copyDir(join(templatesDir, "hooks"), join(safewordDir, "hooks"));
|
|
89
|
+
makeScriptsExecutable(join(safewordDir, "hooks"));
|
|
91
90
|
created.push(".safeword/");
|
|
92
91
|
success("Created .safeword directory");
|
|
93
92
|
info("\nConfiguring AGENTS.md...");
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
96
|
-
const content = readFile(agentsMdPath);
|
|
97
|
-
if (!content.includes("@./.safeword/SAFEWORD.md")) {
|
|
98
|
-
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
99
|
-
|
|
100
|
-
${content}`);
|
|
101
|
-
modified.push("AGENTS.md");
|
|
102
|
-
success("Prepended link to AGENTS.md");
|
|
103
|
-
} else {
|
|
104
|
-
info("AGENTS.md already has safeword link");
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
108
|
-
`);
|
|
93
|
+
const agentsMdResult = ensureAgentsMdLink(cwd);
|
|
94
|
+
if (agentsMdResult === "created") {
|
|
109
95
|
created.push("AGENTS.md");
|
|
110
96
|
success("Created AGENTS.md");
|
|
97
|
+
} else if (agentsMdResult === "modified") {
|
|
98
|
+
modified.push("AGENTS.md");
|
|
99
|
+
success("Prepended link to AGENTS.md");
|
|
100
|
+
} else {
|
|
101
|
+
info("AGENTS.md already has safeword link");
|
|
111
102
|
}
|
|
112
103
|
info("\nRegistering Claude Code hooks...");
|
|
113
104
|
const claudeDir = join(cwd, ".claude");
|
|
@@ -118,9 +109,7 @@ ${content}`);
|
|
|
118
109
|
const hooks = existing?.hooks ?? {};
|
|
119
110
|
for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {
|
|
120
111
|
const existingHooks = hooks[event] ?? [];
|
|
121
|
-
const nonSafewordHooks = existingHooks
|
|
122
|
-
(h) => typeof h === "object" && h !== null && "command" in h && typeof h.command === "string" && !h.command.includes(".safeword")
|
|
123
|
-
);
|
|
112
|
+
const nonSafewordHooks = filterOutSafewordHooks(existingHooks);
|
|
124
113
|
hooks[event] = [...nonSafewordHooks, ...newHooks];
|
|
125
114
|
}
|
|
126
115
|
return { ...existing, hooks };
|
|
@@ -136,11 +125,35 @@ ${content}`);
|
|
|
136
125
|
process.exit(1);
|
|
137
126
|
}
|
|
138
127
|
info("\nInstalling skills...");
|
|
139
|
-
const skillsDir = join(claudeDir, "skills"
|
|
140
|
-
|
|
141
|
-
writeFile(join(skillsDir, "SKILL.md"), SKILL_QUALITY_REVIEWER);
|
|
128
|
+
const skillsDir = join(claudeDir, "skills");
|
|
129
|
+
copyDir(join(templatesDir, "skills"), skillsDir);
|
|
142
130
|
created.push(".claude/skills/safeword-quality-reviewer/");
|
|
143
131
|
success("Installed skills");
|
|
132
|
+
info("\nInstalling slash commands...");
|
|
133
|
+
const commandsDir = join(claudeDir, "commands");
|
|
134
|
+
copyDir(join(templatesDir, "commands"), commandsDir);
|
|
135
|
+
created.push(".claude/commands/");
|
|
136
|
+
success("Installed slash commands");
|
|
137
|
+
info("\nConfiguring MCP servers...");
|
|
138
|
+
const mcpConfigPath = join(cwd, ".mcp.json");
|
|
139
|
+
updateJson(mcpConfigPath, (existing) => {
|
|
140
|
+
const mcpServers = existing?.mcpServers ?? {};
|
|
141
|
+
mcpServers.context7 = {
|
|
142
|
+
command: "npx",
|
|
143
|
+
args: ["-y", "@upstash/context7-mcp@latest"]
|
|
144
|
+
};
|
|
145
|
+
mcpServers.playwright = {
|
|
146
|
+
command: "npx",
|
|
147
|
+
args: ["@playwright/mcp@latest"]
|
|
148
|
+
};
|
|
149
|
+
return { ...existing, mcpServers };
|
|
150
|
+
});
|
|
151
|
+
if (exists(mcpConfigPath)) {
|
|
152
|
+
modified.push(".mcp.json");
|
|
153
|
+
} else {
|
|
154
|
+
created.push(".mcp.json");
|
|
155
|
+
}
|
|
156
|
+
success("Configured MCP servers");
|
|
144
157
|
info("\nConfiguring linting...");
|
|
145
158
|
const packageJson = readJson(packageJsonPath);
|
|
146
159
|
if (!packageJson) {
|
|
@@ -164,6 +177,14 @@ ${content}`);
|
|
|
164
177
|
} else {
|
|
165
178
|
info(".prettierrc already exists");
|
|
166
179
|
}
|
|
180
|
+
const markdownlintPath = join(cwd, ".markdownlint.jsonc");
|
|
181
|
+
if (!exists(markdownlintPath)) {
|
|
182
|
+
copyFile(join(templatesDir, "markdownlint.jsonc"), markdownlintPath);
|
|
183
|
+
created.push(".markdownlint.jsonc");
|
|
184
|
+
success("Created .markdownlint.jsonc");
|
|
185
|
+
} else {
|
|
186
|
+
info(".markdownlint.jsonc already exists");
|
|
187
|
+
}
|
|
167
188
|
try {
|
|
168
189
|
const scripts = packageJson.scripts ?? {};
|
|
169
190
|
let scriptsModified = false;
|
|
@@ -171,10 +192,18 @@ ${content}`);
|
|
|
171
192
|
scripts.lint = "eslint .";
|
|
172
193
|
scriptsModified = true;
|
|
173
194
|
}
|
|
195
|
+
if (!scripts["lint:md"]) {
|
|
196
|
+
scripts["lint:md"] = 'markdownlint-cli2 "**/*.md" "#node_modules"';
|
|
197
|
+
scriptsModified = true;
|
|
198
|
+
}
|
|
174
199
|
if (!scripts.format) {
|
|
175
200
|
scripts.format = "prettier --write .";
|
|
176
201
|
scriptsModified = true;
|
|
177
202
|
}
|
|
203
|
+
if (!scripts["format:check"]) {
|
|
204
|
+
scripts["format:check"] = "prettier --check .";
|
|
205
|
+
scriptsModified = true;
|
|
206
|
+
}
|
|
178
207
|
if (scriptsModified) {
|
|
179
208
|
packageJson.scripts = scripts;
|
|
180
209
|
writeJson(packageJsonPath, packageJson);
|
|
@@ -234,4 +263,4 @@ Safeword ${VERSION} installed successfully!`);
|
|
|
234
263
|
export {
|
|
235
264
|
setup
|
|
236
265
|
};
|
|
237
|
-
//# sourceMappingURL=setup-
|
|
266
|
+
//# sourceMappingURL=setup-TSFCHD2D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 } 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, installGitHook } 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, getEslintConfig, SETTINGS_HOOKS } from '../templates/index.js';\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}\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\n const packageJsonPath = join(cwd, 'package.json');\n if (!exists(packageJsonPath)) {\n error('No package.json found. Run this command in a Node.js project.');\n process.exit(1);\n }\n\n const isNonInteractive = options.yes || !process.stdin.isTTY;\n\n header('Safeword Setup');\n info(`Version: ${VERSION}`);\n\n // Track created files for summary\n const created: string[] = [];\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\n const markdownlintPath = join(cwd, '.markdownlint.jsonc');\n if (!exists(markdownlintPath)) {\n copyFile(join(templatesDir, 'markdownlint.jsonc'), markdownlintPath);\n created.push('.markdownlint.jsonc');\n success('Created .markdownlint.jsonc');\n } else {\n info('.markdownlint.jsonc already exists');\n }\n\n // Add scripts to package.json\n try {\n const scripts = packageJson.scripts ?? {};\n let scriptsModified = false;\n\n if (!scripts.lint) {\n scripts.lint = 'eslint .';\n scriptsModified = true;\n }\n\n if (!scripts['lint:md']) {\n scripts['lint:md'] = 'markdownlint-cli2 \"**/*.md\" \"#node_modules\"';\n scriptsModified = true;\n }\n\n if (!scripts.format) {\n scripts.format = 'prettier --write .';\n scriptsModified = true;\n }\n\n if (!scripts['format:check']) {\n scripts['format:check'] = 'prettier --check .';\n scriptsModified = true;\n }\n\n if (scriptsModified) {\n packageJson.scripts = scripts;\n writeJson(packageJsonPath, packageJson);\n modified.push('package.json');\n success('Added lint and format scripts');\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. Handle git repository\n info('\\nConfiguring git...');\n\n if (isGitRepo(cwd)) {\n installGitHook(cwd);\n modified.push('.git/hooks/pre-commit');\n success('Installed git pre-commit hook');\n } else if (isNonInteractive) {\n warn('Skipped git initialization (non-interactive mode)');\n warn('Git hooks not installed (no repository)');\n } else {\n // Interactive mode - would prompt here\n // For now, skip in all cases\n warn('Skipped git initialization (no .git directory)');\n warn('Git hooks not installed (no repository)');\n }\n\n // 9. Note about dependencies\n info('\\nNote: Install linting dependencies manually:');\n listItem('npm install -D eslint prettier @eslint/js');\n if (projectType.typescript) {\n listItem('npm install -D typescript-eslint');\n }\n if (projectType.react) {\n listItem('npm install -D eslint-plugin-react eslint-plugin-react-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('Install linting dependencies (see above)');\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 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 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 electron: hasElectron,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compareVersions
|
|
3
3
|
} from "./chunk-W66Z3C5H.js";
|
|
4
|
-
import {
|
|
5
|
-
AGENTS_MD_LINK,
|
|
6
|
-
HOOK_AGENTS_CHECK,
|
|
7
|
-
HOOK_POST_TOOL,
|
|
8
|
-
HOOK_PRE_COMMIT,
|
|
9
|
-
SAFEWORD_MD,
|
|
10
|
-
SETTINGS_HOOKS,
|
|
11
|
-
SKILL_QUALITY_REVIEWER
|
|
12
|
-
} from "./chunk-DB4CMUFD.js";
|
|
13
4
|
import {
|
|
14
5
|
VERSION
|
|
15
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z2SOGTNJ.js";
|
|
16
7
|
import {
|
|
8
|
+
SETTINGS_HOOKS,
|
|
9
|
+
ensureAgentsMdLink,
|
|
10
|
+
filterOutSafewordHooks,
|
|
17
11
|
installGitHook,
|
|
18
12
|
isGitRepo
|
|
19
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2XWIUEQK.js";
|
|
20
14
|
import {
|
|
15
|
+
copyDir,
|
|
16
|
+
copyFile,
|
|
21
17
|
ensureDir,
|
|
22
18
|
error,
|
|
23
19
|
exists,
|
|
20
|
+
getTemplatesDir,
|
|
24
21
|
header,
|
|
25
22
|
info,
|
|
26
23
|
listItem,
|
|
27
|
-
|
|
28
|
-
readFile,
|
|
24
|
+
makeScriptsExecutable,
|
|
29
25
|
readFileSafe,
|
|
30
26
|
success,
|
|
31
27
|
updateJson,
|
|
32
28
|
writeFile
|
|
33
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-GZRQL3SX.js";
|
|
34
30
|
|
|
35
31
|
// src/commands/upgrade.ts
|
|
36
32
|
import { join } from "path";
|
|
@@ -53,41 +49,35 @@ async function upgrade() {
|
|
|
53
49
|
const updated = [];
|
|
54
50
|
const unchanged = [];
|
|
55
51
|
try {
|
|
52
|
+
const templatesDir = getTemplatesDir();
|
|
56
53
|
info("\nUpdating .safeword directory...");
|
|
57
|
-
|
|
58
|
-
ensureDir(join(safewordDir, "templates"));
|
|
59
|
-
ensureDir(join(safewordDir, "hooks"));
|
|
60
|
-
writeFile(join(safewordDir, "SAFEWORD.md"), SAFEWORD_MD);
|
|
54
|
+
copyFile(join(templatesDir, "SAFEWORD.md"), join(safewordDir, "SAFEWORD.md"));
|
|
61
55
|
writeFile(join(safewordDir, "version"), VERSION);
|
|
62
56
|
updated.push(".safeword/SAFEWORD.md");
|
|
63
57
|
updated.push(".safeword/version");
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
copyDir(join(templatesDir, "guides"), join(safewordDir, "guides"));
|
|
59
|
+
copyDir(join(templatesDir, "doc-templates"), join(safewordDir, "templates"));
|
|
60
|
+
copyDir(join(templatesDir, "prompts"), join(safewordDir, "prompts"));
|
|
61
|
+
copyDir(join(templatesDir, "lib"), join(safewordDir, "lib"));
|
|
62
|
+
makeScriptsExecutable(join(safewordDir, "lib"));
|
|
63
|
+
copyDir(join(templatesDir, "hooks"), join(safewordDir, "hooks"));
|
|
64
|
+
makeScriptsExecutable(join(safewordDir, "hooks"));
|
|
65
|
+
updated.push(".safeword/guides/");
|
|
66
|
+
updated.push(".safeword/templates/");
|
|
67
|
+
updated.push(".safeword/prompts/");
|
|
70
68
|
updated.push(".safeword/hooks/");
|
|
71
69
|
success("Updated .safeword directory");
|
|
72
70
|
info("\nVerifying AGENTS.md...");
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
const content = readFile(agentsMdPath);
|
|
76
|
-
if (!content.includes("@./.safeword/SAFEWORD.md")) {
|
|
77
|
-
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
78
|
-
|
|
79
|
-
${content}`);
|
|
80
|
-
updated.push("AGENTS.md");
|
|
81
|
-
success("Restored link to AGENTS.md");
|
|
82
|
-
} else {
|
|
83
|
-
unchanged.push("AGENTS.md");
|
|
84
|
-
info("AGENTS.md link is present");
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
88
|
-
`);
|
|
71
|
+
const agentsMdResult = ensureAgentsMdLink(cwd);
|
|
72
|
+
if (agentsMdResult === "created") {
|
|
89
73
|
updated.push("AGENTS.md");
|
|
90
74
|
success("Created AGENTS.md");
|
|
75
|
+
} else if (agentsMdResult === "modified") {
|
|
76
|
+
updated.push("AGENTS.md");
|
|
77
|
+
success("Restored link to AGENTS.md");
|
|
78
|
+
} else {
|
|
79
|
+
unchanged.push("AGENTS.md");
|
|
80
|
+
info("AGENTS.md link is present");
|
|
91
81
|
}
|
|
92
82
|
info("\nUpdating Claude Code hooks...");
|
|
93
83
|
const claudeDir = join(cwd, ".claude");
|
|
@@ -97,21 +87,19 @@ ${content}`);
|
|
|
97
87
|
const hooks = existing?.hooks ?? {};
|
|
98
88
|
for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {
|
|
99
89
|
const existingHooks = hooks[event] ?? [];
|
|
100
|
-
const nonSafewordHooks = existingHooks
|
|
101
|
-
(h) => typeof h === "object" && h !== null && "command" in h && typeof h.command === "string" && !h.command.includes(".safeword")
|
|
102
|
-
);
|
|
90
|
+
const nonSafewordHooks = filterOutSafewordHooks(existingHooks);
|
|
103
91
|
hooks[event] = [...nonSafewordHooks, ...newHooks];
|
|
104
92
|
}
|
|
105
93
|
return { ...existing, hooks };
|
|
106
94
|
});
|
|
107
95
|
updated.push(".claude/settings.json");
|
|
108
96
|
success("Updated hooks in .claude/settings.json");
|
|
109
|
-
info("\nUpdating skills...");
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
updated.push(".claude/
|
|
114
|
-
success("Updated skills");
|
|
97
|
+
info("\nUpdating skills and commands...");
|
|
98
|
+
copyDir(join(templatesDir, "skills"), join(claudeDir, "skills"));
|
|
99
|
+
copyDir(join(templatesDir, "commands"), join(claudeDir, "commands"));
|
|
100
|
+
updated.push(".claude/skills/");
|
|
101
|
+
updated.push(".claude/commands/");
|
|
102
|
+
success("Updated skills and commands");
|
|
115
103
|
if (isGitRepo(cwd)) {
|
|
116
104
|
info("\nUpdating git hooks...");
|
|
117
105
|
installGitHook(cwd);
|
|
@@ -143,4 +131,4 @@ Safeword upgraded to v${VERSION}`);
|
|
|
143
131
|
export {
|
|
144
132
|
upgrade
|
|
145
133
|
};
|
|
146
|
-
//# sourceMappingURL=upgrade-
|
|
134
|
+
//# sourceMappingURL=upgrade-XDPQFSMC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/upgrade.ts"],"sourcesContent":["/**\n * Upgrade command - Update safeword configuration to latest version\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport {\n exists,\n ensureDir,\n writeFile,\n readFileSafe,\n updateJson,\n copyDir,\n copyFile,\n getTemplatesDir,\n makeScriptsExecutable,\n} from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo, installGitHook } from '../utils/git.js';\nimport { compareVersions } from '../utils/version.js';\nimport { filterOutSafewordHooks } from '../utils/hooks.js';\nimport { ensureAgentsMdLink } from '../utils/agents-md.js';\nimport { SETTINGS_HOOKS } from '../templates/index.js';\n\nexport async function upgrade(): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? '0.0.0';\n\n // Check for downgrade\n if (compareVersions(VERSION, projectVersion) < 0) {\n error(`CLI v${VERSION} is older than project v${projectVersion}.`);\n error('Update the CLI first: npm install -g safeword');\n process.exit(1);\n }\n\n header('Safeword Upgrade');\n info(`Upgrading from v${projectVersion} to v${VERSION}`);\n\n const updated: string[] = [];\n const unchanged: string[] = [];\n\n try {\n const templatesDir = getTemplatesDir();\n\n // 1. Update .safeword directory\n info('\\nUpdating .safeword directory...');\n\n // Update core files from templates\n copyFile(join(templatesDir, 'SAFEWORD.md'), join(safewordDir, 'SAFEWORD.md'));\n writeFile(join(safewordDir, 'version'), VERSION);\n updated.push('.safeword/SAFEWORD.md');\n updated.push('.safeword/version');\n\n // Update guides, templates, prompts from templates\n copyDir(join(templatesDir, 'guides'), join(safewordDir, 'guides'));\n copyDir(join(templatesDir, 'doc-templates'), join(safewordDir, 'templates'));\n copyDir(join(templatesDir, 'prompts'), join(safewordDir, 'prompts'));\n\n // Update lib scripts and make executable\n copyDir(join(templatesDir, 'lib'), join(safewordDir, 'lib'));\n makeScriptsExecutable(join(safewordDir, 'lib'));\n\n // Update hook scripts and make executable\n copyDir(join(templatesDir, 'hooks'), join(safewordDir, 'hooks'));\n makeScriptsExecutable(join(safewordDir, 'hooks'));\n\n updated.push('.safeword/guides/');\n updated.push('.safeword/templates/');\n updated.push('.safeword/prompts/');\n updated.push('.safeword/hooks/');\n success('Updated .safeword directory');\n\n // 2. Verify AGENTS.md link\n info('\\nVerifying AGENTS.md...');\n const agentsMdResult = ensureAgentsMdLink(cwd);\n if (agentsMdResult === 'created') {\n updated.push('AGENTS.md');\n success('Created AGENTS.md');\n } else if (agentsMdResult === 'modified') {\n updated.push('AGENTS.md');\n success('Restored link to AGENTS.md');\n } else {\n unchanged.push('AGENTS.md');\n info('AGENTS.md link is present');\n }\n\n // 3. Update Claude Code hooks\n info('\\nUpdating Claude Code hooks...');\n\n const claudeDir = join(cwd, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n ensureDir(claudeDir);\n\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 updated.push('.claude/settings.json');\n success('Updated hooks in .claude/settings.json');\n\n // 4. Update skills and commands\n info('\\nUpdating skills and commands...');\n\n copyDir(join(templatesDir, 'skills'), join(claudeDir, 'skills'));\n copyDir(join(templatesDir, 'commands'), join(claudeDir, 'commands'));\n\n updated.push('.claude/skills/');\n updated.push('.claude/commands/');\n success('Updated skills and commands');\n\n // 5. Update git hooks if repo exists\n if (isGitRepo(cwd)) {\n info('\\nUpdating git hooks...');\n installGitHook(cwd);\n updated.push('.git/hooks/pre-commit');\n success('Updated git pre-commit hook');\n }\n\n // Print summary\n header('Upgrade Complete');\n\n info(`\\nVersion: v${projectVersion} → v${VERSION}`);\n\n if (updated.length > 0) {\n info('\\nUpdated:');\n for (const file of updated) {\n listItem(file);\n }\n }\n\n if (unchanged.length > 0) {\n info('\\nUnchanged:');\n for (const file of unchanged) {\n listItem(file);\n }\n }\n\n success(`\\nSafeword upgraded to v${VERSION}`);\n } catch (err) {\n error(`Upgrade failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|