( projectRoot: string, hooks: GitHookName[] = DEFAULT_SYNC_HOOKS, )
| 123 | * it, and any user-authored hook content is preserved. |
| 124 | */ |
| 125 | export function installGitSyncHook( |
| 126 | projectRoot: string, |
| 127 | hooks: GitHookName[] = DEFAULT_SYNC_HOOKS, |
| 128 | ): GitHookResult { |
| 129 | const hooksDir = gitHooksDir(projectRoot); |
| 130 | if (!hooksDir) { |
| 131 | return { installed: [], hooksDir: null, skipped: 'not a git repository' }; |
| 132 | } |
| 133 | |
| 134 | try { |
| 135 | fs.mkdirSync(hooksDir, { recursive: true }); |
| 136 | } catch { |
| 137 | return { installed: [], hooksDir, skipped: 'could not access the git hooks directory' }; |
| 138 | } |
| 139 | |
| 140 | const block = markerBlock(); |
| 141 | const installed: GitHookName[] = []; |
| 142 | |
| 143 | for (const hook of hooks) { |
| 144 | const file = path.join(hooksDir, hook); |
| 145 | let content: string; |
| 146 | |
| 147 | if (fs.existsSync(file)) { |
| 148 | // Strip any prior block, then re-append the current one. |
| 149 | const base = stripMarkerBlock(fs.readFileSync(file, 'utf8')).replace(/\s*$/, ''); |
| 150 | content = base.length > 0 |
| 151 | ? `${base}\n\n${block}\n` |
| 152 | : `#!/bin/sh\n${block}\n`; |
| 153 | } else { |
| 154 | content = `#!/bin/sh\n${block}\n`; |
| 155 | } |
| 156 | |
| 157 | fs.writeFileSync(file, content); |
| 158 | chmodExecutable(file); |
| 159 | installed.push(hook); |
| 160 | } |
| 161 | |
| 162 | return { installed, hooksDir }; |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Remove the CodeGraph sync hooks. Strips only our marker block; deletes the |
no test coverage detected