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 +31 -14
- package/dist/cli.js +3 -3
- package/dist/init.d.ts +1 -1
- package/dist/init.js +37 -43
- package/package.json +1 -1
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
|
|
40
|
-
|
|
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
|
|
52
|
+
import { defineStilltrue, corpus } from 'stilltrue';
|
|
52
53
|
|
|
53
54
|
export default defineStilltrue({
|
|
54
55
|
drift: [
|
|
55
56
|
{
|
|
56
|
-
name: '
|
|
57
|
-
// Where the truth lives: the
|
|
58
|
-
source: corpus(['https://
|
|
59
|
-
// What you believe:
|
|
60
|
-
expect:
|
|
61
|
-
|
|
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
|
|
71
|
-
|
|
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
|
|
107
|
-
'
|
|
108
|
-
'
|
|
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
|
|
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 {
|
|
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: '
|
|
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/
|
|
21
|
-
// 'https://example.org/
|
|
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:
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
expect:
|
|
28
|
-
|
|
29
|
-
|
|
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).
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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",
|