tdd-enforcer 0.1.7 → 0.1.9

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.
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { existsSync } from "node:fs";
3
3
  import { join } from "node:path";
4
- import { loadPhaseState, loadConfig, savePhaseState, initGit, snapshot } from "../../engine/index.js";
4
+ import { loadPhaseState, loadConfig, savePhaseState, initGit, resetGit, snapshot } from "../../engine/index.js";
5
5
  import { registerTools } from "./tools.js";
6
6
  import { registerHooks } from "./hooks.js";
7
7
  import { loadTddState } from "./helpers.js";
@@ -163,6 +163,49 @@ export default function (pi: ExtensionAPI) {
163
163
  },
164
164
  });
165
165
 
166
+ pi.registerCommand("tdd:reset", {
167
+ description:
168
+ "WARNING: Destroys ALL TDD snapshot history and resets to RED phase. " +
169
+ "Working tree is preserved. Run /tdd:on to re-enable after reset.",
170
+ handler: async (_args: string, ctx: ExtensionContext) => {
171
+ const root = ctx.cwd;
172
+ const tddDir = join(root, ".pi", "tdd");
173
+
174
+ tddLog(tddDir, "INFO", "tdd:reset: starting");
175
+
176
+ if (!existsSync(tddDir)) {
177
+ tddLog(tddDir, "WARN", "tdd:reset: missing .pi/tdd/ directory");
178
+ ctx.ui.notify("No .pi/tdd/ directory found — nothing to reset.", "error");
179
+ return;
180
+ }
181
+
182
+ // Nuke git history and re-init
183
+ try {
184
+ resetGit(root);
185
+ tddLog(tddDir, "INFO", "tdd:reset: git reset and re-initialised");
186
+ } catch (e) {
187
+ tddLog(tddDir, "ERROR", "tdd:reset: git reset failed", {
188
+ error: (e as Error).message,
189
+ });
190
+ ctx.ui.notify("Failed to reset private git repo.", "error");
191
+ return;
192
+ }
193
+
194
+ // Snapshot current working tree
195
+ snapshot(root, "red");
196
+ tddLog(tddDir, "INFO", "tdd:reset: snapshot taken");
197
+
198
+ // Reset state to RED (disabled, user must run /tdd:on)
199
+ savePhaseState(root, { enabled: false, current: "red" });
200
+ tddLog(tddDir, "INFO", "tdd:reset: complete");
201
+
202
+ ctx.ui.notify(
203
+ "TDD snapshot history reset. Run /tdd:on to re-enable enforcement.",
204
+ "warning",
205
+ );
206
+ },
207
+ });
208
+
166
209
  registerTools(pi);
167
210
  registerHooks(pi);
168
211
  }
@@ -1,6 +1,9 @@
1
1
  import { join } from "node:path";
2
2
  import { Type } from "typebox";
3
- import { execSync } from "node:child_process";
3
+ import { exec } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+
6
+ const asyncExec = promisify(exec);
4
7
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
8
  import {
6
9
  savePhaseState,
@@ -67,7 +70,7 @@ export function registerTools(pi: ExtensionAPI): void {
67
70
  const results = await Promise.all(
68
71
  commands.map(async (cmd) => {
69
72
  try {
70
- execSync(cmd, { cwd: root, stdio: "pipe", timeout: timeout * 1000 });
73
+ await asyncExec(cmd, { cwd: root, timeout: timeout * 1000 });
71
74
  return { command: cmd, passed: true };
72
75
  } catch {
73
76
  return { command: cmd, passed: false };
package/engine/git.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { execSync, type ExecSyncOptions } from "node:child_process";
2
- import { existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
2
+ import { existsSync, mkdirSync, writeFileSync, unlinkSync, rmSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
 
5
5
  const TDD_DIR = ".pi/tdd";
@@ -40,6 +40,16 @@ export function initGit(projectRoot: string): void {
40
40
  gitExec('commit --allow-empty -m "tdd: init"', projectRoot, { stdio: "pipe" as const });
41
41
  }
42
42
 
43
+ /** Destroy the private git repo and re-init from scratch. */
44
+ export function resetGit(projectRoot: string): void {
45
+ const tddPath = join(projectRoot, TDD_DIR);
46
+ const gitDir = join(tddPath, ".git");
47
+ if (existsSync(gitDir)) {
48
+ rmSync(gitDir, { recursive: true, force: true });
49
+ }
50
+ initGit(projectRoot);
51
+ }
52
+
43
53
  /** Stage all + commit with --allow-empty so every phase transition has a labeled commit. */
44
54
  export function snapshot(projectRoot: string, phase: string): string {
45
55
  gitExec("add -A", projectRoot, { stdio: "pipe" as const });
package/engine/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { isAllowed, disallowedFiles } from "./enforce.js";
2
- export { initGit, snapshot, changesSinceSnapshot, changesSince, modifiedFiles, untrackedFiles, restoreFilesTo, gitStashCreate, headHash, headMessage, hasParent, resetHard, undoLastCommit } from "./git.js";
2
+ export { initGit, resetGit, snapshot, changesSinceSnapshot, changesSince, modifiedFiles, untrackedFiles, restoreFilesTo, gitStashCreate, headHash, headMessage, hasParent, resetHard, undoLastCommit } from "./git.js";
3
3
  export { loadConfig } from "./config.js";
4
4
  export { loadPhaseState, savePhaseState } from "./state.js";
5
5
  export { nextPhase, checkGate, getDisallowedChanges } from "./transition.js";
@@ -36,7 +36,7 @@ export async function checkGate(
36
36
  if (result.passed) {
37
37
  return {
38
38
  passed: false,
39
- message: "Tests pass. Add a failing test before transitioning to GREEN.",
39
+ message: "Tests passed. Add a failing test before transitioning to GREEN.",
40
40
  };
41
41
  }
42
42
  return { passed: true, message: "Tests fail — proceed to GREEN." };
@@ -45,7 +45,7 @@ export async function checkGate(
45
45
  if (!result.passed) {
46
46
  return {
47
47
  passed: false,
48
- message: "Tests fail. Fix them before transitioning to REFACTOR.",
48
+ message: "Tests failed. Fix them before transitioning to REFACTOR.",
49
49
  };
50
50
  }
51
51
  return { passed: true, message: "All tests pass — proceeding." };
@@ -54,7 +54,7 @@ export async function checkGate(
54
54
  if (!result.passed) {
55
55
  return {
56
56
  passed: false,
57
- message: "Tests fail. Fix them before transitioning to RED.",
57
+ message: "Tests failed. Fix them before transitioning to RED.",
58
58
  };
59
59
  }
60
60
  return { passed: true, message: "All tests pass — proceeding." };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tdd-enforcer",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "keywords": [
6
6
  "pi-package"