stilltrue 0.4.1 → 0.5.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.
package/README.md CHANGED
@@ -36,33 +36,60 @@ project folder with your facts saved in a JSON file.
36
36
  npm install --save-dev stilltrue
37
37
  ```
38
38
 
39
- **2. Describe a check** — create a file called `stilltrue.config.mjs` in
40
- the same folder. Each check answers three questions: *where do I fetch the
41
- truth from* (`source`), *what do I believe* (`expect`), and *how do I
42
- compare the two* (`compare`):
39
+ **2. Scaffold a check** — this creates a ready-to-edit config file (it
40
+ never overwrites anything you already have):
41
+
42
+ ```bash
43
+ npx stilltrue init
44
+ ```
45
+
46
+ Open the generated `stilltrue.config.mjs`. Each check answers three
47
+ questions: *where do I fetch the truth from* (`source`), *what do I
48
+ believe* (`expect`), and *how do I compare the two* (`compare`) — and in
49
+ its simplest form, **the only editing needed is replacing strings**:
43
50
 
44
51
  ```js
45
- import { defineStilltrue, corpus, json, surname } from 'stilltrue';
52
+ import { defineStilltrue, corpus } from 'stilltrue';
46
53
 
47
54
  export default defineStilltrue({
48
55
  drift: [
49
56
  {
50
- name: 'school-board-roster',
51
- // Where the truth lives: the district's own pages.
52
- source: corpus(['https://www.myschooldistrict.org/board']),
53
- // What you believe: read your saved file, keep just the last names.
54
- expect: json('./data/school-board.json', (data) =>
55
- data.members.map((m) => surname(m.name))
56
- ),
57
- // How to compare: every name must still appear on those pages.
57
+ name: 'my-first-check',
58
+ // Where the truth lives: the official page(s) your facts came from.
59
+ source: corpus(['https://example.org/about']),
60
+ // What you believe: your facts, as short strings ("markers").
61
+ expect: ['Jane Smith', 'Main Street Office', '12 locations'],
62
+ // How to compare: every marker must still appear on those pages.
58
63
  compare: 'contains-all',
59
64
  },
60
65
  ],
61
66
  });
62
67
  ```
63
68
 
64
- This example says: *fetch the board page, and make sure every last name in
65
- my `school-board.json` file still appears somewhere on it.*
69
+ This says: *fetch that page, and make sure "Jane Smith", "Main Street
70
+ Office", and "12 locations" all still appear on it.* Swap in your own URL
71
+ and your own facts — that's a working check, no programming required.
72
+
73
+ Choosing what to assert is the judgment call — surnames beat full names,
74
+ "143 schools" beats a bare "143". The short guide:
75
+ [docs/MARKERS.md](docs/MARKERS.md).
76
+
77
+ **Leveling up (optional):** you don't have to keep the facts in the config.
78
+ If `./data/markers.json` is just a list of strings, point at it with
79
+ `expect: json('./data/markers.json')` — still no code. And if your app
80
+ already keeps structured data, derive the markers from it so your data file
81
+ stays the single source of truth:
82
+
83
+ ```js
84
+ // ./data/school-board.json: { "members": [{ "name": "Dr. Jane Smith" }, ...] }
85
+ expect: json('./data/school-board.json', (data) =>
86
+ data.members.map((m) => surname(m.name)) // keeps just "Smith" — titles
87
+ ), // and first names vary on pages
88
+ ```
89
+
90
+ The function receives *your* file's contents, whatever shape they are, and
91
+ returns the list of markers — stilltrue never needs to understand your data
92
+ structure. Update the data file and the markers update themselves.
66
93
 
67
94
  **3. Run it:**
68
95
 
package/dist/cli.js CHANGED
@@ -94,13 +94,31 @@ try {
94
94
  case 'report':
95
95
  process.exit(await report(rest));
96
96
  break;
97
+ case 'init': {
98
+ const { init } = await import("./init.js");
99
+ const { created, skipped } = await init(process.cwd());
100
+ for (const f of created)
101
+ console.log(`created ${f}`);
102
+ for (const f of skipped)
103
+ console.log(`kept existing ${f} (never overwritten)`);
104
+ if (created.length) {
105
+ console.log('\nNext steps:\n' +
106
+ ' 1. Edit stilltrue.config.mjs — point source at the real page(s) your facts came from,\n' +
107
+ ' and replace the example markers with your own facts (plain strings)\n' +
108
+ ' 2. Run: npx stilltrue drift\n' +
109
+ 'Picking good markers: docs/MARKERS.md in the stilltrue repo');
110
+ }
111
+ process.exit(0);
112
+ break;
113
+ }
97
114
  case 'golden':
98
115
  console.error('stilltrue deliberately has no golden runner — promptfoo tests your prompts; stilltrue tests your facts.\n' +
99
116
  'Use https://promptfoo.dev for prompt/answer regression evals. Doctrine + pairing guide: docs/GOLDEN.md');
100
117
  process.exit(1);
101
118
  break;
102
119
  default:
103
- console.log('usage: stilltrue drift [--config <path>] [--only <names>] [--json <path>] [--record] [--history <path>]\n' +
120
+ console.log('usage: stilltrue init\n' +
121
+ ' stilltrue drift [--config <path>] [--only <names>] [--json <path>] [--record] [--history <path>]\n' +
104
122
  ' stilltrue report [--history <path>] [--out <path>] [--title <text>]');
105
123
  process.exit(command ? 1 : 0);
106
124
  }
package/dist/init.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export interface InitResult {
2
+ created: string[];
3
+ skipped: string[];
4
+ }
5
+ /** Write the starter config. Never overwrites an existing one. */
6
+ export declare function init(dir: string): Promise<InitResult>;
package/dist/init.js ADDED
@@ -0,0 +1,72 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ const CONFIG_TEMPLATE = `// stilltrue configuration — one entry per fact-set you want watched.
5
+ // Docs: https://github.com/sg-bfoster/stilltrue#readme
6
+ // Picking good markers: https://github.com/sg-bfoster/stilltrue/blob/main/docs/MARKERS.md
7
+ import { defineStilltrue, corpus, json, surname } from 'stilltrue';
8
+
9
+ export default defineStilltrue({
10
+ drift: [
11
+ {
12
+ // A short id for this check — shows up in output and reports.
13
+ name: 'my-first-check',
14
+
15
+ // WHERE THE TRUTH LIVES: the official page(s) your facts came from.
16
+ // corpus() fetches each page and strips it down to readable text.
17
+ source: corpus([
18
+ 'https://example.org/about',
19
+ // 'https://example.org/board', // add more pages if facts span several
20
+ ]),
21
+
22
+ // WHAT YOU BELIEVE: list the facts as short strings ("markers").
23
+ // Every one of these must still appear somewhere on the page(s)
24
+ // above, or the check fails and names the missing one.
25
+ expect: [
26
+ 'Jane Smith',
27
+ 'Main Street Office',
28
+ '12 locations',
29
+ ],
30
+
31
+ // HOW TO COMPARE: 'contains-all' = every marker must appear in the
32
+ // fetched page text (case-insensitive).
33
+ compare: 'contains-all',
34
+ },
35
+
36
+ // ── Leveling up (optional) ──────────────────────────────────────────
37
+ //
38
+ // Instead of listing markers inline, you can keep them in a file.
39
+ // If ./data/markers.json contains just a list of strings —
40
+ // ["Jane Smith", "Main Street Office", "12 locations"]
41
+ // — then this works with no code at all:
42
+ //
43
+ // expect: json('./data/markers.json'),
44
+ //
45
+ // And if your app already keeps structured data — say
46
+ // ./data/board.json looks like
47
+ // { "members": [ { "name": "Dr. Jane Smith" }, { "name": "Bob Jones Jr." } ] }
48
+ // — you can derive the markers from it, so your data file stays the
49
+ // single source of truth (surname() keeps just "Smith" / "Jones",
50
+ // because pages change titles and first names more than surnames):
51
+ //
52
+ // expect: json('./data/board.json', (data) =>
53
+ // data.members.map((m) => surname(m.name))
54
+ // ),
55
+ ],
56
+ });
57
+ `;
58
+ /** Write the starter config. Never overwrites an existing one. */
59
+ export async function init(dir) {
60
+ const existingConfig = [
61
+ 'stilltrue.config.mjs',
62
+ 'stilltrue.config.ts',
63
+ 'stilltrue.config.mts',
64
+ 'stilltrue.config.js',
65
+ 'stilltrue.config.cjs',
66
+ ].find((name) => existsSync(join(dir, name)));
67
+ if (existingConfig) {
68
+ return { created: [], skipped: [existingConfig] };
69
+ }
70
+ await writeFile(join(dir, 'stilltrue.config.mjs'), CONFIG_TEMPLATE);
71
+ return { created: ['stilltrue.config.mjs'], skipped: [] };
72
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stilltrue",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Your tests check your code. stilltrue checks your facts. CI checks for AI-in-the-loop apps: drift (curated facts vs live sources), golden (regression evals), verify (judge answers against sources).",
5
5
  "keywords": [
6
6
  "drift",