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
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-W66Z3C5H.js";
|
|
4
4
|
import {
|
|
5
5
|
VERSION
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z2SOGTNJ.js";
|
|
7
7
|
import {
|
|
8
8
|
exists,
|
|
9
9
|
header,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
readFileSafe,
|
|
14
14
|
success,
|
|
15
15
|
warn
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-GZRQL3SX.js";
|
|
17
17
|
|
|
18
18
|
// src/commands/check.ts
|
|
19
19
|
import { join } from "path";
|
|
@@ -126,4 +126,4 @@ Upgrade available for project config`);
|
|
|
126
126
|
export {
|
|
127
127
|
check
|
|
128
128
|
};
|
|
129
|
-
//# sourceMappingURL=check-
|
|
129
|
+
//# sourceMappingURL=check-US6EQLNS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/check.ts"],"sourcesContent":["/**\n * Check command - Verify project health and configuration\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, readFile, readFileSafe } from '../utils/fs.js';\nimport { info, success, warn, header, keyValue } from '../utils/output.js';\nimport { isNewerVersion } from '../utils/version.js';\n\nexport interface CheckOptions {\n offline?: boolean;\n}\n\ninterface HealthStatus {\n configured: boolean;\n projectVersion: string | null;\n cliVersion: string;\n updateAvailable: boolean;\n latestVersion: string | null;\n issues: string[];\n}\n\n/**\n * Check for latest version from npm (with timeout)\n */\nasync function checkLatestVersion(timeout = 3000): Promise<string | null> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const response = await fetch('https://registry.npmjs.org/safeword/latest', {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as { version?: string };\n return data.version ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check project configuration health\n */\nfunction checkHealth(cwd: string): HealthStatus {\n const safewordDir = join(cwd, '.safeword');\n const issues: string[] = [];\n\n // Check if configured\n if (!exists(safewordDir)) {\n return {\n configured: false,\n projectVersion: null,\n cliVersion: VERSION,\n updateAvailable: false,\n latestVersion: null,\n issues: [],\n };\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? null;\n\n // Check for required files\n const requiredFiles = ['SAFEWORD.md', 'version', 'hooks/agents-md-check.sh'];\n\n for (const file of requiredFiles) {\n if (!exists(join(safewordDir, file))) {\n issues.push(`Missing: .safeword/${file}`);\n }\n }\n\n // Check AGENTS.md link\n const agentsMdPath = join(cwd, 'AGENTS.md');\n if (exists(agentsMdPath)) {\n const content = readFile(agentsMdPath);\n if (!content.includes('@./.safeword/SAFEWORD.md')) {\n issues.push('AGENTS.md missing safeword link');\n }\n } else {\n issues.push('AGENTS.md file missing');\n }\n\n // Check .claude/settings.json\n const settingsPath = join(cwd, '.claude', 'settings.json');\n if (!exists(settingsPath)) {\n issues.push('Missing: .claude/settings.json');\n }\n\n return {\n configured: true,\n projectVersion,\n cliVersion: VERSION,\n updateAvailable: false,\n latestVersion: null,\n issues,\n };\n}\n\nexport async function check(options: CheckOptions): Promise<void> {\n const cwd = process.cwd();\n\n header('Safeword Health Check');\n\n const health = checkHealth(cwd);\n\n // Not configured\n if (!health.configured) {\n info('Not configured. Run `safeword setup` to initialize.');\n return;\n }\n\n // Show versions\n keyValue('Safeword CLI', `v${health.cliVersion}`);\n keyValue('Project config', health.projectVersion ? `v${health.projectVersion}` : 'unknown');\n\n // Check for updates (unless offline)\n if (!options.offline) {\n info('\\nChecking for updates...');\n const latestVersion = await checkLatestVersion();\n\n if (latestVersion) {\n health.latestVersion = latestVersion;\n health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);\n\n if (health.updateAvailable) {\n warn(`Update available: v${latestVersion}`);\n info('Run `npm install -g safeword` to upgrade');\n } else {\n success('CLI is up to date');\n }\n } else {\n warn(\"Couldn't check for updates (offline?)\");\n }\n } else {\n info('\\nSkipped update check (offline mode)');\n }\n\n // Check project version vs CLI version\n if (health.projectVersion && isNewerVersion(health.cliVersion, health.projectVersion)) {\n warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);\n info('Consider upgrading the CLI');\n } else if (health.projectVersion && isNewerVersion(health.projectVersion, health.cliVersion)) {\n info(`\\nUpgrade available for project config`);\n info(\n `Run \\`safeword upgrade\\` to update from v${health.projectVersion} to v${health.cliVersion}`,\n );\n }\n\n // Show issues\n if (health.issues.length > 0) {\n header('Issues Found');\n for (const issue of health.issues) {\n warn(issue);\n }\n info('\\nRun `safeword upgrade` to repair configuration');\n } else {\n success('\\nConfiguration is healthy');\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAIA,SAAS,YAAY;AAsBrB,eAAe,mBAAmB,UAAU,KAA8B;AACxE,MAAI;AACF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,UAAM,WAAW,MAAM,MAAM,8CAA8C;AAAA,MACzE,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,iBAAa,SAAS;AAEtB,QAAI,CAAC,SAAS,GAAI,QAAO;AAEzB,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,WAAW;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,SAAS,YAAY,KAA2B;AAC9C,QAAM,cAAc,KAAK,KAAK,WAAW;AACzC,QAAM,SAAmB,CAAC;AAG1B,MAAI,CAAC,OAAO,WAAW,GAAG;AACxB,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,eAAe;AAAA,MACf,QAAQ,CAAC;AAAA,IACX;AAAA,EACF;AAGA,QAAM,cAAc,KAAK,aAAa,SAAS;AAC/C,QAAM,iBAAiB,aAAa,WAAW,GAAG,KAAK,KAAK;AAG5D,QAAM,gBAAgB,CAAC,eAAe,WAAW,0BAA0B;AAE3E,aAAW,QAAQ,eAAe;AAChC,QAAI,CAAC,OAAO,KAAK,aAAa,IAAI,CAAC,GAAG;AACpC,aAAO,KAAK,sBAAsB,IAAI,EAAE;AAAA,IAC1C;AAAA,EACF;AAGA,QAAM,eAAe,KAAK,KAAK,WAAW;AAC1C,MAAI,OAAO,YAAY,GAAG;AACxB,UAAM,UAAU,SAAS,YAAY;AACrC,QAAI,CAAC,QAAQ,SAAS,0BAA0B,GAAG;AACjD,aAAO,KAAK,iCAAiC;AAAA,IAC/C;AAAA,EACF,OAAO;AACL,WAAO,KAAK,wBAAwB;AAAA,EACtC;AAGA,QAAM,eAAe,KAAK,KAAK,WAAW,eAAe;AACzD,MAAI,CAAC,OAAO,YAAY,GAAG;AACzB,WAAO,KAAK,gCAAgC;AAAA,EAC9C;AAEA,SAAO;AAAA,IACL,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf;AAAA,EACF;AACF;AAEA,eAAsB,MAAM,SAAsC;AAChE,QAAM,MAAM,QAAQ,IAAI;AAExB,SAAO,uBAAuB;AAE9B,QAAM,SAAS,YAAY,GAAG;AAG9B,MAAI,CAAC,OAAO,YAAY;AACtB,SAAK,qDAAqD;AAC1D;AAAA,EACF;AAGA,WAAS,gBAAgB,IAAI,OAAO,UAAU,EAAE;AAChD,WAAS,kBAAkB,OAAO,iBAAiB,IAAI,OAAO,cAAc,KAAK,SAAS;AAG1F,MAAI,CAAC,QAAQ,SAAS;AACpB,SAAK,2BAA2B;AAChC,UAAM,gBAAgB,MAAM,mBAAmB;AAE/C,QAAI,eAAe;AACjB,aAAO,gBAAgB;AACvB,aAAO,kBAAkB,eAAe,OAAO,YAAY,aAAa;AAExE,UAAI,OAAO,iBAAiB;AAC1B,aAAK,sBAAsB,aAAa,EAAE;AAC1C,aAAK,0CAA0C;AAAA,MACjD,OAAO;AACL,gBAAQ,mBAAmB;AAAA,MAC7B;AAAA,IACF,OAAO;AACL,WAAK,uCAAuC;AAAA,IAC9C;AAAA,EACF,OAAO;AACL,SAAK,uCAAuC;AAAA,EAC9C;AAGA,MAAI,OAAO,kBAAkB,eAAe,OAAO,YAAY,OAAO,cAAc,GAAG;AACrF,SAAK,oBAAoB,OAAO,cAAc,yBAAyB,OAAO,UAAU,GAAG;AAC3F,SAAK,4BAA4B;AAAA,EACnC,WAAW,OAAO,kBAAkB,eAAe,OAAO,gBAAgB,OAAO,UAAU,GAAG;AAC5F,SAAK;AAAA,qCAAwC;AAC7C;AAAA,MACE,4CAA4C,OAAO,cAAc,QAAQ,OAAO,UAAU;AAAA,IAC5F;AAAA,EACF;AAGA,MAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,WAAO,cAAc;AACrB,eAAW,SAAS,OAAO,QAAQ;AACjC,WAAK,KAAK;AAAA,IACZ;AACA,SAAK,kDAAkD;AAAA,EACzD,OAAO;AACL,YAAQ,4BAA4B;AAAA,EACtC;AACF;","names":[]}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureDir,
|
|
3
|
+
exists,
|
|
4
|
+
makeExecutable,
|
|
5
|
+
readFile,
|
|
6
|
+
writeFile
|
|
7
|
+
} from "./chunk-GZRQL3SX.js";
|
|
8
|
+
|
|
9
|
+
// src/utils/git.ts
|
|
10
|
+
import { execSync } from "child_process";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
var MARKER_START = "# SAFEWORD_ARCH_CHECK_START";
|
|
13
|
+
var MARKER_END = "# SAFEWORD_ARCH_CHECK_END";
|
|
14
|
+
function isGitRepo(cwd) {
|
|
15
|
+
return exists(join(cwd, ".git"));
|
|
16
|
+
}
|
|
17
|
+
function getHookContent() {
|
|
18
|
+
return `
|
|
19
|
+
${MARKER_START}
|
|
20
|
+
# Safeword architecture check
|
|
21
|
+
# This section is managed by safeword - do not edit manually
|
|
22
|
+
if [ -f ".safeword/hooks/pre-commit.sh" ]; then
|
|
23
|
+
bash .safeword/hooks/pre-commit.sh
|
|
24
|
+
fi
|
|
25
|
+
${MARKER_END}
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
function installGitHook(cwd) {
|
|
29
|
+
const hooksDir = join(cwd, ".git", "hooks");
|
|
30
|
+
const hookPath = join(hooksDir, "pre-commit");
|
|
31
|
+
ensureDir(hooksDir);
|
|
32
|
+
let content = "";
|
|
33
|
+
if (exists(hookPath)) {
|
|
34
|
+
content = readFile(hookPath);
|
|
35
|
+
if (content.includes(MARKER_START)) {
|
|
36
|
+
content = removeMarkerSection(content);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
content = "#!/bin/bash\n";
|
|
40
|
+
}
|
|
41
|
+
content = content.trimEnd() + "\n" + getHookContent();
|
|
42
|
+
writeFile(hookPath, content);
|
|
43
|
+
makeExecutable(hookPath);
|
|
44
|
+
}
|
|
45
|
+
function removeGitHook(cwd) {
|
|
46
|
+
const hookPath = join(cwd, ".git", "hooks", "pre-commit");
|
|
47
|
+
if (!exists(hookPath)) return;
|
|
48
|
+
let content = readFile(hookPath);
|
|
49
|
+
if (!content.includes(MARKER_START)) return;
|
|
50
|
+
content = removeMarkerSection(content);
|
|
51
|
+
writeFile(hookPath, content);
|
|
52
|
+
}
|
|
53
|
+
function removeMarkerSection(content) {
|
|
54
|
+
const lines = content.split("\n");
|
|
55
|
+
const result = [];
|
|
56
|
+
let inMarkerSection = false;
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
if (line.includes(MARKER_START)) {
|
|
59
|
+
inMarkerSection = true;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (line.includes(MARKER_END)) {
|
|
63
|
+
inMarkerSection = false;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (!inMarkerSection) {
|
|
67
|
+
result.push(line);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result.join("\n").trim() + "\n";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/utils/hooks.ts
|
|
74
|
+
function isHookObject(h) {
|
|
75
|
+
return typeof h === "object" && h !== null && "command" in h && typeof h.command === "string";
|
|
76
|
+
}
|
|
77
|
+
function isSafewordHook(h) {
|
|
78
|
+
return isHookObject(h) && h.command.includes(".safeword");
|
|
79
|
+
}
|
|
80
|
+
function filterOutSafewordHooks(hooks) {
|
|
81
|
+
return hooks.filter((h) => !isSafewordHook(h));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/templates/content.ts
|
|
85
|
+
var AGENTS_MD_LINK = "**\u26A0\uFE0F ALWAYS READ FIRST: @./.safeword/SAFEWORD.md**";
|
|
86
|
+
var PRETTIERRC = `{
|
|
87
|
+
"semi": true,
|
|
88
|
+
"singleQuote": true,
|
|
89
|
+
"tabWidth": 2,
|
|
90
|
+
"trailingComma": "es5",
|
|
91
|
+
"printWidth": 100
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
// src/templates/config.ts
|
|
96
|
+
function getEslintConfig(options) {
|
|
97
|
+
const imports = ['import js from "@eslint/js";'];
|
|
98
|
+
const configs = ["js.configs.recommended"];
|
|
99
|
+
if (options.typescript) {
|
|
100
|
+
imports.push('import tseslint from "typescript-eslint";');
|
|
101
|
+
configs.push("...tseslint.configs.recommended");
|
|
102
|
+
}
|
|
103
|
+
if (options.react || options.nextjs) {
|
|
104
|
+
imports.push('import react from "eslint-plugin-react";');
|
|
105
|
+
imports.push('import reactHooks from "eslint-plugin-react-hooks";');
|
|
106
|
+
configs.push("react.configs.flat.recommended");
|
|
107
|
+
configs.push('react.configs.flat["jsx-runtime"]');
|
|
108
|
+
configs.push(
|
|
109
|
+
'{ plugins: { "react-hooks": reactHooks }, rules: reactHooks.configs.recommended.rules }'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return `${imports.join("\n")}
|
|
113
|
+
|
|
114
|
+
export default [
|
|
115
|
+
${configs.join(",\n ")},
|
|
116
|
+
{
|
|
117
|
+
ignores: ["node_modules/", "dist/", ".next/", "build/"],
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
var SETTINGS_HOOKS = {
|
|
123
|
+
SessionStart: [
|
|
124
|
+
{
|
|
125
|
+
command: "bash .safeword/hooks/agents-md-check.sh",
|
|
126
|
+
description: "Safeword: Verify AGENTS.md link"
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
UserPromptSubmit: [
|
|
130
|
+
{
|
|
131
|
+
command: "bash .safeword/hooks/inject-timestamp.sh",
|
|
132
|
+
description: "Safeword: Inject current timestamp"
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
PostToolUse: [
|
|
136
|
+
{
|
|
137
|
+
command: "bash .safeword/hooks/post-tool.sh 2>/dev/null || true",
|
|
138
|
+
description: "Safeword: Post-tool validation"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/utils/agents-md.ts
|
|
144
|
+
import { join as join2 } from "path";
|
|
145
|
+
var SAFEWORD_LINK_MARKER = "@./.safeword/SAFEWORD.md";
|
|
146
|
+
function ensureAgentsMdLink(cwd) {
|
|
147
|
+
const agentsMdPath = join2(cwd, "AGENTS.md");
|
|
148
|
+
if (!exists(agentsMdPath)) {
|
|
149
|
+
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
150
|
+
`);
|
|
151
|
+
return "created";
|
|
152
|
+
}
|
|
153
|
+
const content = readFile(agentsMdPath);
|
|
154
|
+
if (!content.includes(SAFEWORD_LINK_MARKER)) {
|
|
155
|
+
writeFile(agentsMdPath, `${AGENTS_MD_LINK}
|
|
156
|
+
|
|
157
|
+
${content}`);
|
|
158
|
+
return "modified";
|
|
159
|
+
}
|
|
160
|
+
return "unchanged";
|
|
161
|
+
}
|
|
162
|
+
function removeAgentsMdLink(cwd) {
|
|
163
|
+
const agentsMdPath = join2(cwd, "AGENTS.md");
|
|
164
|
+
if (!exists(agentsMdPath)) return false;
|
|
165
|
+
const content = readFile(agentsMdPath);
|
|
166
|
+
const lines = content.split("\n");
|
|
167
|
+
const filteredLines = lines.filter((line) => !line.includes(SAFEWORD_LINK_MARKER));
|
|
168
|
+
while (filteredLines.length > 0 && filteredLines[0].trim() === "") {
|
|
169
|
+
filteredLines.shift();
|
|
170
|
+
}
|
|
171
|
+
const newContent = filteredLines.join("\n");
|
|
172
|
+
if (newContent !== content) {
|
|
173
|
+
writeFile(agentsMdPath, newContent);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export {
|
|
180
|
+
isGitRepo,
|
|
181
|
+
installGitHook,
|
|
182
|
+
removeGitHook,
|
|
183
|
+
filterOutSafewordHooks,
|
|
184
|
+
PRETTIERRC,
|
|
185
|
+
getEslintConfig,
|
|
186
|
+
SETTINGS_HOOKS,
|
|
187
|
+
ensureAgentsMdLink,
|
|
188
|
+
removeAgentsMdLink
|
|
189
|
+
};
|
|
190
|
+
//# sourceMappingURL=chunk-2XWIUEQK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/git.ts","../src/utils/hooks.ts","../src/templates/content.ts","../src/templates/config.ts","../src/utils/agents-md.ts"],"sourcesContent":["/**\n * Git utilities for CLI operations\n */\n\nimport { execSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { exists, readFile, writeFile, ensureDir, makeExecutable } from './fs.js';\n\nconst MARKER_START = '# SAFEWORD_ARCH_CHECK_START';\nconst MARKER_END = '# SAFEWORD_ARCH_CHECK_END';\n\n/**\n * Check if directory is a git repository\n */\nexport function isGitRepo(cwd: string): boolean {\n return exists(join(cwd, '.git'));\n}\n\n/**\n * Initialize a git repository\n */\nexport function initGitRepo(cwd: string): void {\n execSync('git init', { cwd, stdio: 'pipe' });\n}\n\n/**\n * Get the pre-commit hook content to add\n */\nfunction getHookContent(): string {\n return `\n${MARKER_START}\n# Safeword architecture check\n# This section is managed by safeword - do not edit manually\nif [ -f \".safeword/hooks/pre-commit.sh\" ]; then\n bash .safeword/hooks/pre-commit.sh\nfi\n${MARKER_END}\n`;\n}\n\n/**\n * Install safeword markers into pre-commit hook\n */\nexport function installGitHook(cwd: string): void {\n const hooksDir = join(cwd, '.git', 'hooks');\n const hookPath = join(hooksDir, 'pre-commit');\n\n ensureDir(hooksDir);\n\n let content = '';\n\n if (exists(hookPath)) {\n content = readFile(hookPath);\n\n // Check if already has safeword markers\n if (content.includes(MARKER_START)) {\n // Remove existing safeword section and re-add (update)\n content = removeMarkerSection(content);\n }\n } else {\n // Create new hook file with shebang\n content = '#!/bin/bash\\n';\n }\n\n // Add safeword section\n content = content.trimEnd() + '\\n' + getHookContent();\n\n writeFile(hookPath, content);\n makeExecutable(hookPath);\n}\n\n/**\n * Remove safeword markers from pre-commit hook\n */\nexport function removeGitHook(cwd: string): void {\n const hookPath = join(cwd, '.git', 'hooks', 'pre-commit');\n\n if (!exists(hookPath)) return;\n\n let content = readFile(hookPath);\n\n if (!content.includes(MARKER_START)) return;\n\n content = removeMarkerSection(content);\n\n // If only shebang remains, we could delete the file\n // but safer to leave it\n writeFile(hookPath, content);\n}\n\n/**\n * Remove the section between markers (inclusive)\n */\nfunction removeMarkerSection(content: string): string {\n const lines = content.split('\\n');\n const result: string[] = [];\n let inMarkerSection = false;\n\n for (const line of lines) {\n if (line.includes(MARKER_START)) {\n inMarkerSection = true;\n continue;\n }\n if (line.includes(MARKER_END)) {\n inMarkerSection = false;\n continue;\n }\n if (!inMarkerSection) {\n result.push(line);\n }\n }\n\n return result.join('\\n').trim() + '\\n';\n}\n\n/**\n * Check if git hooks have safeword markers\n */\nexport function hasGitHook(cwd: string): boolean {\n const hookPath = join(cwd, '.git', 'hooks', 'pre-commit');\n if (!exists(hookPath)) return false;\n const content = readFile(hookPath);\n return content.includes(MARKER_START);\n}\n","/**\n * Hook utilities for Claude Code settings\n */\n\n/**\n * Type guard to check if a value is a hook object with a command property\n */\nexport function isHookObject(h: unknown): h is { command: string } {\n return (\n typeof h === 'object' &&\n h !== null &&\n 'command' in h &&\n typeof (h as { command: string }).command === 'string'\n );\n}\n\n/**\n * Check if a hook is a safeword hook (command contains '.safeword')\n */\nexport function isSafewordHook(h: unknown): boolean {\n return isHookObject(h) && h.command.includes('.safeword');\n}\n\n/**\n * Filter out safeword hooks from an array of hooks\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\nexport const PRETTIERRC = `{\n \"semi\": true,\n \"singleQuote\": true,\n \"tabWidth\": 2,\n \"trailingComma\": \"es5\",\n \"printWidth\": 100\n}\n`;\n","/**\n * Configuration templates - ESLint config generation and hook settings\n */\n\nexport function getEslintConfig(options: {\n typescript?: boolean;\n react?: boolean;\n nextjs?: boolean;\n}): string {\n const imports: string[] = ['import js from \"@eslint/js\";'];\n const configs: string[] = ['js.configs.recommended'];\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 return `${imports.join('\\n')}\n\nexport default [\n ${configs.join(',\\n ')},\n {\n ignores: [\"node_modules/\", \"dist/\", \".next/\", \"build/\"],\n },\n];\n`;\n}\n\nexport const SETTINGS_HOOKS = {\n SessionStart: [\n {\n command: 'bash .safeword/hooks/agents-md-check.sh',\n description: 'Safeword: Verify AGENTS.md link',\n },\n ],\n UserPromptSubmit: [\n {\n command: 'bash .safeword/hooks/inject-timestamp.sh',\n description: 'Safeword: Inject current timestamp',\n },\n ],\n PostToolUse: [\n {\n command: 'bash .safeword/hooks/post-tool.sh 2>/dev/null || true',\n description: 'Safeword: Post-tool validation',\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 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 const lines = content.split('\\n');\n const filteredLines = lines.filter((line) => !line.includes(SAFEWORD_LINK_MARKER));\n\n // Remove extra blank lines at the start\n while (filteredLines.length > 0 && filteredLines[0].trim() === '') {\n filteredLines.shift();\n }\n\n const newContent = filteredLines.join('\\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":["join","join"]}
|
|
@@ -7,9 +7,19 @@ import {
|
|
|
7
7
|
rmSync,
|
|
8
8
|
readdirSync,
|
|
9
9
|
statSync,
|
|
10
|
-
chmodSync
|
|
10
|
+
chmodSync,
|
|
11
|
+
copyFileSync
|
|
11
12
|
} from "fs";
|
|
12
13
|
import { join, dirname } from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
function getTemplatesDir() {
|
|
17
|
+
const fromDist = join(__dirname, "..", "templates");
|
|
18
|
+
const fallback = join(__dirname, "..", "..", "templates");
|
|
19
|
+
if (existsSync(fromDist)) return fromDist;
|
|
20
|
+
if (existsSync(fallback)) return fallback;
|
|
21
|
+
throw new Error("Templates directory not found");
|
|
22
|
+
}
|
|
13
23
|
function exists(path) {
|
|
14
24
|
return existsSync(path);
|
|
15
25
|
}
|
|
@@ -38,9 +48,34 @@ function listDir(path) {
|
|
|
38
48
|
if (!existsSync(path)) return [];
|
|
39
49
|
return readdirSync(path);
|
|
40
50
|
}
|
|
51
|
+
function copyFile(src, dest) {
|
|
52
|
+
ensureDir(dirname(dest));
|
|
53
|
+
copyFileSync(src, dest);
|
|
54
|
+
}
|
|
55
|
+
function copyDir(src, dest) {
|
|
56
|
+
ensureDir(dest);
|
|
57
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const srcPath = join(src, entry.name);
|
|
60
|
+
const destPath = join(dest, entry.name);
|
|
61
|
+
if (entry.isDirectory()) {
|
|
62
|
+
copyDir(srcPath, destPath);
|
|
63
|
+
} else {
|
|
64
|
+
copyFileSync(srcPath, destPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
41
68
|
function makeExecutable(path) {
|
|
42
69
|
chmodSync(path, 493);
|
|
43
70
|
}
|
|
71
|
+
function makeScriptsExecutable(dirPath) {
|
|
72
|
+
if (!existsSync(dirPath)) return;
|
|
73
|
+
for (const file of readdirSync(dirPath)) {
|
|
74
|
+
if (file.endsWith(".sh")) {
|
|
75
|
+
chmodSync(join(dirPath, file), 493);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
44
79
|
function readJson(path) {
|
|
45
80
|
const content = readFileSafe(path);
|
|
46
81
|
if (!content) return null;
|
|
@@ -85,6 +120,7 @@ function keyValue(key, value) {
|
|
|
85
120
|
}
|
|
86
121
|
|
|
87
122
|
export {
|
|
123
|
+
getTemplatesDir,
|
|
88
124
|
exists,
|
|
89
125
|
ensureDir,
|
|
90
126
|
readFile,
|
|
@@ -92,7 +128,10 @@ export {
|
|
|
92
128
|
writeFile,
|
|
93
129
|
remove,
|
|
94
130
|
listDir,
|
|
131
|
+
copyFile,
|
|
132
|
+
copyDir,
|
|
95
133
|
makeExecutable,
|
|
134
|
+
makeScriptsExecutable,
|
|
96
135
|
readJson,
|
|
97
136
|
writeJson,
|
|
98
137
|
updateJson,
|
|
@@ -104,4 +143,4 @@ export {
|
|
|
104
143
|
listItem,
|
|
105
144
|
keyValue
|
|
106
145
|
};
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
146
|
+
//# sourceMappingURL=chunk-GZRQL3SX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["// Version is read from package.json at build time\n// This will be replaced by tsup or read dynamically\nexport const VERSION = '0.
|
|
1
|
+
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["// Version is read from package.json at build time\n// This will be replaced by tsup or read dynamically\nexport const VERSION = '0.2.0';\n"],"mappings":";AAEO,IAAM,UAAU;","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
VERSION
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Z2SOGTNJ.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
var program = new Command();
|
|
9
9
|
program.name("safeword").description("CLI for setting up and managing safeword development environments").version(VERSION);
|
|
10
10
|
program.command("setup").description("Set up safeword in the current project").option("-y, --yes", "Accept all defaults (non-interactive mode)").action(async (options) => {
|
|
11
|
-
const { setup } = await import("./setup-
|
|
11
|
+
const { setup } = await import("./setup-TSFCHD2D.js");
|
|
12
12
|
await setup(options);
|
|
13
13
|
});
|
|
14
14
|
program.command("check").description("Check project health and versions").option("--offline", "Skip remote version check").action(async (options) => {
|
|
15
|
-
const { check } = await import("./check-
|
|
15
|
+
const { check } = await import("./check-US6EQLNS.js");
|
|
16
16
|
await check(options);
|
|
17
17
|
});
|
|
18
18
|
program.command("upgrade").description("Upgrade safeword configuration to latest version").action(async () => {
|
|
19
|
-
const { upgrade } = await import("./upgrade-
|
|
19
|
+
const { upgrade } = await import("./upgrade-XDPQFSMC.js");
|
|
20
20
|
await upgrade();
|
|
21
21
|
});
|
|
22
22
|
program.command("diff").description("Preview changes that would be made by upgrade").option("-v, --verbose", "Show full diff output").action(async (options) => {
|
|
23
|
-
const { diff } = await import("./diff-
|
|
23
|
+
const { diff } = await import("./diff-72ZUEZ6A.js");
|
|
24
24
|
await diff(options);
|
|
25
25
|
});
|
|
26
26
|
program.command("reset").description("Remove safeword configuration from project").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
27
|
-
const { reset } = await import("./reset-
|
|
27
|
+
const { reset } = await import("./reset-3ACTIYYE.js");
|
|
28
28
|
await reset(options);
|
|
29
29
|
});
|
|
30
30
|
if (process.argv.length === 2) {
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HOOK_AGENTS_CHECK,
|
|
3
|
-
HOOK_POST_TOOL,
|
|
4
|
-
HOOK_PRE_COMMIT,
|
|
5
|
-
SAFEWORD_MD,
|
|
6
|
-
SKILL_QUALITY_REVIEWER
|
|
7
|
-
} from "./chunk-DB4CMUFD.js";
|
|
8
1
|
import {
|
|
9
2
|
VERSION
|
|
10
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Z2SOGTNJ.js";
|
|
11
4
|
import {
|
|
12
5
|
error,
|
|
13
6
|
exists,
|
|
7
|
+
getTemplatesDir,
|
|
14
8
|
header,
|
|
15
9
|
info,
|
|
16
10
|
listItem,
|
|
17
11
|
readFileSafe,
|
|
18
12
|
success
|
|
19
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GZRQL3SX.js";
|
|
20
14
|
|
|
21
15
|
// src/commands/diff.ts
|
|
22
16
|
import { join } from "path";
|
|
@@ -50,41 +44,50 @@ function createUnifiedDiff(oldContent, newContent, filename) {
|
|
|
50
44
|
return lines.join("\n");
|
|
51
45
|
}
|
|
52
46
|
function getFileDiffs(cwd) {
|
|
53
|
-
const
|
|
47
|
+
const templatesDir = getTemplatesDir();
|
|
54
48
|
const diffs = [];
|
|
55
49
|
const files = [
|
|
56
|
-
{
|
|
57
|
-
{
|
|
58
|
-
{
|
|
59
|
-
{
|
|
60
|
-
{
|
|
61
|
-
{
|
|
62
|
-
path: ".claude/skills/safeword-quality-reviewer/SKILL.md",
|
|
63
|
-
newContent: SKILL_QUALITY_REVIEWER
|
|
64
|
-
}
|
|
50
|
+
{ templatePath: "SAFEWORD.md", installPath: ".safeword/SAFEWORD.md" },
|
|
51
|
+
{ templatePath: "hooks/agents-md-check.sh", installPath: ".safeword/hooks/agents-md-check.sh" },
|
|
52
|
+
{ templatePath: "hooks/pre-commit.sh", installPath: ".safeword/hooks/pre-commit.sh" },
|
|
53
|
+
{ templatePath: "hooks/post-tool.sh", installPath: ".safeword/hooks/post-tool.sh" },
|
|
54
|
+
{ templatePath: "hooks/inject-timestamp.sh", installPath: ".safeword/hooks/inject-timestamp.sh" },
|
|
55
|
+
{ templatePath: "skills/safeword-quality-reviewer/SKILL.md", installPath: ".claude/skills/safeword-quality-reviewer/SKILL.md" }
|
|
65
56
|
];
|
|
57
|
+
const versionPath = join(cwd, ".safeword/version");
|
|
58
|
+
const currentVersion = readFileSafe(versionPath);
|
|
59
|
+
if (currentVersion === null) {
|
|
60
|
+
diffs.push({ path: ".safeword/version", status: "added", newContent: VERSION });
|
|
61
|
+
} else if (currentVersion.trim() !== VERSION) {
|
|
62
|
+
diffs.push({ path: ".safeword/version", status: "modified", currentContent: currentVersion, newContent: VERSION });
|
|
63
|
+
} else {
|
|
64
|
+
diffs.push({ path: ".safeword/version", status: "unchanged", currentContent: currentVersion, newContent: VERSION });
|
|
65
|
+
}
|
|
66
66
|
for (const file of files) {
|
|
67
|
-
const
|
|
68
|
-
const
|
|
67
|
+
const templateFullPath = join(templatesDir, file.templatePath);
|
|
68
|
+
const installFullPath = join(cwd, file.installPath);
|
|
69
|
+
const newContent = readFileSafe(templateFullPath);
|
|
70
|
+
if (newContent === null) continue;
|
|
71
|
+
const currentContent = readFileSafe(installFullPath);
|
|
69
72
|
if (currentContent === null) {
|
|
70
73
|
diffs.push({
|
|
71
|
-
path: file.
|
|
74
|
+
path: file.installPath,
|
|
72
75
|
status: "added",
|
|
73
|
-
newContent
|
|
76
|
+
newContent
|
|
74
77
|
});
|
|
75
|
-
} else if (currentContent.trim() !==
|
|
78
|
+
} else if (currentContent.trim() !== newContent.trim()) {
|
|
76
79
|
diffs.push({
|
|
77
|
-
path: file.
|
|
80
|
+
path: file.installPath,
|
|
78
81
|
status: "modified",
|
|
79
82
|
currentContent,
|
|
80
|
-
newContent
|
|
83
|
+
newContent
|
|
81
84
|
});
|
|
82
85
|
} else {
|
|
83
86
|
diffs.push({
|
|
84
|
-
path: file.
|
|
87
|
+
path: file.installPath,
|
|
85
88
|
status: "unchanged",
|
|
86
89
|
currentContent,
|
|
87
|
-
newContent
|
|
90
|
+
newContent
|
|
88
91
|
});
|
|
89
92
|
}
|
|
90
93
|
}
|
|
@@ -160,4 +163,4 @@ ${file.path}: (new file)`);
|
|
|
160
163
|
export {
|
|
161
164
|
diff
|
|
162
165
|
};
|
|
163
|
-
//# sourceMappingURL=diff-
|
|
166
|
+
//# sourceMappingURL=diff-72ZUEZ6A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/diff.ts"],"sourcesContent":["/**\n * Diff command - Preview changes that would be made by upgrade\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, readFileSafe, getTemplatesDir } from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\n\nexport interface DiffOptions {\n verbose?: boolean;\n}\n\ninterface FileDiff {\n path: string;\n status: 'added' | 'modified' | 'unchanged';\n currentContent?: string;\n newContent: string;\n}\n\n/**\n * Create a unified diff between two strings\n */\nfunction createUnifiedDiff(oldContent: string, newContent: string, filename: string): string {\n const oldLines = oldContent.split('\\n');\n const newLines = newContent.split('\\n');\n\n const lines: string[] = [];\n lines.push(`--- a/${filename}`);\n lines.push(`+++ b/${filename}`);\n\n // Simple diff - show all changes\n // A real implementation would use a proper diff algorithm\n let hasChanges = false;\n\n const maxLines = Math.max(oldLines.length, newLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const oldLine = oldLines[i];\n const newLine = newLines[i];\n\n if (oldLine === newLine) {\n lines.push(` ${oldLine ?? ''}`);\n } else {\n hasChanges = true;\n if (oldLine !== undefined) {\n lines.push(`-${oldLine}`);\n }\n if (newLine !== undefined) {\n lines.push(`+${newLine}`);\n }\n }\n }\n\n if (!hasChanges) {\n return '';\n }\n\n // Add context marker\n lines.splice(2, 0, `@@ -1,${oldLines.length} +1,${newLines.length} @@`);\n\n return lines.join('\\n');\n}\n\n/**\n * Get all files that would be changed by upgrade\n */\nfunction getFileDiffs(cwd: string): FileDiff[] {\n const templatesDir = getTemplatesDir();\n const diffs: FileDiff[] = [];\n\n // Define files to check (template source -> install destination)\n const files: Array<{ templatePath: string; installPath: string }> = [\n { templatePath: 'SAFEWORD.md', installPath: '.safeword/SAFEWORD.md' },\n { templatePath: 'hooks/agents-md-check.sh', installPath: '.safeword/hooks/agents-md-check.sh' },\n { templatePath: 'hooks/pre-commit.sh', installPath: '.safeword/hooks/pre-commit.sh' },\n { templatePath: 'hooks/post-tool.sh', installPath: '.safeword/hooks/post-tool.sh' },\n { templatePath: 'hooks/inject-timestamp.sh', installPath: '.safeword/hooks/inject-timestamp.sh' },\n { templatePath: 'skills/safeword-quality-reviewer/SKILL.md', installPath: '.claude/skills/safeword-quality-reviewer/SKILL.md' },\n ];\n\n // Add version file (not from templates)\n const versionPath = join(cwd, '.safeword/version');\n const currentVersion = readFileSafe(versionPath);\n if (currentVersion === null) {\n diffs.push({ path: '.safeword/version', status: 'added', newContent: VERSION });\n } else if (currentVersion.trim() !== VERSION) {\n diffs.push({ path: '.safeword/version', status: 'modified', currentContent: currentVersion, newContent: VERSION });\n } else {\n diffs.push({ path: '.safeword/version', status: 'unchanged', currentContent: currentVersion, newContent: VERSION });\n }\n\n for (const file of files) {\n const templateFullPath = join(templatesDir, file.templatePath);\n const installFullPath = join(cwd, file.installPath);\n\n const newContent = readFileSafe(templateFullPath);\n if (newContent === null) continue; // Skip if template doesn't exist\n\n const currentContent = readFileSafe(installFullPath);\n\n if (currentContent === null) {\n diffs.push({\n path: file.installPath,\n status: 'added',\n newContent,\n });\n } else if (currentContent.trim() !== newContent.trim()) {\n diffs.push({\n path: file.installPath,\n status: 'modified',\n currentContent,\n newContent,\n });\n } else {\n diffs.push({\n path: file.installPath,\n status: 'unchanged',\n currentContent,\n newContent,\n });\n }\n }\n\n return diffs;\n}\n\nexport async function diff(options: DiffOptions): 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() ?? 'unknown';\n\n header('Safeword Diff');\n info(`Changes from v${projectVersion} → v${VERSION}`);\n\n const diffs = getFileDiffs(cwd);\n\n const added = diffs.filter(d => d.status === 'added');\n const modified = diffs.filter(d => d.status === 'modified');\n const unchanged = diffs.filter(d => d.status === 'unchanged');\n\n // Summary\n info(\n `\\nSummary: ${added.length} added, ${modified.length} modified, ${unchanged.length} unchanged`,\n );\n\n // List by category\n if (added.length > 0) {\n info('\\nAdded:');\n for (const file of added) {\n listItem(file.path);\n }\n }\n\n if (modified.length > 0) {\n info('\\nModified:');\n for (const file of modified) {\n listItem(file.path);\n }\n }\n\n if (unchanged.length > 0) {\n info('\\nUnchanged:');\n for (const file of unchanged) {\n listItem(file.path);\n }\n }\n\n // Verbose output - show actual diffs\n if (options.verbose) {\n header('Detailed Changes');\n\n for (const file of modified) {\n if (file.currentContent) {\n info(`\\n${file.path}:`);\n const diffOutput = createUnifiedDiff(file.currentContent, file.newContent, file.path);\n if (diffOutput) {\n console.log(diffOutput);\n }\n }\n }\n\n for (const file of added) {\n info(`\\n${file.path}: (new file)`);\n const lines = file.newContent.split('\\n').slice(0, 10);\n for (const line of lines) {\n console.log(`+${line}`);\n }\n if (file.newContent.split('\\n').length > 10) {\n console.log('... (truncated)');\n }\n }\n }\n\n if (added.length === 0 && modified.length === 0) {\n success('\\nNo changes needed - configuration is up to date');\n } else {\n info('\\nRun `safeword upgrade` to apply these changes');\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED