promptvet-core 1.0.0
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 +278 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +273 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/layers/heuristics/actionVerbs.d.ts +4 -0
- package/dist/layers/heuristics/actionVerbs.d.ts.map +1 -0
- package/dist/layers/heuristics/actionVerbs.js +9 -0
- package/dist/layers/heuristics/actionVerbs.js.map +1 -0
- package/dist/layers/heuristics/browserApiRisk.d.ts +2 -0
- package/dist/layers/heuristics/browserApiRisk.d.ts.map +1 -0
- package/dist/layers/heuristics/browserApiRisk.js +18 -0
- package/dist/layers/heuristics/browserApiRisk.js.map +1 -0
- package/dist/layers/heuristics/frustrationSignal.d.ts +2 -0
- package/dist/layers/heuristics/frustrationSignal.d.ts.map +1 -0
- package/dist/layers/heuristics/frustrationSignal.js +26 -0
- package/dist/layers/heuristics/frustrationSignal.js.map +1 -0
- package/dist/layers/heuristics/geoFlakeRisk.d.ts +2 -0
- package/dist/layers/heuristics/geoFlakeRisk.d.ts.map +1 -0
- package/dist/layers/heuristics/geoFlakeRisk.js +14 -0
- package/dist/layers/heuristics/geoFlakeRisk.js.map +1 -0
- package/dist/layers/heuristics/missingFramework.d.ts +2 -0
- package/dist/layers/heuristics/missingFramework.d.ts.map +1 -0
- package/dist/layers/heuristics/missingFramework.js +16 -0
- package/dist/layers/heuristics/missingFramework.js.map +1 -0
- package/dist/layers/heuristics/missingMockStrategy.d.ts +2 -0
- package/dist/layers/heuristics/missingMockStrategy.d.ts.map +1 -0
- package/dist/layers/heuristics/missingMockStrategy.js +17 -0
- package/dist/layers/heuristics/missingMockStrategy.js.map +1 -0
- package/dist/layers/heuristics/missingOutputFormat.d.ts +2 -0
- package/dist/layers/heuristics/missingOutputFormat.d.ts.map +1 -0
- package/dist/layers/heuristics/missingOutputFormat.js +31 -0
- package/dist/layers/heuristics/missingOutputFormat.js.map +1 -0
- package/dist/layers/heuristics/missingScope.d.ts +2 -0
- package/dist/layers/heuristics/missingScope.d.ts.map +1 -0
- package/dist/layers/heuristics/missingScope.js +11 -0
- package/dist/layers/heuristics/missingScope.js.map +1 -0
- package/dist/layers/heuristics/noActionableIntent.d.ts +2 -0
- package/dist/layers/heuristics/noActionableIntent.d.ts.map +1 -0
- package/dist/layers/heuristics/noActionableIntent.js +14 -0
- package/dist/layers/heuristics/noActionableIntent.js.map +1 -0
- package/dist/layers/linguistic/bloatDetector.d.ts +2 -0
- package/dist/layers/linguistic/bloatDetector.d.ts.map +1 -0
- package/dist/layers/linguistic/bloatDetector.js +91 -0
- package/dist/layers/linguistic/bloatDetector.js.map +1 -0
- package/dist/layers/linguistic/chainedIntent.d.ts +3 -0
- package/dist/layers/linguistic/chainedIntent.d.ts.map +1 -0
- package/dist/layers/linguistic/chainedIntent.js +26 -0
- package/dist/layers/linguistic/chainedIntent.js.map +1 -0
- package/dist/layers/linguistic/hedgeDetector.d.ts +2 -0
- package/dist/layers/linguistic/hedgeDetector.d.ts.map +1 -0
- package/dist/layers/linguistic/hedgeDetector.js +12 -0
- package/dist/layers/linguistic/hedgeDetector.js.map +1 -0
- package/dist/layers/linguistic/negationDensity.d.ts +2 -0
- package/dist/layers/linguistic/negationDensity.d.ts.map +1 -0
- package/dist/layers/linguistic/negationDensity.js +8 -0
- package/dist/layers/linguistic/negationDensity.js.map +1 -0
- package/dist/layers/linguistic/passiveVoice.d.ts +2 -0
- package/dist/layers/linguistic/passiveVoice.d.ts.map +1 -0
- package/dist/layers/linguistic/passiveVoice.js +8 -0
- package/dist/layers/linguistic/passiveVoice.js.map +1 -0
- package/dist/layers/linguistic/pronounAmbiguity.d.ts +2 -0
- package/dist/layers/linguistic/pronounAmbiguity.d.ts.map +1 -0
- package/dist/layers/linguistic/pronounAmbiguity.js +25 -0
- package/dist/layers/linguistic/pronounAmbiguity.js.map +1 -0
- package/dist/layers/linguistic/tokenCount.d.ts +2 -0
- package/dist/layers/linguistic/tokenCount.d.ts.map +1 -0
- package/dist/layers/linguistic/tokenCount.js +6 -0
- package/dist/layers/linguistic/tokenCount.js.map +1 -0
- package/dist/layers/semantic/classifier.d.ts +7 -0
- package/dist/layers/semantic/classifier.d.ts.map +1 -0
- package/dist/layers/semantic/classifier.js +40 -0
- package/dist/layers/semantic/classifier.js.map +1 -0
- package/dist/layers/syscan/auditor.d.ts +12 -0
- package/dist/layers/syscan/auditor.d.ts.map +1 -0
- package/dist/layers/syscan/auditor.js +108 -0
- package/dist/layers/syscan/auditor.js.map +1 -0
- package/dist/layers/syscan/qePersona.d.ts +2 -0
- package/dist/layers/syscan/qePersona.d.ts.map +1 -0
- package/dist/layers/syscan/qePersona.js +45 -0
- package/dist/layers/syscan/qePersona.js.map +1 -0
- package/dist/scorer/fixSuggestions.d.ts +2 -0
- package/dist/scorer/fixSuggestions.d.ts.map +1 -0
- package/dist/scorer/fixSuggestions.js +18 -0
- package/dist/scorer/fixSuggestions.js.map +1 -0
- package/dist/scorer/scorer.d.ts +23 -0
- package/dist/scorer/scorer.d.ts.map +1 -0
- package/dist/scorer/scorer.js +122 -0
- package/dist/scorer/scorer.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# PromptVet
|
|
4
|
+
|
|
5
|
+
### Catch a bad prompt before it costs you tokens — not after.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@promptvet/core)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](#architecture)
|
|
11
|
+
[](CLAUDE.md)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @promptvet/core scan "your prompt here"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
*No install. No API key. No account. Nothing leaves your machine.*
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## ⚡ See It In Action
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
$ promptvet scan "write tests for my app"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```diff
|
|
30
|
+
PromptVet Analysis
|
|
31
|
+
──────────────────
|
|
32
|
+
Overall Score: 59/100
|
|
33
|
+
|
|
34
|
+
Issues Found:
|
|
35
|
+
- ✗ [CRITICAL] MISSING_SCOPE: 'my app' isn't specific enough.
|
|
36
|
+
|
|
37
|
+
Suggested Fixes:
|
|
38
|
+
+ Name the specific file, function, or component instead
|
|
39
|
+
+ of using vague references like 'my app' or 'this feature'.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🧠 The One Thing Regex Can't Catch — But This Does
|
|
45
|
+
|
|
46
|
+
Pattern-matching alone misses prompts that are **grammatically perfect but semantically empty.** That's the gap PromptVet's two-layer engine is built to close.
|
|
47
|
+
|
|
48
|
+
<table>
|
|
49
|
+
<tr>
|
|
50
|
+
<td width="50%" valign="top">
|
|
51
|
+
|
|
52
|
+
**❌ Looks fine. Isn't.**
|
|
53
|
+
```bash
|
|
54
|
+
$ promptvet scan "improve error handling"
|
|
55
|
+
|
|
56
|
+
Running deeper analysis...
|
|
57
|
+
✗ [CRITICAL] SEMANTIC_LOW_QUALITY
|
|
58
|
+
Too vague for a model to identify
|
|
59
|
+
a clear instruction.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
</td>
|
|
63
|
+
<td width="50%" valign="top">
|
|
64
|
+
|
|
65
|
+
**✅ Same shape. Actually specific.**
|
|
66
|
+
```bash
|
|
67
|
+
$ promptvet scan "optimize the login
|
|
68
|
+
query in UserRepository.ts"
|
|
69
|
+
|
|
70
|
+
Overall Score: 100/100
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
</td>
|
|
74
|
+
</tr>
|
|
75
|
+
</table>
|
|
76
|
+
|
|
77
|
+
Same sentence structure. Same grammar. One ships, one wastes a round trip. No regex on earth tells those apart — that's why PromptVet has two layers, not one.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🏗️ How It Works
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
┌─────────────────────────────────────────────────────┐
|
|
85
|
+
│ 1. LINGUISTIC + HEURISTICS → milliseconds │
|
|
86
|
+
│ Deterministic. Pattern-based. Zero cost. │
|
|
87
|
+
│ Catches ~90% of bad prompts instantly. │
|
|
88
|
+
└───────────────────────┬───────────────────────────────┘
|
|
89
|
+
│ clean but short & generic?
|
|
90
|
+
▼
|
|
91
|
+
┌─────────────────────────────────────────────────────┐
|
|
92
|
+
│ 2. SEMANTIC LAYER → a couple of seconds │
|
|
93
|
+
│ Local model inference. Runs on YOUR machine. │
|
|
94
|
+
│ Only triggers when layer 1 can't decide. │
|
|
95
|
+
│ Always tells you when it's running. No surprises. │
|
|
96
|
+
└─────────────────────────────────────────────────────┘
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Most scans never reach layer 2 — they're caught instantly. The semantic layer is the safety net for prompts that *look* fine and aren't.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 🚀 Quick Start
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Inline string
|
|
107
|
+
promptvet scan "write unit tests for my geolocation feature"
|
|
108
|
+
|
|
109
|
+
# A file
|
|
110
|
+
promptvet scan prompt.txt
|
|
111
|
+
|
|
112
|
+
# Machine-readable, for scripts/CI/editor integrations
|
|
113
|
+
promptvet scan "your prompt" --json
|
|
114
|
+
|
|
115
|
+
# Stricter pass bar (default: 60)
|
|
116
|
+
promptvet scan "your prompt" --threshold 70
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Exit Code | Meaning |
|
|
120
|
+
|:---:|---|
|
|
121
|
+
| `0` | Passed — score above threshold, no critical issues |
|
|
122
|
+
| `1` | Failed — low score, **or** a critical issue (even if the score looks fine) |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 🔍 What It Catches
|
|
127
|
+
|
|
128
|
+
<details open>
|
|
129
|
+
<summary><b>Pattern-based checks — instant, free, deterministic</b></summary>
|
|
130
|
+
<br>
|
|
131
|
+
|
|
132
|
+
| Check | Catches |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `geoFlakeRisk` / `browserApiRisk` | Unit-testing browser-only APIs — a known flake trap |
|
|
135
|
+
| `missingFramework` | No test framework named — JS/TS, **pytest, JUnit, RSpec, go test, PHPUnit** & more |
|
|
136
|
+
| `missingMockStrategy` | External dependency (API/DB/ORM) with no mocking plan |
|
|
137
|
+
| `missingOutputFormat` | Generation request with no format specified |
|
|
138
|
+
| `missingScope` | "my app", "this feature" — vague reference, nothing named |
|
|
139
|
+
| `noActionableIntent` | No real instruction at all — *but bug reports are exempted, see below* |
|
|
140
|
+
| `frustrationSignal` | Profanity / visible frustration — a nudge that stressed prompts skip details |
|
|
141
|
+
| `hedgeDetector` `passiveVoice` `pronounAmbiguity` `negationDensity` `chainedIntent` `bloatDetector` | Hedge words, passive voice, unresolved pronouns, filler, chained requests |
|
|
142
|
+
|
|
143
|
+
</details>
|
|
144
|
+
|
|
145
|
+
<details>
|
|
146
|
+
<summary><b>Semantic check — only when pattern-matching can't decide</b></summary>
|
|
147
|
+
<br>
|
|
148
|
+
|
|
149
|
+
A local model classifies whether a short, pattern-clean prompt is actually specific enough to act on. This is what catches `"improve error handling"` above — nothing else in the engine can.
|
|
150
|
+
|
|
151
|
+
</details>
|
|
152
|
+
|
|
153
|
+
> **Worth knowing:** `noActionableIntent` doesn't punish bug reports. *"The rest timer resets after clicking increment"* has no action verb — but it's a perfectly good prompt, the fix is implied. PromptVet recognizes the shape of a bug report and lets it through.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 📦 Sample JSON Output
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"overall": 63.5,
|
|
162
|
+
"dimensions": {
|
|
163
|
+
"ambiguity": 70,
|
|
164
|
+
"tokenBloat": 100,
|
|
165
|
+
"clarity": 75,
|
|
166
|
+
"riskSignals": 0
|
|
167
|
+
},
|
|
168
|
+
"issues": [
|
|
169
|
+
{
|
|
170
|
+
"severity": "critical",
|
|
171
|
+
"code": "FLAKE_RISK_GEO",
|
|
172
|
+
"message": "Geolocation APIs are browser-dependent — unit testing is flake-prone."
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
"suggestedFixes": [
|
|
176
|
+
"Consider integration or E2E tests instead of unit tests for browser-dependent APIs."
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Every issue maps to a pre-written fix from a deterministic lookup table — pattern-based suggestions aren't generated, they're looked up.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 🏛️ Architecture
|
|
186
|
+
|
|
187
|
+
| | |
|
|
188
|
+
|---|---|
|
|
189
|
+
| 🔑 **Zero API keys** | Not optional, not configurable, never will be |
|
|
190
|
+
| 👤 **Zero accounts** | None needed, none planned |
|
|
191
|
+
| 🔒 **Local-first** | The semantic layer runs on your machine — prompts never leave it |
|
|
192
|
+
| 🧪 **TDD-built** | Every detector has a failing test before it has an implementation |
|
|
193
|
+
| 🧬 **Pure functions** | The pattern-matching layers have nothing to mock, because there's nothing impure to mock |
|
|
194
|
+
| 📘 **TypeScript strict**, Node 22, ESM only | |
|
|
195
|
+
| ⏱️ **Honest about cost** | Pattern-matching is near-instant. The semantic layer takes a couple of seconds and always says so first. We'd rather be transparent than pretend to be magic. |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 🗺️ Roadmap
|
|
200
|
+
|
|
201
|
+
**V4 (planned):** Claude Code hook integration — `@promptvet/hook`. A `PreToolUse` hook that intercepts your prompt before Claude Code fires any tool, runs `scan` automatically, and blocks on critical issues. Ambient enforcement without remembering to run it manually. Separate package, separate install.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 💡 Getting the Best Results
|
|
206
|
+
|
|
207
|
+
### For `scan`
|
|
208
|
+
|
|
209
|
+
Use file input for long or complex prompts — especially anything with
|
|
210
|
+
special characters like `[`, `]`, `*`, or `@`:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
promptvet scan prompt.txt
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**The iteration loop:** PromptVet works best as a feedback cycle, not
|
|
217
|
+
a one-shot check. Scan → fix what it flags → scan again. The tool
|
|
218
|
+
found issues in its own Claude instruction prompt and helped refine it
|
|
219
|
+
to 100/100 in three iterations.
|
|
220
|
+
|
|
221
|
+
### For `syscan`
|
|
222
|
+
|
|
223
|
+
**Best results with well-structured, standard agent prompts** — role
|
|
224
|
+
definition, scope, constraints, under ~300-400 words:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Works well
|
|
228
|
+
promptvet syscan customer-support-agent.txt
|
|
229
|
+
promptvet syscan coding-assistant.txt
|
|
230
|
+
promptvet syscan content-moderator.txt
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Known limitation — complex editorial specifications:** very long
|
|
234
|
+
prompts (500+ words) with dense rule sets, custom output markers, or
|
|
235
|
+
deeply nested constraints may produce unreliable results. The local
|
|
236
|
+
1.5B model can be overwhelmed by constraint density — the same
|
|
237
|
+
complexity that confuses the model also makes the prompt harder for
|
|
238
|
+
humans to maintain.
|
|
239
|
+
|
|
240
|
+
If `syscan` finds the same false gap on repeated runs, that's a signal
|
|
241
|
+
the prompt itself may be too complex — consider simplifying before
|
|
242
|
+
re-auditing.
|
|
243
|
+
|
|
244
|
+
**Use file input for system prompts** — system prompts routinely
|
|
245
|
+
contain special characters that shells interpret as glob patterns:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
promptvet syscan system-prompt.txt # always use file input for syscan
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### The Full Pre-Flight Check
|
|
252
|
+
|
|
253
|
+
Run both commands together for complete coverage:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# Check your dev-task prompt for clarity
|
|
257
|
+
promptvet scan "write unit tests for the auth module"
|
|
258
|
+
|
|
259
|
+
# Check your system prompt for coverage gaps
|
|
260
|
+
promptvet syscan system-prompt.txt
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`scan` catches what's unclear in a single instruction.
|
|
264
|
+
`syscan` catches what's missing from a production spec.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 🤝 Contributing
|
|
269
|
+
|
|
270
|
+
Read [`CLAUDE.md`](CLAUDE.md) first — it's the real contract this project is built against: TDD rules, architecture boundaries, and what's explicitly out of scope. PRs that don't follow it won't merge.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
<div align="center">
|
|
275
|
+
|
|
276
|
+
**MIT Licensed** · Built prompt by prompt, bug by bug, on real projects.
|
|
277
|
+
|
|
278
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { score } from '../scorer/scorer.js';
|
|
5
|
+
import { fixSuggestions } from '../scorer/fixSuggestions.js';
|
|
6
|
+
import { DEFAULT_MODEL } from '../layers/syscan/auditor.js';
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name('promptvet')
|
|
10
|
+
.description('Analyze LLM prompts before they are sent to any AI');
|
|
11
|
+
program
|
|
12
|
+
.command('scan <prompt>')
|
|
13
|
+
.description('Scan a prompt string or file for quality issues')
|
|
14
|
+
.option('--json', 'Output results as JSON')
|
|
15
|
+
.option('--threshold <number>', 'Pass/fail score threshold (0-100)', '60')
|
|
16
|
+
.action(async (input, options) => {
|
|
17
|
+
const jsonMode = options.json === true;
|
|
18
|
+
const threshold = parseInt(options.threshold, 10);
|
|
19
|
+
// Only treat input as a file path if it ends with a known extension.
|
|
20
|
+
// Single-word or no-space strings without an extension are inline prompts.
|
|
21
|
+
const FILE_EXTENSIONS = ['.txt', '.md'];
|
|
22
|
+
let rawPrompt = input;
|
|
23
|
+
if (FILE_EXTENSIONS.some((ext) => input.endsWith(ext))) {
|
|
24
|
+
if (!existsSync(input)) {
|
|
25
|
+
emitError('FILE_NOT_FOUND', `File not found: ${input}`, jsonMode);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
rawPrompt = readFileSync(input, 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
const result = score(rawPrompt);
|
|
31
|
+
if ('error' in result) {
|
|
32
|
+
emitError(result.code, result.message, jsonMode);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
// Semantic layer: only when heuristics+linguistic found zero issues AND prompt is short.
|
|
36
|
+
// Short vague prompts (e.g. "fix all things") can score 100/100 on heuristics yet be
|
|
37
|
+
// meaningless — this is the gap the classifier is designed to catch.
|
|
38
|
+
const wordCount = rawPrompt.trim().split(/\s+/).length;
|
|
39
|
+
let semanticDispose = null;
|
|
40
|
+
if (result.issues.length === 0 && wordCount < 8) {
|
|
41
|
+
process.stderr.write('Running deeper analysis...\n');
|
|
42
|
+
const { classifyIntent, disposeClassifier } = await import('../layers/semantic/classifier.js');
|
|
43
|
+
semanticDispose = disposeClassifier;
|
|
44
|
+
const semantic = await classifyIntent(rawPrompt);
|
|
45
|
+
if (!semantic.isMeaningful) {
|
|
46
|
+
result.issues.push({
|
|
47
|
+
severity: 'critical',
|
|
48
|
+
code: 'SEMANTIC_LOW_QUALITY',
|
|
49
|
+
message: 'This prompt appears too vague or unclear for the model to identify a clear instruction.',
|
|
50
|
+
});
|
|
51
|
+
result.overall = Math.min(result.overall, 59);
|
|
52
|
+
const fix = fixSuggestions['SEMANTIC_LOW_QUALITY'];
|
|
53
|
+
if (fix)
|
|
54
|
+
result.suggestedFixes.push(fix);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const hasCritical = result.issues.some((i) => i.severity === 'critical');
|
|
58
|
+
const passed = result.overall >= threshold && !hasCritical;
|
|
59
|
+
if (jsonMode) {
|
|
60
|
+
write(JSON.stringify({
|
|
61
|
+
overall: result.overall,
|
|
62
|
+
dimensions: result.dimensions,
|
|
63
|
+
issues: result.issues,
|
|
64
|
+
suggestedFixes: result.suggestedFixes,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
write(humanFormat(result));
|
|
69
|
+
}
|
|
70
|
+
if (semanticDispose)
|
|
71
|
+
await semanticDispose();
|
|
72
|
+
process.exit(passed ? 0 : 1);
|
|
73
|
+
});
|
|
74
|
+
program
|
|
75
|
+
.command('syscan <prompt>')
|
|
76
|
+
.description('Deep audit of a system prompt for coverage gaps')
|
|
77
|
+
.option('--json', 'Output results as JSON')
|
|
78
|
+
.option('--model <modelUri>', 'Model to use: hf: URI or local .gguf path', DEFAULT_MODEL)
|
|
79
|
+
.action(async (input, options) => {
|
|
80
|
+
const jsonMode = options.json === true;
|
|
81
|
+
const modelUri = options.model;
|
|
82
|
+
if (!modelUri.startsWith('hf:') && !modelUri.endsWith('.gguf')) {
|
|
83
|
+
emitError('INVALID_MODEL', 'Invalid model: must be an hf: URI (e.g. hf:bartowski/SmolLM3-3B-GGUF:Q4_K_M) or a local .gguf file path.', jsonMode);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
const FILE_EXTENSIONS = ['.txt', '.md'];
|
|
87
|
+
let rawPrompt = input;
|
|
88
|
+
if (FILE_EXTENSIONS.some((ext) => input.endsWith(ext))) {
|
|
89
|
+
if (!existsSync(input)) {
|
|
90
|
+
emitError('FILE_NOT_FOUND', `File not found: ${input}`, jsonMode);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
rawPrompt = readFileSync(input, 'utf-8');
|
|
94
|
+
}
|
|
95
|
+
if (!rawPrompt || rawPrompt.trim() === '') {
|
|
96
|
+
emitError('EMPTY_PROMPT', 'Prompt must not be empty.', jsonMode);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
if (rawPrompt.trim().split(/\s+/).length < 3) {
|
|
100
|
+
emitError('PROMPT_TOO_SHORT', 'Prompt must be at least 3 words.', jsonMode);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
const stopSpinner = startSpinner('Running deep system-prompt audit...');
|
|
104
|
+
const { auditSystemPrompt } = await import('../layers/syscan/auditor.js');
|
|
105
|
+
const result = await auditSystemPrompt(rawPrompt, modelUri);
|
|
106
|
+
const elapsed = stopSpinner();
|
|
107
|
+
if (jsonMode) {
|
|
108
|
+
write(JSON.stringify({ gaps: result.gaps, raw: result.raw }));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
write(syscanHumanFormat(result.gaps, elapsed));
|
|
112
|
+
}
|
|
113
|
+
process.exit(0);
|
|
114
|
+
});
|
|
115
|
+
await program.parseAsync();
|
|
116
|
+
// ── spinner ───────────────────────────────────────────────────────────────────
|
|
117
|
+
function startSpinner(message) {
|
|
118
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
119
|
+
const CLEAR_WIDTH = message.length + 16;
|
|
120
|
+
let frameIdx = 0;
|
|
121
|
+
const startTime = Date.now();
|
|
122
|
+
const render = () => {
|
|
123
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
124
|
+
process.stderr.write(`\r${frames[frameIdx % frames.length]} ${message} ${elapsed}s`);
|
|
125
|
+
frameIdx++;
|
|
126
|
+
};
|
|
127
|
+
render();
|
|
128
|
+
const interval = setInterval(render, 100);
|
|
129
|
+
return () => {
|
|
130
|
+
clearInterval(interval);
|
|
131
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
132
|
+
process.stderr.write('\r' + ' '.repeat(CLEAR_WIDTH) + '\r');
|
|
133
|
+
return elapsed;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// ── syscan output ─────────────────────────────────────────────────────────────
|
|
137
|
+
function syscanHumanFormat(gaps, elapsed) {
|
|
138
|
+
const lines = [
|
|
139
|
+
`PromptVet System Audit (${elapsed}s)`,
|
|
140
|
+
'───────────────────────',
|
|
141
|
+
'',
|
|
142
|
+
`Gaps Found (${gaps.length}):`,
|
|
143
|
+
];
|
|
144
|
+
if (gaps.length === 0) {
|
|
145
|
+
lines.push('', ' No gaps detected in this run.');
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
for (let i = 0; i < gaps.length; i++) {
|
|
149
|
+
const gap = gaps[i];
|
|
150
|
+
if (!gap)
|
|
151
|
+
continue;
|
|
152
|
+
lines.push('', ` ${i + 1}. ${gap.summary}`);
|
|
153
|
+
if (gap.detail)
|
|
154
|
+
lines.push(` ${gap.detail}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
lines.push('', 'Note: There maybe false positives. Please verify each gap before treating it as confirmed.', '');
|
|
158
|
+
return lines.join('\n');
|
|
159
|
+
}
|
|
160
|
+
// ── output helpers ────────────────────────────────────────────────────────────
|
|
161
|
+
function emitError(code, message, jsonMode) {
|
|
162
|
+
if (jsonMode) {
|
|
163
|
+
write(JSON.stringify({ error: true, code, message }));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
write(`✗ Error [${code}]: ${message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function write(text) {
|
|
170
|
+
process.stdout.write(text + '\n');
|
|
171
|
+
}
|
|
172
|
+
function bar(n) {
|
|
173
|
+
const filled = Math.round(n / 10);
|
|
174
|
+
return '█'.repeat(filled) + '░'.repeat(10 - filled);
|
|
175
|
+
}
|
|
176
|
+
function dimIcon(n) {
|
|
177
|
+
if (n >= 80)
|
|
178
|
+
return '✓';
|
|
179
|
+
if (n >= 60)
|
|
180
|
+
return '⚠';
|
|
181
|
+
return '✗';
|
|
182
|
+
}
|
|
183
|
+
function score3(n) {
|
|
184
|
+
return String(n).padStart(3);
|
|
185
|
+
}
|
|
186
|
+
function dimLabel(dim, v) {
|
|
187
|
+
if (dim === 'ambiguity') {
|
|
188
|
+
if (v === 0)
|
|
189
|
+
return 'Zero ambiguity';
|
|
190
|
+
if (v <= 25)
|
|
191
|
+
return 'Very low ambiguity';
|
|
192
|
+
if (v <= 50)
|
|
193
|
+
return 'Some ambiguity';
|
|
194
|
+
if (v <= 75)
|
|
195
|
+
return 'High ambiguity';
|
|
196
|
+
if (v < 100)
|
|
197
|
+
return 'Very high ambiguity';
|
|
198
|
+
return 'Maximum ambiguity';
|
|
199
|
+
}
|
|
200
|
+
if (dim === 'tokenBloat') {
|
|
201
|
+
if (v === 0)
|
|
202
|
+
return 'Zero bloated tokens';
|
|
203
|
+
if (v <= 25)
|
|
204
|
+
return 'Very low token bloat';
|
|
205
|
+
if (v <= 50)
|
|
206
|
+
return 'Some token bloat';
|
|
207
|
+
if (v <= 75)
|
|
208
|
+
return 'High token bloat';
|
|
209
|
+
if (v < 100)
|
|
210
|
+
return 'Heavy token bloat';
|
|
211
|
+
return 'Maximum token bloat';
|
|
212
|
+
}
|
|
213
|
+
if (dim === 'clarity') {
|
|
214
|
+
if (v === 100)
|
|
215
|
+
return 'Perfect linguistic quality';
|
|
216
|
+
if (v >= 75)
|
|
217
|
+
return 'Good linguistic quality';
|
|
218
|
+
if (v >= 50)
|
|
219
|
+
return 'Moderate linguistic quality';
|
|
220
|
+
if (v >= 25)
|
|
221
|
+
return 'Low linguistic quality';
|
|
222
|
+
if (v > 0)
|
|
223
|
+
return 'Very poor linguistic quality';
|
|
224
|
+
return 'No linguistic quality detected';
|
|
225
|
+
}
|
|
226
|
+
// riskSignals
|
|
227
|
+
if (v === 0)
|
|
228
|
+
return 'Zero structural risk';
|
|
229
|
+
if (v <= 25)
|
|
230
|
+
return 'Very low structural risk';
|
|
231
|
+
if (v <= 50)
|
|
232
|
+
return 'Some structural risk';
|
|
233
|
+
if (v <= 75)
|
|
234
|
+
return 'High structural risk';
|
|
235
|
+
if (v < 100)
|
|
236
|
+
return 'Severe structural risk';
|
|
237
|
+
return 'Maximum structural risk';
|
|
238
|
+
}
|
|
239
|
+
function humanFormat(result) {
|
|
240
|
+
const { overall, dimensions, issues, suggestedFixes } = result;
|
|
241
|
+
const dispAmbiguity = 100 - dimensions.ambiguity;
|
|
242
|
+
const dispTokenBloat = 100 - dimensions.tokenBloat;
|
|
243
|
+
const dispRiskSignals = 100 - dimensions.riskSignals;
|
|
244
|
+
const dispClarity = dimensions.clarity;
|
|
245
|
+
const lines = [
|
|
246
|
+
'PromptVet Analysis',
|
|
247
|
+
'──────────────────',
|
|
248
|
+
`Overall Score: ${overall}/100`,
|
|
249
|
+
'',
|
|
250
|
+
'Dimensions:',
|
|
251
|
+
` Ambiguity ${bar(dispAmbiguity)} ${score3(dispAmbiguity)}/100 ${dimIcon(dimensions.ambiguity)} (${dimLabel('ambiguity', dispAmbiguity)})`,
|
|
252
|
+
` Token Bloat ${bar(dispTokenBloat)} ${score3(dispTokenBloat)}/100 ${dimIcon(dimensions.tokenBloat)} (${dimLabel('tokenBloat', dispTokenBloat)})`,
|
|
253
|
+
` Linguistic Quality ${bar(dispClarity)} ${score3(dispClarity)}/100 ${dimIcon(dimensions.clarity)} (${dimLabel('clarity', dispClarity)})`,
|
|
254
|
+
` Risk Signals ${bar(dispRiskSignals)} ${score3(dispRiskSignals)}/100 ${dimIcon(dimensions.riskSignals)} (${dimLabel('riskSignals', dispRiskSignals)})`,
|
|
255
|
+
];
|
|
256
|
+
if (issues.length > 0) {
|
|
257
|
+
lines.push('', 'Issues Found:');
|
|
258
|
+
for (const issue of issues) {
|
|
259
|
+
const sym = issue.severity === 'critical' ? '✗' : '⚠';
|
|
260
|
+
const tag = issue.severity === 'critical' ? '[CRITICAL]' : '[WARNING] ';
|
|
261
|
+
lines.push(` ${sym} ${tag} ${issue.code}: ${issue.message}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (suggestedFixes.length > 0) {
|
|
265
|
+
lines.push('', 'Suggested Fixes:');
|
|
266
|
+
for (const fix of suggestedFixes) {
|
|
267
|
+
lines.push(` - ${fix}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
lines.push('');
|
|
271
|
+
return lines.join('\n');
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionVerbs.d.ts","sourceRoot":"","sources":["../../../src/layers/heuristics/actionVerbs.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,UAG5B,CAAA;AAED,eAAO,MAAM,SAAS,UAA8D,CAAA;AAKpF,eAAO,MAAM,YAAY,UAA6D,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const GENERATION_VERBS = [
|
|
2
|
+
'generate', 'write', 'create', 'produce', 'summarize', 'summarise',
|
|
3
|
+
'draft', 'compose', 'build', 'make', 'explain', 'describe', 'list',
|
|
4
|
+
];
|
|
5
|
+
export const FIX_VERBS = ['fix', 'debug', 'refactor', 'resolve', 'patch', 'correct'];
|
|
6
|
+
// Additional action verbs not used by missingOutputFormat's fix-request exemption
|
|
7
|
+
const MODIFICATION_VERBS = ['add', 'update', 'remove', 'implement', 'test', 'analyze', 'analyse', 'improve', 'optimize'];
|
|
8
|
+
export const ACTION_VERBS = [...GENERATION_VERBS, ...FIX_VERBS, ...MODIFICATION_VERBS];
|
|
9
|
+
//# sourceMappingURL=actionVerbs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionVerbs.js","sourceRoot":"","sources":["../../../src/layers/heuristics/actionVerbs.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW;IAClE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM;CACnE,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAAA;AAEpF,kFAAkF;AAClF,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;AAExH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAG,gBAAgB,EAAE,GAAG,SAAS,EAAE,GAAG,kBAAkB,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browserApiRisk.d.ts","sourceRoot":"","sources":["../../../src/layers/heuristics/browserApiRisk.ts"],"names":[],"mappings":"AAWA,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKtD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const BROWSER_API_KEYWORDS = [
|
|
2
|
+
'navigator', 'window', 'document', 'localstorage', 'sessionstorage',
|
|
3
|
+
'cachestorage', 'cache storage', 'storagebuckets', 'storage buckets',
|
|
4
|
+
'indexeddb', 'cookies', 'cookie',
|
|
5
|
+
'pushmessaging', 'push messaging', 'pushmanager',
|
|
6
|
+
'reportingapi', 'reporting api', 'reportingobserver',
|
|
7
|
+
'extensions', 'chrome.runtime', 'browser.runtime',
|
|
8
|
+
'getusermedia', 'matchmedia', 'mediarecorder',
|
|
9
|
+
];
|
|
10
|
+
const UNIT_TEST_PATTERN = /\bunit tests?\b/i;
|
|
11
|
+
export function browserApiRisk(prompt) {
|
|
12
|
+
if (!prompt)
|
|
13
|
+
return false;
|
|
14
|
+
const lower = prompt.toLowerCase();
|
|
15
|
+
const hasBrowserApi = BROWSER_API_KEYWORDS.some(api => lower.includes(api));
|
|
16
|
+
return hasBrowserApi && UNIT_TEST_PATTERN.test(prompt);
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=browserApiRisk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browserApiRisk.js","sourceRoot":"","sources":["../../../src/layers/heuristics/browserApiRisk.ts"],"names":[],"mappings":"AAAA,MAAM,oBAAoB,GAAG;IAC3B,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB;IACnE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB;IACpE,WAAW,EAAE,SAAS,EAAE,QAAQ;IAChC,eAAe,EAAE,gBAAgB,EAAE,aAAa;IAChD,cAAc,EAAE,eAAe,EAAE,mBAAmB;IACpD,YAAY,EAAE,gBAAgB,EAAE,iBAAiB;IACjD,cAAc,EAAE,YAAY,EAAE,eAAe;CAC9C,CAAC;AACF,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAE7C,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,MAAM,aAAa,GAAG,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E,OAAO,aAAa,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frustrationSignal.d.ts","sourceRoot":"","sources":["../../../src/layers/heuristics/frustrationSignal.ts"],"names":[],"mappings":"AAuBA,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKzD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const PROFANITY = [
|
|
2
|
+
/\bfuck\b/i,
|
|
3
|
+
/\bshit\b/i,
|
|
4
|
+
/\bdamn\b/i,
|
|
5
|
+
/\bwtf\b/i,
|
|
6
|
+
/\bffs\b/i,
|
|
7
|
+
];
|
|
8
|
+
const FRUSTRATION_PHRASES = [
|
|
9
|
+
/this is ridiculous/i,
|
|
10
|
+
/i give up/i,
|
|
11
|
+
/nothing works/i,
|
|
12
|
+
/nothing is working/i,
|
|
13
|
+
/why isn't this working/i,
|
|
14
|
+
/why is this happening/i,
|
|
15
|
+
/why does this keep/i,
|
|
16
|
+
/why won't this/i,
|
|
17
|
+
/this makes no sense/i,
|
|
18
|
+
/still broken/i,
|
|
19
|
+
/still not working/i,
|
|
20
|
+
/i've tried everything/i,
|
|
21
|
+
];
|
|
22
|
+
export function frustrationSignal(prompt) {
|
|
23
|
+
return (PROFANITY.some((re) => re.test(prompt)) ||
|
|
24
|
+
FRUSTRATION_PHRASES.some((re) => re.test(prompt)));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=frustrationSignal.js.map
|