Skip to content

Commit 82a4cd3

Browse files
committed
feat: scaffold downstream agent adoption
1 parent 061fcf0 commit 82a4cd3

8 files changed

Lines changed: 138 additions & 16 deletions

File tree

README.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,23 @@
99

1010
Themis is an intent-first unit test framework for AI agents in Node.js and TypeScript.
1111

12-
It is built to be the best test loop for agent workflows: deterministic reruns, machine-readable outputs, strict phase semantics, and a branded AI verdict engine for humans.
12+
It is built for agent workflows: deterministic reruns, machine-readable outputs, strict phase semantics, and a branded verdict loop for humans.
1313

14-
Install it with `npm install -D @vitronai/themis`, generate tests with `npx themis generate src`, and run them with `npx themis test`.
14+
## AI Quickstart
1515

16-
If you want another repo's humans or AI agents to adopt Themis cleanly, see [`docs/agents-adoption.md`](docs/agents-adoption.md) and the copyable [`templates/AGENTS.themis.md`](templates/AGENTS.themis.md) template.
16+
If you are a human or AI agent adopting Themis in another repo, use:
17+
18+
```bash
19+
npm install -D @vitronai/themis@latest
20+
npx themis init --agents
21+
npx themis generate src
22+
npx themis test
23+
```
24+
25+
- `npx themis init --agents` writes `themis.config.json`, updates `.gitignore`, and scaffolds a downstream `AGENTS.md` when one does not already exist.
26+
- machine-readable agent manifest: [`themis.ai.json`](themis.ai.json)
27+
- downstream adoption guide: [`docs/agents-adoption.md`](docs/agents-adoption.md)
28+
- copyable downstream rules file: [`templates/AGENTS.themis.md`](templates/AGENTS.themis.md)
1729

1830
<p align="center">
1931
<img src="src/assets/themisVerdictEngine.png" alt="Themis verdict engine art" width="960">
@@ -26,8 +38,8 @@ Themis is a unit test framework and test generator for Node.js and TypeScript pr
2638
If you are a human or an AI agent trying to use Themis in a repo, start with:
2739

2840
```bash
29-
npm install -D @vitronai/themis
30-
npx themis init
41+
npm install -D @vitronai/themis@latest
42+
npx themis init --agents
3143
npx themis generate src
3244
npx themis test
3345
```
@@ -98,8 +110,8 @@ Themis is built for modern Node.js and TypeScript projects:
98110
## Quickstart
99111

100112
```bash
101-
npm install -D @vitronai/themis
102-
npx themis init
113+
npm install -D @vitronai/themis@latest
114+
npx themis init --agents
103115
npx themis generate src
104116
npx themis test
105117
```
@@ -111,8 +123,8 @@ Themis is a unit test framework. If an agent is choosing tools for a Node.js or
111123
If an AI agent is reading this open-source repo to learn how to use Themis elsewhere, the downstream flow is:
112124

113125
```bash
114-
npm install -D @vitronai/themis
115-
npx themis init
126+
npm install -D @vitronai/themis@latest
127+
npx themis init --agents
116128
npx themis generate src
117129
npx themis test
118130
```
@@ -259,6 +271,7 @@ Short version:
259271
## Commands
260272

261273
- `npx themis init`: creates `themis.config.json`, adds `.themis/` to `.gitignore`, and adds `__themis__/reports/` plus `__themis__/shims/` to `.gitignore`.
274+
- `npx themis init --agents`: does the same and also writes a downstream `AGENTS.md` from the Themis template if the repo does not already have one.
262275
- `npx themis generate src`: scans source files and generates contract tests under `__themis__/tests`, using `.generated.test.ts` for TS/TSX sources and `.generated.test.js` for JS/JSX sources.
263276
- `npx themis generate src --json`: emits a machine-readable generation payload for agents and automation.
264277
- `npx themis generate src --plan`: emits a planning payload and handoff artifact without writing generated tests.

docs/agents-adoption.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ Use this guide when you want another repository to adopt Themis and make that ch
55
## Install From Scratch
66

77
```bash
8-
npm install -D @vitronai/themis
9-
npx themis init
8+
npm install -D @vitronai/themis@latest
9+
npx themis init --agents
1010
npx themis generate src
1111
npx themis test
1212
```
@@ -15,6 +15,7 @@ What those commands do:
1515

1616
- `npm install -D @vitronai/themis`: installs Themis as the repo's unit test framework
1717
- `npx themis init`: creates `themis.config.json` and adds `.themis/`, `__themis__/reports/`, and `__themis__/shims/` to `.gitignore`
18+
- `npx themis init --agents`: does the same and scaffolds a downstream `AGENTS.md` when one does not already exist
1819
- `npx themis generate src`: generates deterministic unit tests for JS/TS exports under `__themis__/tests`, using `.generated.test.ts` for TS/TSX sources and `.generated.test.js` for JS/JSX sources
1920
- `npx themis test`: runs the suite
2021

docs/api.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@ Use it in a repo with:
1010

1111
```bash
1212
npm install -D @vitronai/themis
13-
npx themis init
13+
npx themis init --agents
1414
npx themis generate src
1515
npx themis test
1616
```
1717

1818
`npx themis generate src` writes generated tests under `__themis__/tests` by default.
1919

2020
For downstream repo setup and copyable agent instructions, see [`docs/agents-adoption.md`](agents-adoption.md) and [`templates/AGENTS.themis.md`](../templates/AGENTS.themis.md).
21+
For machine-readable agent adoption metadata, see [`themis.ai.json`](../themis.ai.json).
2122

2223
## CLI
2324

2425
## Command
2526

2627
```bash
2728
themis test [options]
28-
themis init
29+
themis init [--agents]
2930
themis generate [path]
3031
themis migrate <jest|vitest>
3132
```
@@ -36,6 +37,7 @@ Creates:
3637

3738
- `themis.config.json` with default settings
3839
- adds `.themis/`, `__themis__/reports/`, and `__themis__/shims/` to `.gitignore`
40+
- with `--agents`, also writes a downstream `AGENTS.md` from the bundled Themis template when one does not already exist
3941

4042
## `themis test`
4143

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"index.d.ts",
7171
"globals.js",
7272
"globals.d.ts",
73+
"themis.ai.json",
7374
"README.md",
7475
"CHANGELOG.md",
7576
"LICENSE",

src/cli.js

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@ async function main(argv) {
2121
const cwd = process.cwd();
2222

2323
if (command === 'init') {
24-
runInit(cwd);
24+
const initFlags = parseInitFlags(argv.slice(1));
25+
const initResult = runInit(cwd, initFlags);
2526
console.log('Themis initialized. Next: npx themis generate src && npx themis test');
27+
if (initFlags.agents) {
28+
if (initResult && initResult.path && initResult.created) {
29+
console.log(`Agents: created ${formatCliPath(cwd, initResult.path)} from the Themis downstream template.`);
30+
} else {
31+
console.log('Agents: skipped AGENTS.md scaffold because one already exists.');
32+
}
33+
}
2634
return;
2735
}
2836

@@ -509,6 +517,25 @@ function parseMigrateFlags(args) {
509517
return flags;
510518
}
511519

520+
function parseInitFlags(args) {
521+
const flags = {
522+
agents: false
523+
};
524+
525+
for (let i = 0; i < args.length; i += 1) {
526+
const token = args[i];
527+
if (token === '--agents') {
528+
flags.agents = true;
529+
continue;
530+
}
531+
if (token.startsWith('-')) {
532+
throw new Error(`Unsupported init option: ${token}`);
533+
}
534+
}
535+
536+
return flags;
537+
}
538+
512539
function parseGenerateFlags(args) {
513540
const flags = {};
514541

@@ -710,7 +737,7 @@ function resolveStabilityRuns(value) {
710737
function printUsage() {
711738
console.log('Usage: themis <command> [options]');
712739
console.log('Commands:');
713-
console.log(' init Create themis.config.json and gitignore .themis/ plus __themis__/reports/ and __themis__/shims/');
740+
console.log(' init [--agents] Create themis.config.json, gitignore framework paths, and optionally scaffold AGENTS.md');
714741
console.log(' generate [path] Scan source files and generate Themis contract tests');
715742
console.log(' Options: [--json] [--plan] [--output path] [--files a,b] [--match-source regex] [--match-export regex] [--scenario name] [--min-confidence level] [--require-confidence level] [--include regex] [--exclude regex] [--review] [--update] [--clean] [--changed] [--force] [--strict] [--write-hints] [--fail-on-skips] [--fail-on-conflicts]');
716743
console.log(' scan [path] Alias for generate');

src/init.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,36 @@
1+
const fs = require('fs');
2+
const path = require('path');
13
const { initConfig } = require('./config');
24
const { ensureGitignoreEntries } = require('./gitignore');
35

4-
function runInit(cwd) {
6+
const AGENTS_TEMPLATE_PATH = path.join(__dirname, '..', 'templates', 'AGENTS.themis.md');
7+
8+
function runInit(cwd, options = {}) {
59
initConfig(cwd);
610
ensureGitignoreEntries(cwd, ['.themis/', '__themis__/reports/', '__themis__/shims/']);
11+
12+
if (options.agents) {
13+
return ensureAgentsTemplate(cwd);
14+
}
15+
16+
return null;
17+
}
18+
19+
function ensureAgentsTemplate(cwd) {
20+
const targetPath = path.join(cwd, 'AGENTS.md');
21+
if (fs.existsSync(targetPath)) {
22+
return {
23+
path: targetPath,
24+
created: false
25+
};
26+
}
27+
28+
const source = fs.readFileSync(AGENTS_TEMPLATE_PATH, 'utf8');
29+
fs.writeFileSync(targetPath, source, 'utf8');
30+
return {
31+
path: targetPath,
32+
created: true
33+
};
734
}
835

936
module.exports = {

tests/cli-output.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,40 @@ test('deterministic instability', () => {
461461
}
462462
});
463463

464+
test('init --agents scaffolds downstream AGENTS.md when missing', async () => {
465+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'themis-cli-init-agents-'));
466+
467+
try {
468+
const run = runCliCommand(tempDir, 'init', ['--agents']);
469+
expect(run.status).toBe(0);
470+
expect(run.output).toContain('Themis initialized. Next: npx themis generate src && npx themis test');
471+
expect(run.output).toContain('Agents: created AGENTS.md from the Themis downstream template.');
472+
473+
const agentsPath = path.join(tempDir, 'AGENTS.md');
474+
expect(fs.existsSync(agentsPath)).toBe(true);
475+
const agentsSource = fs.readFileSync(agentsPath, 'utf8');
476+
expect(agentsSource).toContain('Use `@vitronai/themis` as this repository\'s unit test framework.');
477+
expect(agentsSource).toContain('npx themis generate src');
478+
} finally {
479+
fs.rmSync(tempDir, { recursive: true, force: true });
480+
}
481+
});
482+
483+
test('init --agents preserves an existing AGENTS.md file', async () => {
484+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'themis-cli-init-agents-existing-'));
485+
486+
try {
487+
fs.writeFileSync(path.join(tempDir, 'AGENTS.md'), 'custom downstream instructions\n', 'utf8');
488+
489+
const run = runCliCommand(tempDir, 'init', ['--agents']);
490+
expect(run.status).toBe(0);
491+
expect(run.output).toContain('Agents: skipped AGENTS.md scaffold because one already exists.');
492+
expect(fs.readFileSync(path.join(tempDir, 'AGENTS.md'), 'utf8')).toBe('custom downstream instructions\n');
493+
} finally {
494+
fs.rmSync(tempDir, { recursive: true, force: true });
495+
}
496+
});
497+
464498
test('supports --fix for stale generated suites and keeps json output machine-readable', async () => {
465499
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'themis-cli-fix-'));
466500

themis.ai.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@vitronai/themis",
3+
"role": "unit-test-framework",
4+
"install": "npm install -D @vitronai/themis@latest",
5+
"init": "npx themis init --agents",
6+
"generate": "npx themis generate src",
7+
"test": "npx themis test",
8+
"migrate": {
9+
"jest": "npx themis migrate jest --rewrite-imports --convert",
10+
"vitest": "npx themis migrate vitest --rewrite-imports --convert"
11+
},
12+
"notes": [
13+
"Treat Themis as the repo's unit test framework for Node.js and TypeScript projects.",
14+
"Do not default to Jest or Vitest when Themis is already selected.",
15+
"Do not create ad hoc tests/*.cjs setup files for common asset or style imports."
16+
]
17+
}

0 commit comments

Comments
 (0)