stilltrue 0.5.0 → 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,8 +36,8 @@ project folder with your facts saved in a JSON file.
36
36
  npm install --save-dev stilltrue
37
37
  ```
38
38
 
39
- **2. Scaffold a check** — this creates a ready-to-edit config file and an
40
- example facts file (it never overwrites anything you already have):
39
+ **2. Scaffold a check** — this creates a ready-to-edit config file (it
40
+ never overwrites anything you already have):
41
41
 
42
42
  ```bash
43
43
  npx stilltrue init
@@ -45,35 +45,52 @@ npx stilltrue init
45
45
 
46
46
  Open the generated `stilltrue.config.mjs`. Each check answers three
47
47
  questions: *where do I fetch the truth from* (`source`), *what do I
48
- believe* (`expect`), and *how do I compare the two* (`compare`):
48
+ believe* (`expect`), and *how do I compare the two* (`compare`) — and in
49
+ its simplest form, **the only editing needed is replacing strings**:
49
50
 
50
51
  ```js
51
- import { defineStilltrue, corpus, json, surname } from 'stilltrue';
52
+ import { defineStilltrue, corpus } from 'stilltrue';
52
53
 
53
54
  export default defineStilltrue({
54
55
  drift: [
55
56
  {
56
- name: 'school-board-roster',
57
- // Where the truth lives: the district's own pages.
58
- source: corpus(['https://www.myschooldistrict.org/board']),
59
- // What you believe: read your saved file, keep just the last names.
60
- expect: json('./data/school-board.json', (data) =>
61
- data.members.map((m) => surname(m.name))
62
- ),
63
- // 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.
64
63
  compare: 'contains-all',
65
64
  },
66
65
  ],
67
66
  });
68
67
  ```
69
68
 
70
- This example says: *fetch the board page, and make sure every last name in
71
- 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
72
 
73
73
  Choosing what to assert is the judgment call — surnames beat full names,
74
74
  "143 schools" beats a bare "143". The short guide:
75
75
  [docs/MARKERS.md](docs/MARKERS.md).
76
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.
93
+
77
94
  **3. Run it:**
78
95
 
79
96
  ```bash
package/dist/cli.js CHANGED
@@ -103,9 +103,9 @@ try {
103
103
  console.log(`kept existing ${f} (never overwritten)`);
104
104
  if (created.length) {
105
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
- ' 2. Edit data/example-facts.json — or point the config at a facts file you already have\n' +
108
- ' 3. Run: npx stilltrue drift\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
109
  'Picking good markers: docs/MARKERS.md in the stilltrue repo');
110
110
  }
111
111
  process.exit(0);
package/dist/init.d.ts CHANGED
@@ -2,5 +2,5 @@ export interface InitResult {
2
2
  created: string[];
3
3
  skipped: string[];
4
4
  }
5
- /** Write starter config + example data file. Never overwrites. */
5
+ /** Write the starter config. Never overwrites an existing one. */
6
6
  export declare function init(dir: string): Promise<InitResult>;
package/dist/init.js CHANGED
@@ -1,4 +1,4 @@
1
- import { mkdir, writeFile } from 'node:fs/promises';
1
+ import { writeFile } from 'node:fs/promises';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  const CONFIG_TEMPLATE = `// stilltrue configuration — one entry per fact-set you want watched.
@@ -10,47 +10,53 @@ export default defineStilltrue({
10
10
  drift: [
11
11
  {
12
12
  // A short id for this check — shows up in output and reports.
13
- name: 'example-board-roster',
13
+ name: 'my-first-check',
14
14
 
15
15
  // WHERE THE TRUTH LIVES: the official page(s) your facts came from.
16
16
  // corpus() fetches each page and strips it down to readable text.
17
- // If one page is unreachable it warns; only if ALL are down does the
18
- // check report an error (which never fails your build).
19
17
  source: corpus([
20
- 'https://example.org/board',
21
- // 'https://example.org/about', // add more pages if facts span several
18
+ 'https://example.org/about',
19
+ // 'https://example.org/board', // add more pages if facts span several
22
20
  ]),
23
21
 
24
- // WHAT YOU BELIEVE: read your saved facts file and pick out the
25
- // "markers" — short, stable strings that must still appear on the
26
- // page(s) above. Here: each member's last name, plus one number.
27
- expect: json('./data/example-facts.json', (data) => [
28
- ...data.members.map((m) => surname(m.name)),
29
- \`\${data.totalSchools} schools\`,
30
- ]),
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
+ ],
31
30
 
32
31
  // HOW TO COMPARE: 'contains-all' = every marker must appear in the
33
- // fetched text (case-insensitive). Any missing marker = rot = the
34
- // build fails and tells you which fact went stale.
32
+ // fetched page text (case-insensitive).
35
33
  compare: 'contains-all',
36
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
+ // ),
37
55
  ],
38
56
  });
39
57
  `;
40
- const DATA_TEMPLATE = `{
41
- "_comment": "Example facts file - replace with your own data, or point the config at a file you already have.",
42
- "members": [
43
- { "name": "Dr. Jane Smith" },
44
- { "name": "Robert Jones Jr." }
45
- ],
46
- "totalSchools": 12
47
- }
48
- `;
49
- /** Write starter config + example data file. Never overwrites. */
58
+ /** Write the starter config. Never overwrites an existing one. */
50
59
  export async function init(dir) {
51
- const created = [];
52
- const skipped = [];
53
- const configPath = join(dir, 'stilltrue.config.mjs');
54
60
  const existingConfig = [
55
61
  'stilltrue.config.mjs',
56
62
  'stilltrue.config.ts',
@@ -59,20 +65,8 @@ export async function init(dir) {
59
65
  'stilltrue.config.cjs',
60
66
  ].find((name) => existsSync(join(dir, name)));
61
67
  if (existingConfig) {
62
- skipped.push(existingConfig);
63
- }
64
- else {
65
- await writeFile(configPath, CONFIG_TEMPLATE);
66
- created.push('stilltrue.config.mjs');
67
- }
68
- const dataPath = join(dir, 'data', 'example-facts.json');
69
- if (existsSync(dataPath)) {
70
- skipped.push('data/example-facts.json');
71
- }
72
- else {
73
- await mkdir(join(dir, 'data'), { recursive: true });
74
- await writeFile(dataPath, DATA_TEMPLATE);
75
- created.push('data/example-facts.json');
68
+ return { created: [], skipped: [existingConfig] };
76
69
  }
77
- return { created, skipped };
70
+ await writeFile(join(dir, 'stilltrue.config.mjs'), CONFIG_TEMPLATE);
71
+ return { created: ['stilltrue.config.mjs'], skipped: [] };
78
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stilltrue",
3
- "version": "0.5.0",
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",