safeword 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/check-3NGQ4NR5.js +129 -0
- package/dist/check-3NGQ4NR5.js.map +1 -0
- package/dist/chunk-2XWIUEQK.js +190 -0
- package/dist/chunk-2XWIUEQK.js.map +1 -0
- package/dist/chunk-GZRQL3SX.js +146 -0
- package/dist/chunk-GZRQL3SX.js.map +1 -0
- package/dist/chunk-ORQHKDT2.js +10 -0
- package/dist/chunk-ORQHKDT2.js.map +1 -0
- package/dist/chunk-W66Z3C5H.js +21 -0
- package/dist/chunk-W66Z3C5H.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +34 -0
- package/dist/cli.js.map +1 -0
- package/dist/diff-Y6QTAW4O.js +166 -0
- package/dist/diff-Y6QTAW4O.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/reset-3ACTIYYE.js +143 -0
- package/dist/reset-3ACTIYYE.js.map +1 -0
- package/dist/setup-RR4M334C.js +266 -0
- package/dist/setup-RR4M334C.js.map +1 -0
- package/dist/upgrade-6AR3DHUV.js +134 -0
- package/dist/upgrade-6AR3DHUV.js.map +1 -0
- package/package.json +44 -19
- package/{.safeword → templates}/hooks/agents-md-check.sh +0 -0
- package/{.safeword → templates}/hooks/post-tool.sh +0 -0
- package/{.safeword → templates}/hooks/pre-commit.sh +0 -0
- package/.claude/commands/arch-review.md +0 -32
- package/.claude/commands/lint.md +0 -6
- package/.claude/commands/quality-review.md +0 -13
- package/.claude/commands/setup-linting.md +0 -6
- package/.claude/hooks/auto-lint.sh +0 -6
- package/.claude/hooks/auto-quality-review.sh +0 -170
- package/.claude/hooks/check-linting-sync.sh +0 -17
- package/.claude/hooks/inject-timestamp.sh +0 -6
- package/.claude/hooks/question-protocol.sh +0 -12
- package/.claude/hooks/run-linters.sh +0 -8
- package/.claude/hooks/run-quality-review.sh +0 -76
- package/.claude/hooks/version-check.sh +0 -10
- package/.claude/mcp/README.md +0 -96
- package/.claude/mcp/arcade.sample.json +0 -9
- package/.claude/mcp/context7.sample.json +0 -7
- package/.claude/mcp/playwright.sample.json +0 -7
- package/.claude/settings.json +0 -62
- package/.claude/skills/quality-reviewer/SKILL.md +0 -190
- package/.claude/skills/safeword-quality-reviewer/SKILL.md +0 -13
- package/.env.arcade.example +0 -4
- package/.env.example +0 -11
- package/.gitmodules +0 -4
- package/.safeword/SAFEWORD.md +0 -33
- package/.safeword/eslint/eslint-base.mjs +0 -101
- package/.safeword/guides/architecture-guide.md +0 -404
- package/.safeword/guides/code-philosophy.md +0 -174
- package/.safeword/guides/context-files-guide.md +0 -405
- package/.safeword/guides/data-architecture-guide.md +0 -183
- package/.safeword/guides/design-doc-guide.md +0 -165
- package/.safeword/guides/learning-extraction.md +0 -515
- package/.safeword/guides/llm-instruction-design.md +0 -239
- package/.safeword/guides/llm-prompting.md +0 -95
- package/.safeword/guides/tdd-best-practices.md +0 -570
- package/.safeword/guides/test-definitions-guide.md +0 -243
- package/.safeword/guides/testing-methodology.md +0 -573
- package/.safeword/guides/user-story-guide.md +0 -237
- package/.safeword/guides/zombie-process-cleanup.md +0 -214
- package/.safeword/planning/002-user-story-quality-evaluation.md +0 -1840
- package/.safeword/planning/003-langsmith-eval-setup-prompt.md +0 -363
- package/.safeword/planning/004-llm-eval-test-cases.md +0 -3226
- package/.safeword/planning/005-architecture-enforcement-system.md +0 -169
- package/.safeword/planning/006-reactive-fix-prevention-research.md +0 -135
- package/.safeword/planning/011-cli-ux-vision.md +0 -330
- package/.safeword/planning/012-project-structure-cleanup.md +0 -154
- package/.safeword/planning/README.md +0 -39
- package/.safeword/planning/automation-plan-v2.md +0 -1225
- package/.safeword/planning/automation-plan-v3.md +0 -1291
- package/.safeword/planning/automation-plan.md +0 -3058
- package/.safeword/planning/design/005-cli-implementation.md +0 -343
- package/.safeword/planning/design/013-cli-self-contained-templates.md +0 -596
- package/.safeword/planning/design/013a-eslint-plugin-suite.md +0 -256
- package/.safeword/planning/design/013b-implementation-snippets.md +0 -385
- package/.safeword/planning/design/013c-config-isolation-strategy.md +0 -242
- package/.safeword/planning/design/code-philosophy-improvements.md +0 -60
- package/.safeword/planning/mcp-analysis.md +0 -545
- package/.safeword/planning/phase2-subagents-vs-skills-analysis.md +0 -451
- package/.safeword/planning/settings-improvements.md +0 -970
- package/.safeword/planning/test-definitions/005-cli-implementation.md +0 -1301
- package/.safeword/planning/test-definitions/cli-self-contained-templates.md +0 -205
- package/.safeword/planning/user-stories/001-guides-review-user-stories.md +0 -1381
- package/.safeword/planning/user-stories/003-reactive-fix-prevention.md +0 -132
- package/.safeword/planning/user-stories/004-technical-constraints.md +0 -86
- package/.safeword/planning/user-stories/005-cli-implementation.md +0 -311
- package/.safeword/planning/user-stories/cli-self-contained-templates.md +0 -172
- package/.safeword/planning/versioned-distribution.md +0 -740
- package/.safeword/prompts/arch-review.md +0 -43
- package/.safeword/prompts/quality-review.md +0 -11
- package/.safeword/scripts/arch-review.sh +0 -235
- package/.safeword/scripts/check-linting-sync.sh +0 -58
- package/.safeword/scripts/setup-linting.sh +0 -559
- package/.safeword/templates/architecture-template.md +0 -136
- package/.safeword/templates/ci/architecture-check.yml +0 -79
- package/.safeword/templates/design-doc-template.md +0 -127
- package/.safeword/templates/test-definitions-feature.md +0 -100
- package/.safeword/templates/ticket-template.md +0 -74
- package/.safeword/templates/user-stories-template.md +0 -82
- package/.safeword/tickets/001-guides-review-user-stories.md +0 -83
- package/.safeword/tickets/002-architecture-enforcement.md +0 -211
- package/.safeword/tickets/003-reactive-fix-prevention.md +0 -57
- package/.safeword/tickets/004-technical-constraints-in-user-stories.md +0 -39
- package/.safeword/tickets/005-cli-implementation.md +0 -248
- package/.safeword/tickets/006-flesh-out-skills.md +0 -43
- package/.safeword/tickets/007-flesh-out-questioning.md +0 -44
- package/.safeword/tickets/008-upgrade-questioning.md +0 -58
- package/.safeword/tickets/009-naming-conventions.md +0 -41
- package/.safeword/tickets/010-safeword-md-cleanup.md +0 -34
- package/.safeword/tickets/011-cursor-setup.md +0 -86
- package/.safeword/tickets/README.md +0 -73
- package/.safeword/version +0 -1
- package/AGENTS.md +0 -59
- package/CLAUDE.md +0 -12
- package/README.md +0 -347
- package/docs/001-cli-implementation-plan.md +0 -856
- package/docs/elite-dx-implementation-plan.md +0 -1034
- package/framework/README.md +0 -131
- package/framework/mcp/README.md +0 -96
- package/framework/mcp/arcade.sample.json +0 -8
- package/framework/mcp/context7.sample.json +0 -6
- package/framework/mcp/playwright.sample.json +0 -6
- package/framework/scripts/arch-review.sh +0 -235
- package/framework/scripts/check-linting-sync.sh +0 -58
- package/framework/scripts/load-env.sh +0 -49
- package/framework/scripts/setup-claude.sh +0 -223
- package/framework/scripts/setup-linting.sh +0 -559
- package/framework/scripts/setup-quality.sh +0 -477
- package/framework/scripts/setup-safeword.sh +0 -550
- package/framework/templates/ci/architecture-check.yml +0 -78
- package/learnings/ai-sdk-v5-breaking-changes.md +0 -178
- package/learnings/e2e-test-zombie-processes.md +0 -231
- package/learnings/milkdown-crepe-editor-property.md +0 -96
- package/learnings/prosemirror-fragment-traversal.md +0 -119
- package/packages/cli/AGENTS.md +0 -1
- package/packages/cli/ARCHITECTURE.md +0 -279
- package/packages/cli/package.json +0 -51
- package/packages/cli/src/cli.ts +0 -63
- package/packages/cli/src/commands/check.ts +0 -166
- package/packages/cli/src/commands/diff.ts +0 -209
- package/packages/cli/src/commands/reset.ts +0 -190
- package/packages/cli/src/commands/setup.ts +0 -325
- package/packages/cli/src/commands/upgrade.ts +0 -163
- package/packages/cli/src/index.ts +0 -3
- package/packages/cli/src/templates/config.ts +0 -58
- package/packages/cli/src/templates/content.ts +0 -18
- package/packages/cli/src/templates/index.ts +0 -12
- package/packages/cli/src/utils/agents-md.ts +0 -66
- package/packages/cli/src/utils/fs.ts +0 -179
- package/packages/cli/src/utils/git.ts +0 -124
- package/packages/cli/src/utils/hooks.ts +0 -29
- package/packages/cli/src/utils/output.ts +0 -60
- package/packages/cli/src/utils/project-detector.test.ts +0 -185
- package/packages/cli/src/utils/project-detector.ts +0 -44
- package/packages/cli/src/utils/version.ts +0 -28
- package/packages/cli/src/version.ts +0 -6
- package/packages/cli/templates/SAFEWORD.md +0 -776
- package/packages/cli/templates/doc-templates/architecture-template.md +0 -136
- package/packages/cli/templates/doc-templates/design-doc-template.md +0 -134
- package/packages/cli/templates/doc-templates/test-definitions-feature.md +0 -131
- package/packages/cli/templates/doc-templates/ticket-template.md +0 -82
- package/packages/cli/templates/doc-templates/user-stories-template.md +0 -92
- package/packages/cli/templates/guides/architecture-guide.md +0 -423
- package/packages/cli/templates/guides/code-philosophy.md +0 -195
- package/packages/cli/templates/guides/context-files-guide.md +0 -457
- package/packages/cli/templates/guides/data-architecture-guide.md +0 -200
- package/packages/cli/templates/guides/design-doc-guide.md +0 -171
- package/packages/cli/templates/guides/learning-extraction.md +0 -552
- package/packages/cli/templates/guides/llm-instruction-design.md +0 -248
- package/packages/cli/templates/guides/llm-prompting.md +0 -102
- package/packages/cli/templates/guides/tdd-best-practices.md +0 -615
- package/packages/cli/templates/guides/test-definitions-guide.md +0 -334
- package/packages/cli/templates/guides/testing-methodology.md +0 -618
- package/packages/cli/templates/guides/user-story-guide.md +0 -256
- package/packages/cli/templates/guides/zombie-process-cleanup.md +0 -219
- package/packages/cli/templates/hooks/agents-md-check.sh +0 -27
- package/packages/cli/templates/hooks/post-tool.sh +0 -4
- package/packages/cli/templates/hooks/pre-commit.sh +0 -10
- package/packages/cli/templates/prompts/arch-review.md +0 -43
- package/packages/cli/templates/prompts/quality-review.md +0 -10
- package/packages/cli/templates/skills/safeword-quality-reviewer/SKILL.md +0 -207
- package/packages/cli/tests/commands/check.test.ts +0 -129
- package/packages/cli/tests/commands/cli.test.ts +0 -89
- package/packages/cli/tests/commands/diff.test.ts +0 -115
- package/packages/cli/tests/commands/reset.test.ts +0 -310
- package/packages/cli/tests/commands/self-healing.test.ts +0 -170
- package/packages/cli/tests/commands/setup-blocking.test.ts +0 -71
- package/packages/cli/tests/commands/setup-core.test.ts +0 -135
- package/packages/cli/tests/commands/setup-git.test.ts +0 -139
- package/packages/cli/tests/commands/setup-hooks.test.ts +0 -334
- package/packages/cli/tests/commands/setup-linting.test.ts +0 -189
- package/packages/cli/tests/commands/setup-noninteractive.test.ts +0 -80
- package/packages/cli/tests/commands/setup-templates.test.ts +0 -181
- package/packages/cli/tests/commands/upgrade.test.ts +0 -215
- package/packages/cli/tests/helpers.ts +0 -243
- package/packages/cli/tests/npm-package.test.ts +0 -83
- package/packages/cli/tests/technical-constraints.test.ts +0 -96
- package/packages/cli/tsconfig.json +0 -25
- package/packages/cli/tsup.config.ts +0 -11
- package/packages/cli/vitest.config.ts +0 -23
- package/promptfoo.yaml +0 -3270
- /package/{framework → templates}/SAFEWORD.md +0 -0
- /package/{packages/cli/templates → templates}/commands/arch-review.md +0 -0
- /package/{packages/cli/templates → templates}/commands/lint.md +0 -0
- /package/{packages/cli/templates → templates}/commands/quality-review.md +0 -0
- /package/{framework/templates → templates/doc-templates}/architecture-template.md +0 -0
- /package/{framework/templates → templates/doc-templates}/design-doc-template.md +0 -0
- /package/{framework/templates → templates/doc-templates}/test-definitions-feature.md +0 -0
- /package/{framework/templates → templates/doc-templates}/ticket-template.md +0 -0
- /package/{framework/templates → templates/doc-templates}/user-stories-template.md +0 -0
- /package/{framework → templates}/guides/architecture-guide.md +0 -0
- /package/{framework → templates}/guides/code-philosophy.md +0 -0
- /package/{framework → templates}/guides/context-files-guide.md +0 -0
- /package/{framework → templates}/guides/data-architecture-guide.md +0 -0
- /package/{framework → templates}/guides/design-doc-guide.md +0 -0
- /package/{framework → templates}/guides/learning-extraction.md +0 -0
- /package/{framework → templates}/guides/llm-instruction-design.md +0 -0
- /package/{framework → templates}/guides/llm-prompting.md +0 -0
- /package/{framework → templates}/guides/tdd-best-practices.md +0 -0
- /package/{framework → templates}/guides/test-definitions-guide.md +0 -0
- /package/{framework → templates}/guides/testing-methodology.md +0 -0
- /package/{framework → templates}/guides/user-story-guide.md +0 -0
- /package/{framework → templates}/guides/zombie-process-cleanup.md +0 -0
- /package/{packages/cli/templates → templates}/hooks/inject-timestamp.sh +0 -0
- /package/{packages/cli/templates → templates}/lib/common.sh +0 -0
- /package/{packages/cli/templates → templates}/lib/jq-fallback.sh +0 -0
- /package/{packages/cli/templates → templates}/markdownlint.jsonc +0 -0
- /package/{framework → templates}/prompts/arch-review.md +0 -0
- /package/{framework → templates}/prompts/quality-review.md +0 -0
- /package/{framework/skills/quality-reviewer → templates/skills/safeword-quality-reviewer}/SKILL.md +0 -0
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File system utilities for CLI operations
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
existsSync,
|
|
7
|
-
mkdirSync,
|
|
8
|
-
readFileSync,
|
|
9
|
-
writeFileSync,
|
|
10
|
-
rmSync,
|
|
11
|
-
readdirSync,
|
|
12
|
-
statSync,
|
|
13
|
-
chmodSync,
|
|
14
|
-
copyFileSync,
|
|
15
|
-
} from 'node:fs';
|
|
16
|
-
import { join, dirname } from 'node:path';
|
|
17
|
-
import { fileURLToPath } from 'node:url';
|
|
18
|
-
|
|
19
|
-
// Get the directory of this module (for locating templates)
|
|
20
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get path to bundled templates directory.
|
|
24
|
-
* Works in both development (src/) and production (dist/) contexts.
|
|
25
|
-
*/
|
|
26
|
-
export function getTemplatesDir(): string {
|
|
27
|
-
// When running from dist/, __dirname is packages/cli/dist/
|
|
28
|
-
// Templates are at packages/cli/templates/ (one level up)
|
|
29
|
-
const fromDist = join(__dirname, '..', 'templates');
|
|
30
|
-
|
|
31
|
-
// Fallback path for edge cases
|
|
32
|
-
const fallback = join(__dirname, '..', '..', 'templates');
|
|
33
|
-
|
|
34
|
-
if (existsSync(fromDist)) return fromDist;
|
|
35
|
-
if (existsSync(fallback)) return fallback;
|
|
36
|
-
|
|
37
|
-
throw new Error('Templates directory not found');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Check if a path exists
|
|
42
|
-
*/
|
|
43
|
-
export function exists(path: string): boolean {
|
|
44
|
-
return existsSync(path);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Check if path is a directory
|
|
49
|
-
*/
|
|
50
|
-
export function isDirectory(path: string): boolean {
|
|
51
|
-
return existsSync(path) && statSync(path).isDirectory();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Create directory recursively
|
|
56
|
-
*/
|
|
57
|
-
export function ensureDir(path: string): void {
|
|
58
|
-
if (!existsSync(path)) {
|
|
59
|
-
mkdirSync(path, { recursive: true });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Read file as string
|
|
65
|
-
*/
|
|
66
|
-
export function readFile(path: string): string {
|
|
67
|
-
return readFileSync(path, 'utf-8');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Read file as string, return null if not exists
|
|
72
|
-
*/
|
|
73
|
-
export function readFileSafe(path: string): string | null {
|
|
74
|
-
if (!existsSync(path)) return null;
|
|
75
|
-
return readFileSync(path, 'utf-8');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Write file, creating parent directories if needed
|
|
80
|
-
*/
|
|
81
|
-
export function writeFile(path: string, content: string): void {
|
|
82
|
-
ensureDir(dirname(path));
|
|
83
|
-
writeFileSync(path, content);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Remove file or directory recursively
|
|
88
|
-
*/
|
|
89
|
-
export function remove(path: string): void {
|
|
90
|
-
if (existsSync(path)) {
|
|
91
|
-
rmSync(path, { recursive: true, force: true });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* List files in directory
|
|
97
|
-
*/
|
|
98
|
-
export function listDir(path: string): string[] {
|
|
99
|
-
if (!existsSync(path)) return [];
|
|
100
|
-
return readdirSync(path);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Copy a single file
|
|
105
|
-
*/
|
|
106
|
-
export function copyFile(src: string, dest: string): void {
|
|
107
|
-
ensureDir(dirname(dest));
|
|
108
|
-
copyFileSync(src, dest);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Copy directory recursively
|
|
113
|
-
*/
|
|
114
|
-
export function copyDir(src: string, dest: string): void {
|
|
115
|
-
ensureDir(dest);
|
|
116
|
-
const entries = readdirSync(src, { withFileTypes: true });
|
|
117
|
-
|
|
118
|
-
for (const entry of entries) {
|
|
119
|
-
const srcPath = join(src, entry.name);
|
|
120
|
-
const destPath = join(dest, entry.name);
|
|
121
|
-
|
|
122
|
-
if (entry.isDirectory()) {
|
|
123
|
-
copyDir(srcPath, destPath);
|
|
124
|
-
} else {
|
|
125
|
-
copyFileSync(srcPath, destPath);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Make file executable
|
|
132
|
-
*/
|
|
133
|
-
export function makeExecutable(path: string): void {
|
|
134
|
-
chmodSync(path, 0o755);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Make all shell scripts in a directory executable
|
|
139
|
-
*/
|
|
140
|
-
export function makeScriptsExecutable(dirPath: string): void {
|
|
141
|
-
if (!existsSync(dirPath)) return;
|
|
142
|
-
for (const file of readdirSync(dirPath)) {
|
|
143
|
-
if (file.endsWith('.sh')) {
|
|
144
|
-
chmodSync(join(dirPath, file), 0o755);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Read JSON file
|
|
151
|
-
*/
|
|
152
|
-
export function readJson<T = unknown>(path: string): T | null {
|
|
153
|
-
const content = readFileSafe(path);
|
|
154
|
-
if (!content) return null;
|
|
155
|
-
try {
|
|
156
|
-
return JSON.parse(content) as T;
|
|
157
|
-
} catch {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Write JSON file with formatting
|
|
164
|
-
*/
|
|
165
|
-
export function writeJson(path: string, data: unknown): void {
|
|
166
|
-
writeFile(path, JSON.stringify(data, null, 2) + '\n');
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Update JSON file, merging with existing content
|
|
171
|
-
*/
|
|
172
|
-
export function updateJson<T extends Record<string, unknown>>(
|
|
173
|
-
path: string,
|
|
174
|
-
updater: (existing: T | null) => T,
|
|
175
|
-
): void {
|
|
176
|
-
const existing = readJson<T>(path);
|
|
177
|
-
const updated = updater(existing);
|
|
178
|
-
writeJson(path, updated);
|
|
179
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git utilities for CLI operations
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { execSync } from 'node:child_process';
|
|
6
|
-
import { join } from 'node:path';
|
|
7
|
-
import { exists, readFile, writeFile, ensureDir, makeExecutable } from './fs.js';
|
|
8
|
-
|
|
9
|
-
const MARKER_START = '# SAFEWORD_ARCH_CHECK_START';
|
|
10
|
-
const MARKER_END = '# SAFEWORD_ARCH_CHECK_END';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if directory is a git repository
|
|
14
|
-
*/
|
|
15
|
-
export function isGitRepo(cwd: string): boolean {
|
|
16
|
-
return exists(join(cwd, '.git'));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Initialize a git repository
|
|
21
|
-
*/
|
|
22
|
-
export function initGitRepo(cwd: string): void {
|
|
23
|
-
execSync('git init', { cwd, stdio: 'pipe' });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get the pre-commit hook content to add
|
|
28
|
-
*/
|
|
29
|
-
function getHookContent(): string {
|
|
30
|
-
return `
|
|
31
|
-
${MARKER_START}
|
|
32
|
-
# Safeword architecture check
|
|
33
|
-
# This section is managed by safeword - do not edit manually
|
|
34
|
-
if [ -f ".safeword/hooks/pre-commit.sh" ]; then
|
|
35
|
-
bash .safeword/hooks/pre-commit.sh
|
|
36
|
-
fi
|
|
37
|
-
${MARKER_END}
|
|
38
|
-
`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Install safeword markers into pre-commit hook
|
|
43
|
-
*/
|
|
44
|
-
export function installGitHook(cwd: string): void {
|
|
45
|
-
const hooksDir = join(cwd, '.git', 'hooks');
|
|
46
|
-
const hookPath = join(hooksDir, 'pre-commit');
|
|
47
|
-
|
|
48
|
-
ensureDir(hooksDir);
|
|
49
|
-
|
|
50
|
-
let content = '';
|
|
51
|
-
|
|
52
|
-
if (exists(hookPath)) {
|
|
53
|
-
content = readFile(hookPath);
|
|
54
|
-
|
|
55
|
-
// Check if already has safeword markers
|
|
56
|
-
if (content.includes(MARKER_START)) {
|
|
57
|
-
// Remove existing safeword section and re-add (update)
|
|
58
|
-
content = removeMarkerSection(content);
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
// Create new hook file with shebang
|
|
62
|
-
content = '#!/bin/bash\n';
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Add safeword section
|
|
66
|
-
content = content.trimEnd() + '\n' + getHookContent();
|
|
67
|
-
|
|
68
|
-
writeFile(hookPath, content);
|
|
69
|
-
makeExecutable(hookPath);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Remove safeword markers from pre-commit hook
|
|
74
|
-
*/
|
|
75
|
-
export function removeGitHook(cwd: string): void {
|
|
76
|
-
const hookPath = join(cwd, '.git', 'hooks', 'pre-commit');
|
|
77
|
-
|
|
78
|
-
if (!exists(hookPath)) return;
|
|
79
|
-
|
|
80
|
-
let content = readFile(hookPath);
|
|
81
|
-
|
|
82
|
-
if (!content.includes(MARKER_START)) return;
|
|
83
|
-
|
|
84
|
-
content = removeMarkerSection(content);
|
|
85
|
-
|
|
86
|
-
// If only shebang remains, we could delete the file
|
|
87
|
-
// but safer to leave it
|
|
88
|
-
writeFile(hookPath, content);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Remove the section between markers (inclusive)
|
|
93
|
-
*/
|
|
94
|
-
function removeMarkerSection(content: string): string {
|
|
95
|
-
const lines = content.split('\n');
|
|
96
|
-
const result: string[] = [];
|
|
97
|
-
let inMarkerSection = false;
|
|
98
|
-
|
|
99
|
-
for (const line of lines) {
|
|
100
|
-
if (line.includes(MARKER_START)) {
|
|
101
|
-
inMarkerSection = true;
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (line.includes(MARKER_END)) {
|
|
105
|
-
inMarkerSection = false;
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
if (!inMarkerSection) {
|
|
109
|
-
result.push(line);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return result.join('\n').trim() + '\n';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Check if git hooks have safeword markers
|
|
118
|
-
*/
|
|
119
|
-
export function hasGitHook(cwd: string): boolean {
|
|
120
|
-
const hookPath = join(cwd, '.git', 'hooks', 'pre-commit');
|
|
121
|
-
if (!exists(hookPath)) return false;
|
|
122
|
-
const content = readFile(hookPath);
|
|
123
|
-
return content.includes(MARKER_START);
|
|
124
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook utilities for Claude Code settings
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Type guard to check if a value is a hook object with a command property
|
|
7
|
-
*/
|
|
8
|
-
export function isHookObject(h: unknown): h is { command: string } {
|
|
9
|
-
return (
|
|
10
|
-
typeof h === 'object' &&
|
|
11
|
-
h !== null &&
|
|
12
|
-
'command' in h &&
|
|
13
|
-
typeof (h as { command: string }).command === 'string'
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Check if a hook is a safeword hook (command contains '.safeword')
|
|
19
|
-
*/
|
|
20
|
-
export function isSafewordHook(h: unknown): boolean {
|
|
21
|
-
return isHookObject(h) && h.command.includes('.safeword');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Filter out safeword hooks from an array of hooks
|
|
26
|
-
*/
|
|
27
|
-
export function filterOutSafewordHooks(hooks: unknown[]): unknown[] {
|
|
28
|
-
return hooks.filter((h) => !isSafewordHook(h));
|
|
29
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Console output utilities for consistent CLI messaging
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Print info message
|
|
7
|
-
*/
|
|
8
|
-
export function info(message: string): void {
|
|
9
|
-
console.log(message);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Print success message
|
|
14
|
-
*/
|
|
15
|
-
export function success(message: string): void {
|
|
16
|
-
console.log(`✓ ${message}`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Print warning message
|
|
21
|
-
*/
|
|
22
|
-
export function warn(message: string): void {
|
|
23
|
-
console.warn(`⚠ ${message}`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Print error message to stderr
|
|
28
|
-
*/
|
|
29
|
-
export function error(message: string): void {
|
|
30
|
-
console.error(`✗ ${message}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Print a blank line
|
|
35
|
-
*/
|
|
36
|
-
export function blank(): void {
|
|
37
|
-
console.log('');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Print a section header
|
|
42
|
-
*/
|
|
43
|
-
export function header(title: string): void {
|
|
44
|
-
console.log(`\n${title}`);
|
|
45
|
-
console.log('─'.repeat(title.length));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Print a list item
|
|
50
|
-
*/
|
|
51
|
-
export function listItem(item: string, indent = 2): void {
|
|
52
|
-
console.log(`${' '.repeat(indent)}• ${item}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Print key-value pair
|
|
57
|
-
*/
|
|
58
|
-
export function keyValue(key: string, value: string): void {
|
|
59
|
-
console.log(` ${key}: ${value}`);
|
|
60
|
-
}
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for project type detection (Tests 4.1-4.3)
|
|
3
|
-
*
|
|
4
|
-
* These are pure unit tests for the detectProjectType function.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect } from 'vitest';
|
|
8
|
-
import { detectProjectType, PackageJson } from './project-detector';
|
|
9
|
-
|
|
10
|
-
describe('detectProjectType', () => {
|
|
11
|
-
describe('Test 4.1: Detects TypeScript project', () => {
|
|
12
|
-
it('should detect typescript from devDependencies', () => {
|
|
13
|
-
const packageJson: PackageJson = {
|
|
14
|
-
name: 'test',
|
|
15
|
-
version: '1.0.0',
|
|
16
|
-
devDependencies: {
|
|
17
|
-
typescript: '^5.0.0',
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const result = detectProjectType(packageJson);
|
|
22
|
-
expect(result.typescript).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should detect typescript from dependencies', () => {
|
|
26
|
-
const packageJson: PackageJson = {
|
|
27
|
-
name: 'test',
|
|
28
|
-
version: '1.0.0',
|
|
29
|
-
dependencies: {
|
|
30
|
-
typescript: '^5.0.0',
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const result = detectProjectType(packageJson);
|
|
35
|
-
expect(result.typescript).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should return false when typescript is not present', () => {
|
|
39
|
-
const packageJson: PackageJson = {
|
|
40
|
-
name: 'test',
|
|
41
|
-
version: '1.0.0',
|
|
42
|
-
dependencies: {},
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const result = detectProjectType(packageJson);
|
|
46
|
-
expect(result.typescript).toBe(false);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('Test 4.2: Detects React project', () => {
|
|
51
|
-
it('should detect react from dependencies', () => {
|
|
52
|
-
const packageJson: PackageJson = {
|
|
53
|
-
name: 'test',
|
|
54
|
-
version: '1.0.0',
|
|
55
|
-
dependencies: {
|
|
56
|
-
react: '^18.0.0',
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const result = detectProjectType(packageJson);
|
|
61
|
-
expect(result.react).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should detect react from devDependencies', () => {
|
|
65
|
-
const packageJson: PackageJson = {
|
|
66
|
-
name: 'test',
|
|
67
|
-
version: '1.0.0',
|
|
68
|
-
devDependencies: {
|
|
69
|
-
react: '^18.0.0',
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const result = detectProjectType(packageJson);
|
|
74
|
-
expect(result.react).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should return false when react is not present', () => {
|
|
78
|
-
const packageJson: PackageJson = {
|
|
79
|
-
name: 'test',
|
|
80
|
-
version: '1.0.0',
|
|
81
|
-
dependencies: {},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const result = detectProjectType(packageJson);
|
|
85
|
-
expect(result.react).toBe(false);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('Test 4.3: Detects Next.js project', () => {
|
|
90
|
-
it('should detect next.js from dependencies', () => {
|
|
91
|
-
const packageJson: PackageJson = {
|
|
92
|
-
name: 'test',
|
|
93
|
-
version: '1.0.0',
|
|
94
|
-
dependencies: {
|
|
95
|
-
next: '^14.0.0',
|
|
96
|
-
react: '^18.0.0',
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const result = detectProjectType(packageJson);
|
|
101
|
-
expect(result.nextjs).toBe(true);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should imply react when next.js is present', () => {
|
|
105
|
-
const packageJson: PackageJson = {
|
|
106
|
-
name: 'test',
|
|
107
|
-
version: '1.0.0',
|
|
108
|
-
dependencies: {
|
|
109
|
-
next: '^14.0.0',
|
|
110
|
-
// Note: react not explicitly listed
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const result = detectProjectType(packageJson);
|
|
115
|
-
expect(result.nextjs).toBe(true);
|
|
116
|
-
expect(result.react).toBe(true); // Next.js implies React
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe('Detects other frameworks', () => {
|
|
121
|
-
it('should detect Astro project', () => {
|
|
122
|
-
const packageJson: PackageJson = {
|
|
123
|
-
name: 'test',
|
|
124
|
-
version: '1.0.0',
|
|
125
|
-
dependencies: {
|
|
126
|
-
astro: '^4.0.0',
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const result = detectProjectType(packageJson);
|
|
131
|
-
expect(result.astro).toBe(true);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should detect Electron project', () => {
|
|
135
|
-
const packageJson: PackageJson = {
|
|
136
|
-
name: 'test',
|
|
137
|
-
version: '1.0.0',
|
|
138
|
-
devDependencies: {
|
|
139
|
-
electron: '^28.0.0',
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const result = detectProjectType(packageJson);
|
|
144
|
-
expect(result.electron).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe('Handles edge cases', () => {
|
|
149
|
-
it('should handle empty package.json', () => {
|
|
150
|
-
const packageJson: PackageJson = {};
|
|
151
|
-
|
|
152
|
-
const result = detectProjectType(packageJson);
|
|
153
|
-
|
|
154
|
-
expect(result.typescript).toBe(false);
|
|
155
|
-
expect(result.react).toBe(false);
|
|
156
|
-
expect(result.nextjs).toBe(false);
|
|
157
|
-
expect(result.astro).toBe(false);
|
|
158
|
-
expect(result.electron).toBe(false);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('should handle complex project with multiple frameworks', () => {
|
|
162
|
-
const packageJson: PackageJson = {
|
|
163
|
-
name: 'complex-project',
|
|
164
|
-
version: '1.0.0',
|
|
165
|
-
dependencies: {
|
|
166
|
-
next: '^14.0.0',
|
|
167
|
-
react: '^18.0.0',
|
|
168
|
-
'react-dom': '^18.0.0',
|
|
169
|
-
},
|
|
170
|
-
devDependencies: {
|
|
171
|
-
typescript: '^5.3.0',
|
|
172
|
-
electron: '^28.0.0',
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const result = detectProjectType(packageJson);
|
|
177
|
-
|
|
178
|
-
expect(result.typescript).toBe(true);
|
|
179
|
-
expect(result.react).toBe(true);
|
|
180
|
-
expect(result.nextjs).toBe(true);
|
|
181
|
-
expect(result.electron).toBe(true);
|
|
182
|
-
expect(result.astro).toBe(false);
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project type detection from package.json
|
|
3
|
-
*
|
|
4
|
-
* Detects frameworks and tools used in the project to configure
|
|
5
|
-
* appropriate linting rules.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface PackageJson {
|
|
9
|
-
name?: string;
|
|
10
|
-
version?: string;
|
|
11
|
-
dependencies?: Record<string, string>;
|
|
12
|
-
devDependencies?: Record<string, string>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface ProjectType {
|
|
16
|
-
typescript: boolean;
|
|
17
|
-
react: boolean;
|
|
18
|
-
nextjs: boolean;
|
|
19
|
-
astro: boolean;
|
|
20
|
-
electron: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Detects project type from package.json contents
|
|
25
|
-
*/
|
|
26
|
-
export function detectProjectType(packageJson: PackageJson): ProjectType {
|
|
27
|
-
const deps = packageJson.dependencies || {};
|
|
28
|
-
const devDeps = packageJson.devDependencies || {};
|
|
29
|
-
const allDeps = { ...deps, ...devDeps };
|
|
30
|
-
|
|
31
|
-
const hasTypescript = 'typescript' in allDeps;
|
|
32
|
-
const hasReact = 'react' in deps || 'react' in devDeps;
|
|
33
|
-
const hasNextJs = 'next' in deps;
|
|
34
|
-
const hasAstro = 'astro' in deps || 'astro' in devDeps;
|
|
35
|
-
const hasElectron = 'electron' in deps || 'electron' in devDeps;
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
typescript: hasTypescript,
|
|
39
|
-
react: hasReact || hasNextJs, // Next.js implies React
|
|
40
|
-
nextjs: hasNextJs,
|
|
41
|
-
astro: hasAstro,
|
|
42
|
-
electron: hasElectron,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Version comparison utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Compare two semver versions
|
|
7
|
-
* @returns -1 if a < b, 0 if a == b, 1 if a > b
|
|
8
|
-
*/
|
|
9
|
-
export function compareVersions(a: string, b: string): -1 | 0 | 1 {
|
|
10
|
-
const aParts = a.split('.').map(Number);
|
|
11
|
-
const bParts = b.split('.').map(Number);
|
|
12
|
-
|
|
13
|
-
for (let i = 0; i < 3; i++) {
|
|
14
|
-
const aVal = aParts[i] ?? 0;
|
|
15
|
-
const bVal = bParts[i] ?? 0;
|
|
16
|
-
if (aVal < bVal) return -1;
|
|
17
|
-
if (aVal > bVal) return 1;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Check if latest version is newer than current
|
|
25
|
-
*/
|
|
26
|
-
export function isNewerVersion(current: string, latest: string): boolean {
|
|
27
|
-
return compareVersions(current, latest) === -1;
|
|
28
|
-
}
|