scamscript 0.6.1__tar.gz → 0.8.0__tar.gz

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.
Files changed (84) hide show
  1. {scamscript-0.6.1 → scamscript-0.8.0}/CHANGELOG.md +34 -0
  2. {scamscript-0.6.1 → scamscript-0.8.0}/PKG-INFO +49 -4
  3. {scamscript-0.6.1 → scamscript-0.8.0}/README.md +48 -3
  4. scamscript-0.8.0/corpus/real/real-commonwealth-bank-card-vishing.txt +1459 -0
  5. scamscript-0.8.0/corpus/real/real-credit-union-card-vishing.txt +47 -0
  6. scamscript-0.8.0/corpus/real/real-ftc-apple-support-robocall.txt +7 -0
  7. scamscript-0.8.0/corpus/real/real-ftc-interest-rate-robocall.txt +7 -0
  8. scamscript-0.8.0/corpus/real/real-ftc-irs-settlement-robocall.txt +7 -0
  9. scamscript-0.8.0/corpus/real/real-ftc-ssa-suspension-robocall.txt +7 -0
  10. scamscript-0.8.0/corpus/real/real-ftc-student-loan-robocall.txt +7 -0
  11. scamscript-0.8.0/corpus/real/real-ftc-utility-rebate-robocall.txt +7 -0
  12. scamscript-0.8.0/corpus/real/real-medicare-benefits-vishing.txt +519 -0
  13. scamscript-0.8.0/corpus/real/real-usps-invalid-zip-smishing.txt +6 -0
  14. scamscript-0.8.0/docs/real-corpus.md +44 -0
  15. {scamscript-0.6.1 → scamscript-0.8.0}/pyproject.toml +1 -1
  16. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/__init__.py +1 -1
  17. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/analysis.py +21 -4
  18. scamscript-0.8.0/src/scamscript/brief.py +178 -0
  19. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/cli.py +120 -6
  20. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/client.py +1 -1
  21. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/config.py +2 -1
  22. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/ingest.py +33 -13
  23. scamscript-0.8.0/src/scamscript/language.py +62 -0
  24. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/models.py +17 -0
  25. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/prompts.py +13 -5
  26. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/render.py +1 -0
  27. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/report.py +10 -2
  28. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/taxonomy.py +28 -5
  29. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/verify.py +24 -6
  30. scamscript-0.8.0/tests/test_brief.py +76 -0
  31. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_cli.py +68 -6
  32. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_ingest.py +32 -0
  33. scamscript-0.8.0/tests/test_language.py +28 -0
  34. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_report.py +10 -1
  35. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_taxonomy.py +27 -0
  36. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_verify.py +32 -0
  37. {scamscript-0.6.1 → scamscript-0.8.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  38. {scamscript-0.6.1 → scamscript-0.8.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  39. {scamscript-0.6.1 → scamscript-0.8.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  40. {scamscript-0.6.1 → scamscript-0.8.0}/.github/SECURITY.md +0 -0
  41. {scamscript-0.6.1 → scamscript-0.8.0}/.github/pull_request_template.md +0 -0
  42. {scamscript-0.6.1 → scamscript-0.8.0}/.github/workflows/ci.yml +0 -0
  43. {scamscript-0.6.1 → scamscript-0.8.0}/.github/workflows/release.yml +0 -0
  44. {scamscript-0.6.1 → scamscript-0.8.0}/.gitignore +0 -0
  45. {scamscript-0.6.1 → scamscript-0.8.0}/CITATION.cff +0 -0
  46. {scamscript-0.6.1 → scamscript-0.8.0}/CODE_OF_CONDUCT.md +0 -0
  47. {scamscript-0.6.1 → scamscript-0.8.0}/CONTRIBUTING.md +0 -0
  48. {scamscript-0.6.1 → scamscript-0.8.0}/LICENSE +0 -0
  49. {scamscript-0.6.1 → scamscript-0.8.0}/docs/banner.svg +0 -0
  50. {scamscript-0.6.1 → scamscript-0.8.0}/docs/calibration.md +0 -0
  51. {scamscript-0.6.1 → scamscript-0.8.0}/docs/demo.gif +0 -0
  52. {scamscript-0.6.1 → scamscript-0.8.0}/docs/sample-report.md +0 -0
  53. {scamscript-0.6.1 → scamscript-0.8.0}/docs/social-preview.png +0 -0
  54. {scamscript-0.6.1 → scamscript-0.8.0}/examples/adversarial/fake_output_json.txt +0 -0
  55. {scamscript-0.6.1 → scamscript-0.8.0}/examples/adversarial/fence_escape.txt +0 -0
  56. {scamscript-0.6.1 → scamscript-0.8.0}/examples/adversarial/fixed_attack_type.txt +0 -0
  57. {scamscript-0.6.1 → scamscript-0.8.0}/examples/adversarial/non_json_demand.txt +0 -0
  58. {scamscript-0.6.1 → scamscript-0.8.0}/examples/adversarial/print_system_prompt.txt +0 -0
  59. {scamscript-0.6.1 → scamscript-0.8.0}/examples/adversarial/role_switch.txt +0 -0
  60. {scamscript-0.6.1 → scamscript-0.8.0}/examples/adversarial/score_override.txt +0 -0
  61. {scamscript-0.6.1 → scamscript-0.8.0}/examples/crude_robocall.txt +0 -0
  62. {scamscript-0.6.1 → scamscript-0.8.0}/examples/gov_impersonation_vishing.txt +0 -0
  63. {scamscript-0.6.1 → scamscript-0.8.0}/examples/prompt_injection_script.txt +0 -0
  64. {scamscript-0.6.1 → scamscript-0.8.0}/examples/romance_scam_sms.txt +0 -0
  65. {scamscript-0.6.1 → scamscript-0.8.0}/examples/sophisticated_wire_fraud.txt +0 -0
  66. {scamscript-0.6.1 → scamscript-0.8.0}/examples/tech_support_scam.txt +0 -0
  67. {scamscript-0.6.1 → scamscript-0.8.0}/scripts/calibrate.py +0 -0
  68. {scamscript-0.6.1 → scamscript-0.8.0}/scripts/injection_eval.py +0 -0
  69. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/__main__.py +0 -0
  70. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/dedupe.py +0 -0
  71. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/redact.py +0 -0
  72. {scamscript-0.6.1 → scamscript-0.8.0}/src/scamscript/store.py +0 -0
  73. {scamscript-0.6.1 → scamscript-0.8.0}/tests/fixtures/reprints/copy-header.txt +0 -0
  74. {scamscript-0.6.1 → scamscript-0.8.0}/tests/fixtures/reprints/copy-punctuation.txt +0 -0
  75. {scamscript-0.6.1 → scamscript-0.8.0}/tests/fixtures/reprints/near-twin.txt +0 -0
  76. {scamscript-0.6.1 → scamscript-0.8.0}/tests/fixtures/reprints/original.txt +0 -0
  77. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_analysis.py +0 -0
  78. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_client.py +0 -0
  79. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_dedupe.py +0 -0
  80. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_models.py +0 -0
  81. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_prompts.py +0 -0
  82. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_redact.py +0 -0
  83. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_render.py +0 -0
  84. {scamscript-0.6.1 → scamscript-0.8.0}/tests/test_store.py +0 -0
@@ -5,6 +5,40 @@ schema are the things that count as public API.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.8.0 - 2026-11-02
9
+
10
+ ### Added
11
+
12
+ - `channel` field (`vishing`, `smishing`, `email`, `other`) separate from the attack theme, with
13
+ `--channel` filtering in reports and briefings.
14
+ - `brief --html`: self-contained printable page, escaped and offline.
15
+
16
+ ### Changed
17
+
18
+ - Panels show the channel; JSON records carry it; real-corpus validation confirms text lures keep
19
+ their delivery route.
20
+
21
+ ## 0.7.0 - 2026-11-02
22
+
23
+ ### Added
24
+
25
+ - `brief` command: a one-page defence handout per type from a finished run.
26
+ - `--language` override and stopword language detection; the prompt asks for the script's own language.
27
+ - `corpus/real/` with ten published transcripts, provenance in every header, and `docs/real-corpus.md`.
28
+ - `--force` before overwriting an output file.
29
+
30
+ ### Fixed
31
+
32
+ - Apostrophes inside words (`target's`) were read as quote delimiters, producing false `missing`
33
+ verdicts. Long quotes are now checked clause by clause.
34
+ - A recursive walk followed a symlink loop; it now terminates.
35
+ - A file changing mid-read is reported instead of being half-analyzed.
36
+ - Utility-rebate robocalls classified as `refund`; utility is matched first.
37
+
38
+ ### Changed
39
+
40
+ - Default `--max-tokens` 4096 → 6144, after two real calls hit the ceiling on the first attempt.
41
+
8
42
  ## 0.6.1 - 2026-11-02
9
43
 
10
44
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: scamscript
3
- Version: 0.6.1
3
+ Version: 0.8.0
4
4
  Summary: Reverse-engineer scam scripts and vishing transcripts into structured counter-playbooks
5
5
  Project-URL: Homepage, https://github.com/sjdsjadjajdjsa/scamscript
6
6
  Project-URL: Repository, https://github.com/sjdsjadjajdjsa/scamscript
@@ -111,7 +111,7 @@ Each script renders as a colour-coded panel:
111
111
  ╰────────────────────────────────── 7 red flags ───────────────────────────────╯
112
112
  ```
113
113
 
114
- Fields: `attack_type`, `persona`, `opening_hook`, `trust_building_moves`, `urgency_tactics`,
114
+ Fields: `attack_type`, `channel`, `persona`, `opening_hook`, `trust_building_moves`, `urgency_tactics`,
115
115
  `escalation_ladder`, `target_demographics`, `red_flags`, `defense_summary`, `sophistication_score`.
116
116
 
117
117
  The panel also carries a **quote check**: every quoted claim in `opening_hook` and `red_flags` is
@@ -130,11 +130,12 @@ corpus is at [`docs/sample-report.md`](https://github.com/sjdsjadjajdjsa/scamscr
130
130
  | `batch <dir>` | Same as above, ranked |
131
131
  | `report --run-id` | Regenerate a report from a past run's log |
132
132
  | `history` | List previous runs |
133
+ | `brief` | One-page defence handout from a finished run |
133
134
  | `prune` | Delete old logs |
134
135
 
135
136
  Useful flags: `--dry-run`, `--json`, `--redact`, `--top N`, `--recursive`, `--timeout`,
136
137
  `--retries`, `--max-tokens`, `--concurrency`, `--max-cost`, `--dedupe/--no-dedupe`,
137
- `--show-duplicates`, `--type`, `--no-color`. `--json` puts one object per script on stdout and all
138
+ `--show-duplicates`, `--type`, `--channel`, `--language`, `--force`, `--no-color`. `--json` puts one object per script on stdout and all
138
139
  Rich output on stderr, so it pipes:
139
140
 
140
141
  ```bash
@@ -155,6 +156,17 @@ The cap reads `usage.cost` from the response, so it works only where the provide
155
156
  says so when none is sent. Spend is printed and logged per run. In-flight requests can overshoot by
156
157
  up to the concurrency, so keep headroom on a hard limit.
157
158
 
159
+ ## Real corpus
160
+
161
+ `corpus/real/` holds ten published transcripts with source, date and verbatim status in every
162
+ header: a 40-minute bank impersonation call, a Medicare benefits pitch, six FTC robocalls, a
163
+ recalled credit-union call and a USPS smishing message. Provenance and the results of running them
164
+ are in [`docs/real-corpus.md`](https://github.com/sjdsjadjajdjsa/scamscript/blob/master/docs/real-corpus.md).
165
+
166
+ ```bash
167
+ scamscript --redact batch corpus/real --concurrency 3 --max-cost 0.25 --output real.md
168
+ ```
169
+
158
170
  ## Attack types
159
171
 
160
172
  `attack_type` is free prose, which is useful to read and useless to group. Every result also carries a
@@ -166,6 +178,20 @@ per slug above the ranking, and `--type` filters to the ones you ask for:
166
178
  scamscript batch corpus/ --type tech-support,refund --output report.md
167
179
  ```
168
180
 
181
+ Delivery is tracked separately from theme. `channel` is one of `vishing`, `smishing`, `email` or
182
+ `other`, so a delivery-themed text lure is `delivery` + `smishing` rather than losing one of the two.
183
+ `--channel` filters on it:
184
+
185
+ ```bash
186
+ scamscript batch corpus/real --channel smishing --output texts.md
187
+ ```
188
+
189
+ Briefings group by type but accept `--channel` too. Build one from a finished run:
190
+
191
+ ```bash
192
+ scamscript brief --type tech-support --output briefing.md
193
+ ```
194
+
169
195
  The raw phrase is always kept next to the slug. An unrecognised description lands in `other` and the
170
196
  CLI names any filter token it did not recognise.
171
197
 
@@ -220,6 +246,21 @@ Every request appends to a timestamped JSONL file under `~/.scamscript/logs/` (o
220
246
  a run replays without spending tokens, and `prune --keep 200 --keep-days 30` keeps the store
221
247
  bounded. Directories are `0700` and log files `0600`.
222
248
 
249
+ ## Printable briefings
250
+
251
+ `brief --html` writes a self-contained page with inline CSS and escaped text, for handing to people
252
+ who will not open a terminal.
253
+
254
+ ```bash
255
+ scamscript brief --type tech-support --html --output handout.html
256
+ ```
257
+
258
+ ## Paths
259
+
260
+ `--file` and `--dir` resolve symlinks, a recursive walk survives a symlink loop, and a file that
261
+ changes while it is read is an error rather than a half-read prompt. Output files are not overwritten
262
+ unless `--force` is passed.
263
+
223
264
  ## Install and configure
224
265
 
225
266
  Python 3.12+. `pip install scamscript` for use, `pip install -e ".[dev]"` from a clone for tests.
@@ -281,6 +322,7 @@ src/scamscript/
281
322
  verify.py quote checking against the source
282
323
  tests/ schema, retry, redaction, verification, client and CLI tests
283
324
  examples/ six fictional transcripts, plus adversarial/ fixtures
325
+ corpus/real/ ten published transcripts with provenance
284
326
  scripts/ calibration and injection harnesses
285
327
  docs/ calibration record, sample report, banner, demo
286
328
  ```
@@ -293,7 +335,10 @@ docs/ calibration record, sample report, banner, demo
293
335
  - Duplicate folding ignores formatting only; a single changed digit keeps a copy separate.
294
336
  - Type slugs are keyword-matched, so an unusual description falls to `other` rather than inventing one.
295
337
  - The cost cap depends on provider-reported usage and can overshoot by the in-flight count.
296
- - Analysis is English-centric: the prompt and schema assume English artifacts.
338
+ - `channel` is inferred from the model's answer or, failing that, from keywords; a script with no
339
+ channel words falls to `other`.
340
+ - The prompt asks the model to answer in the script's own language and `--language` overrides
341
+ detection, but the type keywords are English: a non-English script often falls to `other`.
297
342
  - Quote checking compares text, not meaning. A legitimate paraphrase left in quotes is flagged;
298
343
  an accurate paraphrase without quotes is skipped rather than judged.
299
344
 
@@ -75,7 +75,7 @@ Each script renders as a colour-coded panel:
75
75
  ╰────────────────────────────────── 7 red flags ───────────────────────────────╯
76
76
  ```
77
77
 
78
- Fields: `attack_type`, `persona`, `opening_hook`, `trust_building_moves`, `urgency_tactics`,
78
+ Fields: `attack_type`, `channel`, `persona`, `opening_hook`, `trust_building_moves`, `urgency_tactics`,
79
79
  `escalation_ladder`, `target_demographics`, `red_flags`, `defense_summary`, `sophistication_score`.
80
80
 
81
81
  The panel also carries a **quote check**: every quoted claim in `opening_hook` and `red_flags` is
@@ -94,11 +94,12 @@ corpus is at [`docs/sample-report.md`](https://github.com/sjdsjadjajdjsa/scamscr
94
94
  | `batch <dir>` | Same as above, ranked |
95
95
  | `report --run-id` | Regenerate a report from a past run's log |
96
96
  | `history` | List previous runs |
97
+ | `brief` | One-page defence handout from a finished run |
97
98
  | `prune` | Delete old logs |
98
99
 
99
100
  Useful flags: `--dry-run`, `--json`, `--redact`, `--top N`, `--recursive`, `--timeout`,
100
101
  `--retries`, `--max-tokens`, `--concurrency`, `--max-cost`, `--dedupe/--no-dedupe`,
101
- `--show-duplicates`, `--type`, `--no-color`. `--json` puts one object per script on stdout and all
102
+ `--show-duplicates`, `--type`, `--channel`, `--language`, `--force`, `--no-color`. `--json` puts one object per script on stdout and all
102
103
  Rich output on stderr, so it pipes:
103
104
 
104
105
  ```bash
@@ -119,6 +120,17 @@ The cap reads `usage.cost` from the response, so it works only where the provide
119
120
  says so when none is sent. Spend is printed and logged per run. In-flight requests can overshoot by
120
121
  up to the concurrency, so keep headroom on a hard limit.
121
122
 
123
+ ## Real corpus
124
+
125
+ `corpus/real/` holds ten published transcripts with source, date and verbatim status in every
126
+ header: a 40-minute bank impersonation call, a Medicare benefits pitch, six FTC robocalls, a
127
+ recalled credit-union call and a USPS smishing message. Provenance and the results of running them
128
+ are in [`docs/real-corpus.md`](https://github.com/sjdsjadjajdjsa/scamscript/blob/master/docs/real-corpus.md).
129
+
130
+ ```bash
131
+ scamscript --redact batch corpus/real --concurrency 3 --max-cost 0.25 --output real.md
132
+ ```
133
+
122
134
  ## Attack types
123
135
 
124
136
  `attack_type` is free prose, which is useful to read and useless to group. Every result also carries a
@@ -130,6 +142,20 @@ per slug above the ranking, and `--type` filters to the ones you ask for:
130
142
  scamscript batch corpus/ --type tech-support,refund --output report.md
131
143
  ```
132
144
 
145
+ Delivery is tracked separately from theme. `channel` is one of `vishing`, `smishing`, `email` or
146
+ `other`, so a delivery-themed text lure is `delivery` + `smishing` rather than losing one of the two.
147
+ `--channel` filters on it:
148
+
149
+ ```bash
150
+ scamscript batch corpus/real --channel smishing --output texts.md
151
+ ```
152
+
153
+ Briefings group by type but accept `--channel` too. Build one from a finished run:
154
+
155
+ ```bash
156
+ scamscript brief --type tech-support --output briefing.md
157
+ ```
158
+
133
159
  The raw phrase is always kept next to the slug. An unrecognised description lands in `other` and the
134
160
  CLI names any filter token it did not recognise.
135
161
 
@@ -184,6 +210,21 @@ Every request appends to a timestamped JSONL file under `~/.scamscript/logs/` (o
184
210
  a run replays without spending tokens, and `prune --keep 200 --keep-days 30` keeps the store
185
211
  bounded. Directories are `0700` and log files `0600`.
186
212
 
213
+ ## Printable briefings
214
+
215
+ `brief --html` writes a self-contained page with inline CSS and escaped text, for handing to people
216
+ who will not open a terminal.
217
+
218
+ ```bash
219
+ scamscript brief --type tech-support --html --output handout.html
220
+ ```
221
+
222
+ ## Paths
223
+
224
+ `--file` and `--dir` resolve symlinks, a recursive walk survives a symlink loop, and a file that
225
+ changes while it is read is an error rather than a half-read prompt. Output files are not overwritten
226
+ unless `--force` is passed.
227
+
187
228
  ## Install and configure
188
229
 
189
230
  Python 3.12+. `pip install scamscript` for use, `pip install -e ".[dev]"` from a clone for tests.
@@ -245,6 +286,7 @@ src/scamscript/
245
286
  verify.py quote checking against the source
246
287
  tests/ schema, retry, redaction, verification, client and CLI tests
247
288
  examples/ six fictional transcripts, plus adversarial/ fixtures
289
+ corpus/real/ ten published transcripts with provenance
248
290
  scripts/ calibration and injection harnesses
249
291
  docs/ calibration record, sample report, banner, demo
250
292
  ```
@@ -257,7 +299,10 @@ docs/ calibration record, sample report, banner, demo
257
299
  - Duplicate folding ignores formatting only; a single changed digit keeps a copy separate.
258
300
  - Type slugs are keyword-matched, so an unusual description falls to `other` rather than inventing one.
259
301
  - The cost cap depends on provider-reported usage and can overshoot by the in-flight count.
260
- - Analysis is English-centric: the prompt and schema assume English artifacts.
302
+ - `channel` is inferred from the model's answer or, failing that, from keywords; a script with no
303
+ channel words falls to `other`.
304
+ - The prompt asks the model to answer in the script's own language and `--language` overrides
305
+ detection, but the type keywords are English: a non-English script often falls to `other`.
261
306
  - Quote checking compares text, not meaning. A legitimate paraphrase left in quotes is flagged;
262
307
  an accurate paraphrase without quotes is skipped rather than judged.
263
308