@lhi/tdd-audit 1.1.0 → 1.1.2

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/README.md CHANGED
@@ -34,6 +34,7 @@ node index.js
34
34
  | `--claude` | Use `.claude/` instead of `.agents/` as the skill directory |
35
35
  | `--with-hooks` | Install a pre-commit hook that blocks commits if security tests fail |
36
36
  | `--skip-scan` | Skip the automatic vulnerability scan on install |
37
+ | `--scan-only` | Run the vulnerability scan without installing anything |
37
38
 
38
39
  **Install to a Claude Code project with pre-commit protection:**
39
40
  ```bash
package/index.js CHANGED
@@ -9,14 +9,16 @@ const isLocal = args.includes('--local');
9
9
  const isClaude = args.includes('--claude');
10
10
  const withHooks = args.includes('--with-hooks');
11
11
  const skipScan = args.includes('--skip-scan');
12
+ const scanOnly = args.includes('--scan-only');
12
13
 
13
14
  const agentBaseDir = isLocal ? process.cwd() : os.homedir();
14
15
  const agentDirName = isClaude ? '.claude' : '.agents';
15
16
  const projectDir = process.cwd();
16
17
 
17
18
  const targetSkillDir = path.join(agentBaseDir, agentDirName, 'skills', 'tdd-remediation');
18
- const targetWorkflowDir = path.join(agentBaseDir, agentDirName, 'workflows');
19
- const targetTestDir = path.join(projectDir, '__tests__', 'security');
19
+ const targetWorkflowDir = isClaude
20
+ ? path.join(agentBaseDir, agentDirName, 'commands')
21
+ : path.join(agentBaseDir, agentDirName, 'workflows');
20
22
 
21
23
  // ─── 1. Framework Detection ──────────────────────────────────────────────────
22
24
 
@@ -43,7 +45,24 @@ function detectFramework() {
43
45
 
44
46
  const framework = detectFramework();
45
47
 
46
- // ─── 2. Quick Scan ───────────────────────────────────────────────────────────
48
+ // ─── 2. Test Directory Detection ─────────────────────────────────────────────
49
+
50
+ function detectTestBaseDir() {
51
+ // Respect an existing convention before inventing one
52
+ const candidates = ['__tests__', 'tests', 'test', 'spec'];
53
+ for (const dir of candidates) {
54
+ if (fs.existsSync(path.join(projectDir, dir))) return dir;
55
+ }
56
+ // Framework-informed defaults when no directory exists yet
57
+ if (framework === 'pytest') return 'tests';
58
+ if (framework === 'go') return 'test';
59
+ return '__tests__';
60
+ }
61
+
62
+ const testBaseDir = detectTestBaseDir();
63
+ const targetTestDir = path.join(projectDir, testBaseDir, 'security');
64
+
65
+ // ─── 3. Quick Scan ───────────────────────────────────────────────────────────
47
66
 
48
67
  const VULN_PATTERNS = [
49
68
  { name: 'SQL Injection', severity: 'CRITICAL', pattern: /(`SELECT[^`]*\$\{|"SELECT[^"]*"\s*\+|execute\(f"|cursor\.execute\(.*%s|\.query\(`[^`]*\$\{)/i },
@@ -111,9 +130,19 @@ function printFindings(findings) {
111
130
  console.log('\n Run /tdd-audit in your agent to remediate.\n');
112
131
  }
113
132
 
114
- // ─── 3. Install Skill Files ───────────────────────────────────────────────────
133
+ // ─── 4. Scan-only early exit ──────────────────────────────────────────────────
134
+
135
+ if (scanOnly) {
136
+ process.stdout.write('\n🔍 Scanning for vulnerability patterns...');
137
+ const findings = quickScan();
138
+ process.stdout.write('\n');
139
+ printFindings(findings);
140
+ process.exit(0);
141
+ }
142
+
143
+ // ─── 5. Install Skill Files ───────────────────────────────────────────────────
115
144
 
116
- console.log(`\nInstalling TDD Remediation Skill (${isLocal ? 'local' : 'global'}, framework: ${framework})...\n`);
145
+ console.log(`\nInstalling TDD Remediation Skill (${isLocal ? 'local' : 'global'}, framework: ${framework}, test dir: ${testBaseDir}/)...\n`);
117
146
 
118
147
  if (!fs.existsSync(targetSkillDir)) fs.mkdirSync(targetSkillDir, { recursive: true });
119
148
 
@@ -123,7 +152,7 @@ for (const item of ['SKILL.md', 'prompts', 'templates']) {
123
152
  if (fs.existsSync(src)) fs.cpSync(src, dest, { recursive: true });
124
153
  }
125
154
 
126
- // ─── 4. Scaffold Security Test Boilerplate ────────────────────────────────────
155
+ // ─── 5. Scaffold Security Test Boilerplate ────────────────────────────────────
127
156
 
128
157
  if (!fs.existsSync(targetTestDir)) {
129
158
  fs.mkdirSync(targetTestDir, { recursive: true });
@@ -147,7 +176,7 @@ if (!fs.existsSync(destTest) && fs.existsSync(srcTest)) {
147
176
  console.log(`✅ Scaffolded ${path.relative(projectDir, destTest)}`);
148
177
  }
149
178
 
150
- // ─── 5. Install Workflow Shortcode ────────────────────────────────────────────
179
+ // ─── 6. Install Workflow Shortcode ────────────────────────────────────────────
151
180
 
152
181
  if (!fs.existsSync(targetWorkflowDir)) fs.mkdirSync(targetWorkflowDir, { recursive: true });
153
182
  const srcWorkflow = path.join(__dirname, 'workflows', 'tdd-audit.md');
@@ -157,7 +186,7 @@ if (fs.existsSync(srcWorkflow)) {
157
186
  console.log(`✅ Installed /tdd-audit workflow shortcode`);
158
187
  }
159
188
 
160
- // ─── 6. Inject test:security into package.json ────────────────────────────────
189
+ // ─── 7. Inject test:security into package.json ────────────────────────────────
161
190
 
162
191
  const pkgPath = path.join(projectDir, 'package.json');
163
192
  if (framework !== 'pytest' && framework !== 'go' && fs.existsSync(pkgPath)) {
@@ -165,11 +194,12 @@ if (framework !== 'pytest' && framework !== 'go' && fs.existsSync(pkgPath)) {
165
194
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
166
195
  if (!pkg.scripts?.['test:security']) {
167
196
  pkg.scripts = pkg.scripts || {};
197
+ const secDir = `${testBaseDir}/security`;
168
198
  pkg.scripts['test:security'] = {
169
- jest: 'jest --testPathPattern=__tests__/security --forceExit',
170
- vitest: 'vitest run __tests__/security',
171
- mocha: "mocha '__tests__/security/**/*.spec.js'",
172
- }[framework] || 'jest --testPathPattern=__tests__/security --forceExit';
199
+ jest: `jest --testPathPattern=${secDir} --forceExit`,
200
+ vitest: `vitest run ${secDir}`,
201
+ mocha: `mocha '${secDir}/**/*.spec.js'`,
202
+ }[framework] || `jest --testPathPattern=${secDir} --forceExit`;
173
203
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
174
204
  console.log(`✅ Added "test:security" script to package.json`);
175
205
  } else {
@@ -180,7 +210,7 @@ if (framework !== 'pytest' && framework !== 'go' && fs.existsSync(pkgPath)) {
180
210
  }
181
211
  }
182
212
 
183
- // ─── 7. Scaffold CI Workflow ─────────────────────────────────────────────────
213
+ // ─── 8. Scaffold CI Workflow ─────────────────────────────────────────────────
184
214
 
185
215
  const ciWorkflowDir = path.join(projectDir, '.github', 'workflows');
186
216
  const ciWorkflowPath = path.join(ciWorkflowDir, 'security-tests.yml');
@@ -203,7 +233,7 @@ if (!fs.existsSync(ciWorkflowPath)) {
203
233
  console.log(` .github/workflows/security-tests.yml already exists — skipped`);
204
234
  }
205
235
 
206
- // ─── 8. Pre-commit Hook (opt-in) ─────────────────────────────────────────────
236
+ // ─── 9. Pre-commit Hook (opt-in) ─────────────────────────────────────────────
207
237
 
208
238
  if (withHooks) {
209
239
  const gitDir = path.join(projectDir, '.git');
@@ -241,7 +271,7 @@ if (withHooks) {
241
271
  }
242
272
  }
243
273
 
244
- // ─── 9. Quick Scan ───────────────────────────────────────────────────────────
274
+ // ─── 10. Quick Scan ──────────────────────────────────────────────────────────
245
275
 
246
276
  if (!skipScan) {
247
277
  process.stdout.write('\n🔍 Scanning for vulnerability patterns...');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lhi/tdd-audit",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Anti-Gravity Skill for TDD Remediation. Patches security vulnerabilities using a Red-Green-Refactor protocol with automated exploit tests.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -101,7 +101,7 @@ Ask the user to confirm the list before beginning remediation. If they say "fix
101
101
 
102
102
  For **each** confirmed vulnerability, rigorously apply the RED-GREEN-REFACTOR protocol in order:
103
103
 
104
- 1. **[RED](./red-phase.md)**: Write the exploit test in `__tests__/security/` and run it to prove the vulnerability exists (test must fail).
104
+ 1. **[RED](./red-phase.md)**: Write the exploit test in the project's security test directory (e.g., `tests/security/`, `__tests__/security/`, `test/security/` — wherever the installer scaffolded the boilerplate) and run it to prove the vulnerability exists (test must fail).
105
105
  2. **[GREEN](./green-phase.md)**: Apply the targeted patch. Run the exploit test — it must now pass.
106
106
  3. **[REFACTOR](./refactor-phase.md)**: Run the full test suite. All tests must be green before moving on.
107
107