speclock 5.2.6 → 5.3.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.
@@ -9,7 +9,7 @@
9
9
  import { readBrain, readEvents } from "./storage.js";
10
10
  import { verifyAuditChain } from "./audit.js";
11
11
 
12
- const VERSION = "5.2.6";
12
+ const VERSION = "5.3.1";
13
13
 
14
14
  // PHI-related keywords for HIPAA filtering
15
15
  const PHI_KEYWORDS = [
@@ -85,6 +85,7 @@ import { handleFileEvent } from "./tracking.js";
85
85
  export async function watchRepo(root) {
86
86
  const { default: chokidar } = await import("chokidar");
87
87
  const brain = ensureInit(root);
88
+ const activeLocks = (brain.specLock?.items || []).filter((l) => l.active !== false);
88
89
  const ignore = [
89
90
  "**/node_modules/**",
90
91
  "**/.git/**",
@@ -92,6 +93,39 @@ export async function watchRepo(root) {
92
93
  ];
93
94
 
94
95
  let lastFileEventAt = 0;
96
+ let checksThisSession = 0;
97
+ let blockedThisSession = 0;
98
+ let warnedThisSession = 0;
99
+ const recentActivity = [];
100
+
101
+ function addActivity(icon, message) {
102
+ const time = new Date().toISOString().substring(11, 19);
103
+ recentActivity.unshift({ time, icon, message });
104
+ if (recentActivity.length > 8) recentActivity.length = 8;
105
+ }
106
+
107
+ function printDashboard() {
108
+ const lines = [];
109
+ lines.push("");
110
+ lines.push(" SpecLock Watch — Live Dashboard");
111
+ lines.push(" " + "=".repeat(50));
112
+ lines.push(` Constraints: ${activeLocks.length} active lock(s)`);
113
+ lines.push(` Session: ${checksThisSession} checks | ${warnedThisSession} warned | ${blockedThisSession} blocked`);
114
+ lines.push(` Project: ${brain.project.name}`);
115
+ lines.push("");
116
+ if (recentActivity.length > 0) {
117
+ lines.push(" Recent Activity:");
118
+ for (const a of recentActivity) {
119
+ lines.push(` ${a.time} [${a.icon}] ${a.message}`);
120
+ }
121
+ } else {
122
+ lines.push(" Waiting for file changes...");
123
+ }
124
+ lines.push("");
125
+ lines.push(" Press Ctrl+C to stop.");
126
+ lines.push("");
127
+ console.log(lines.join("\n"));
128
+ }
95
129
 
96
130
  const watcher = chokidar.watch(root, {
97
131
  ignored: ignore,
@@ -101,15 +135,41 @@ export async function watchRepo(root) {
101
135
 
102
136
  watcher.on("add", (p) => {
103
137
  lastFileEventAt = Date.now();
138
+ const rel = path.relative(root, p);
104
139
  handleFileEvent(root, brain, "file_created", p);
140
+ checksThisSession++;
141
+ addActivity("ADD", rel);
142
+ printDashboard();
105
143
  });
106
144
  watcher.on("change", (p) => {
107
145
  lastFileEventAt = Date.now();
146
+ const rel = path.relative(root, p);
108
147
  handleFileEvent(root, brain, "file_changed", p);
148
+ checksThisSession++;
149
+ // Check if this file is guarded by any lock
150
+ const guarded = activeLocks.some((lock) => {
151
+ const lockLower = (lock.text || "").toLowerCase();
152
+ const fileLower = rel.toLowerCase();
153
+ return lockLower.includes(fileLower) ||
154
+ (fileLower.includes("auth") && lockLower.includes("auth")) ||
155
+ (fileLower.includes("payment") && lockLower.includes("payment")) ||
156
+ (fileLower.includes("database") && lockLower.includes("database"));
157
+ });
158
+ if (guarded) {
159
+ warnedThisSession++;
160
+ addActivity("WARN", `${rel} — touches guarded area`);
161
+ } else {
162
+ addActivity("EDIT", rel);
163
+ }
164
+ printDashboard();
109
165
  });
110
166
  watcher.on("unlink", (p) => {
111
167
  lastFileEventAt = Date.now();
168
+ const rel = path.relative(root, p);
112
169
  handleFileEvent(root, brain, "file_deleted", p);
170
+ checksThisSession++;
171
+ addActivity("DEL", rel);
172
+ printDashboard();
113
173
  });
114
174
 
115
175
  // Revert detection via HEAD polling
@@ -139,6 +199,8 @@ export async function watchRepo(root) {
139
199
  bumpEvents(brain, eventId);
140
200
  appendEvent(root, event);
141
201
  writeBrain(root, brain);
202
+ addActivity("REVERT", `HEAD → ${head.gitCommit.substring(0, 12)}`);
203
+ printDashboard();
142
204
  }
143
205
  brain.state.head.gitBranch = head.gitBranch;
144
206
  brain.state.head.gitCommit = head.gitCommit;
@@ -147,7 +209,7 @@ export async function watchRepo(root) {
147
209
  }, 5000);
148
210
  }
149
211
 
150
- console.log("SpecLock watching for changes...");
212
+ printDashboard();
151
213
  return watcher;
152
214
  }
153
215