()
| 210 | } |
| 211 | |
| 212 | function run(): void { |
| 213 | step("Wiring the spec loop into this project"); |
| 214 | preflight(); |
| 215 | |
| 216 | for (const item of PLAN) { |
| 217 | const dest = relative(REPO_ROOT, item.destination); |
| 218 | |
| 219 | if (item.kind === "copy") { |
| 220 | const source = typeof item.source === "function" ? null : item.source; |
| 221 | const produce = |
| 222 | typeof item.source === "function" |
| 223 | ? item.source |
| 224 | : () => readFileSync(source!, "utf8"); |
| 225 | const outcome = writeIfMissing(item.destination, produce); |
| 226 | |
| 227 | if (outcome === "wrote") { |
| 228 | ok(`wrote ${dest}`); |
| 229 | } else { |
| 230 | skipped(`${dest} already exists (use --force to overwrite)`); |
| 231 | } |
| 232 | continue; |
| 233 | } |
| 234 | |
| 235 | if (item.kind === "merge-json") { |
| 236 | if (typeof item.source !== "function") { |
| 237 | fail(`merge-json target needs a producer function`); |
| 238 | } |
| 239 | if (existsSync(item.destination)) { |
| 240 | const current = readFileSync(item.destination, "utf8"); |
| 241 | const merged = mergeClaudeSettings(current); |
| 242 | |
| 243 | if (merged === current) { |
| 244 | skipped(`${dest} already wires the spec gate`); |
| 245 | } else { |
| 246 | ensureDir(dirname(item.destination)); |
| 247 | writeFileSync(item.destination, merged); |
| 248 | ok(`merged spec-gate hook into ${dest}`); |
| 249 | } |
| 250 | } else { |
| 251 | ensureDir(dirname(item.destination)); |
| 252 | writeFileSync(item.destination, item.source()); |
| 253 | ok(`wrote ${dest}`); |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | process.stdout.write(`\n`); |
| 259 | ok("Spec loop is active in this project."); |
| 260 | process.stdout.write( |
| 261 | `\nNext: open \`.specs/next.md\`, then in Claude Code or Cursor run\n ${C_CYAN}/spec explore <your idea>${C_RESET}\n → ${C_CYAN}/spec slice${C_RESET}\n → ${C_CYAN}/spec approve${C_RESET} (you, not the agent)\n → ${C_CYAN}/spec build${C_RESET}\n\nThe gate at tools/spec-loop/hooks/gate.ts blocks source writes until\n\`.specs/next.md\` has \`status: approved\` in its frontmatter.\n` |
| 262 | ); |
| 263 | } |
| 264 | |
| 265 | run(); |
no test coverage detected