@crvouga/atlas 0.6.0 → 0.6.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/CHANGELOG.md +7 -1
- package/README.md +21 -5
- package/dist/reports/src/init.js +18 -15
- package/dist/reports/src/package-managers.d.ts +35 -0
- package/dist/reports/src/package-managers.js +23 -0
- package/dist/schema/src/cli-results.d.ts +233 -2
- package/dist/schema/src/cli-results.js +14 -0
- package/dist/server/src/llms.js +17 -8
- package/docs/agents.md +166 -15
- package/docs/deploying.md +5 -1
- package/docs/dogfooding.md +5 -1
- package/docs/integrating.md +10 -2
- package/docs/quickstart.md +81 -0
- package/docs/reference/reports.md +29 -0
- package/docs/reference/schema.md +9 -0
- package/docs/server.md +95 -1
- package/llms-full.txt +367 -22
- package/llms.txt +4 -3
- package/package.json +3 -1
- package/packages/core/README.md +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Changelog: @crvouga/atlas
|
|
2
2
|
|
|
3
|
-
## 0.6.
|
|
3
|
+
## 0.6.1 (2026-10-15)
|
|
4
|
+
|
|
5
|
+
### Fixes and improvements
|
|
6
|
+
|
|
7
|
+
- make consumer integration reproducible (#9) ([bfd4e0c](https://github.com/crvouga/atlas/commit/bfd4e0c1c804d1f11fce3299e609af86c50e4732))
|
|
8
|
+
|
|
9
|
+
## 0.6.0 (2026-10-14)
|
|
4
10
|
|
|
5
11
|
### Features
|
|
6
12
|
|
package/README.md
CHANGED
|
@@ -40,8 +40,19 @@ backends, and tells you in one command whether the product still does what it wa
|
|
|
40
40
|
> [using Atlas from another project](docs/dogfooding.md) and [reporting issues](docs/reporting-issues.md).
|
|
41
41
|
> Releases are automatic: see [releasing](https://github.com/crvouga/atlas/blob/main/docs/releasing.md).
|
|
42
42
|
|
|
43
|
+
## Given this repo link to integrate Atlas?
|
|
44
|
+
|
|
45
|
+
Start with **[the agent integration playbook](docs/agents.md#given-only-this-repository-link)** in your own
|
|
46
|
+
project. It covers choosing an integration, installing with your package manager, finding docs for your installed
|
|
47
|
+
version, running without a server, connecting real suites, and proving completion. **[Verify Atlas locally](docs/quickstart.md)**
|
|
48
|
+
is a copyable walkthrough with expected JSON and exit codes; it needs no credentials or existing test suite.
|
|
49
|
+
The machine-readable index is [llms.txt](llms.txt), and [llms-full.txt](llms-full.txt) contains the guides in one file.
|
|
50
|
+
|
|
43
51
|
## Run this repository's example
|
|
44
52
|
|
|
53
|
+
For contributors working in an Atlas checkout with dependencies installed (`bun install --frozen-lockfile`).
|
|
54
|
+
Consumer projects install the npm package instead; they do not need to clone or build this repository.
|
|
55
|
+
|
|
45
56
|
Use two terminals:
|
|
46
57
|
|
|
47
58
|
```sh
|
|
@@ -64,20 +75,21 @@ Atlas is built to be integrated and operated by agents, with nothing to click an
|
|
|
64
75
|
- **`atlas init`** detects a project's test runners, specs and CI and prints a plan (`--format json`) of the exact commands to
|
|
65
76
|
run each suite and send its results; `--write` creates the CI workflow and an `AGENTS.md` section. Running it twice
|
|
66
77
|
changes nothing.
|
|
67
|
-
- **
|
|
68
|
-
|
|
78
|
+
- **Init and report commands print JSON on request** (`--format json`) with published JSON Schemas and consistent
|
|
79
|
+
exit codes (`0` worked, `1` the answer is no, `2` the command was wrong). Reapplying init or publishing identical evidence is safe.
|
|
69
80
|
- **The server describes itself:** `GET /api/openapi.json` (OpenAPI 3.1) and `GET /llms.txt` (a guide for an agent given only
|
|
70
81
|
the address) need no token; errors are RFC 9457 problem documents that say what to do.
|
|
71
82
|
- **Documentation is generated from the code beside it:** `llms.txt` / `llms-full.txt` (also in the package), the TypeScript
|
|
72
83
|
reference in [`docs/reference/`](docs/reference/server.md), the CLI and HTTP references, and the settings tables; tests fail when
|
|
73
84
|
any of it drifts.
|
|
74
85
|
|
|
75
|
-
Start with **[Atlas for AI agents](docs/agents.md)**.
|
|
86
|
+
Start with **[Atlas for AI agents](docs/agents.md)**. A [copyable integration brief](docs/agents.md#an-integration-brief-you-can-give-an-agent)
|
|
87
|
+
lets a user hand an agent this repository URL and a concrete definition of done.
|
|
76
88
|
|
|
77
89
|
## Integrate in five minutes
|
|
78
90
|
|
|
79
91
|
```sh
|
|
80
|
-
bun add -d @crvouga/atlas # or npm install -D / pnpm add -D
|
|
92
|
+
bun add -d @crvouga/atlas # or npm install -D / pnpm add -D
|
|
81
93
|
|
|
82
94
|
# 1. Each suite publishes a layer of what it ran: a JUnit or CTRF file, an Atlas run, or a checks file.
|
|
83
95
|
bunx atlas report publish --store atlas-reports --producer web-e2e --junit out.xml --specs ./specs --environment desktop-web
|
|
@@ -105,7 +117,11 @@ bunx atlas report status --store all --specs ./specs --require-environments desk
|
|
|
105
117
|
## One package
|
|
106
118
|
|
|
107
119
|
Atlas is a single npm package. Each part is its own import path, so a bundler keeps only what you
|
|
108
|
-
import.
|
|
120
|
+
import. Use your project's package manager:
|
|
121
|
+
|
|
122
|
+
<!-- generated:runtime-requirements -->
|
|
123
|
+
Supported runtimes (from `package.json`): **node `>=22.18.0`** or **bun `>=1.2.0`**. Node consumers do not need Bun unless they use the Bun WebView driver.
|
|
124
|
+
<!-- /generated:runtime-requirements -->
|
|
109
125
|
|
|
110
126
|
```sh
|
|
111
127
|
bun add -d @crvouga/atlas
|
package/dist/reports/src/init.js
CHANGED
|
@@ -19,6 +19,7 @@ import { InitPlanSchema } from '../../schema/src/cli-results.js';
|
|
|
19
19
|
import { collectSpecFiles, specKindOf } from './directory-outline.js';
|
|
20
20
|
import { httpStore } from './http-store.js';
|
|
21
21
|
import { serverToken } from './store-url.js';
|
|
22
|
+
import { PACKAGE_MANAGERS } from './package-managers.js';
|
|
22
23
|
const BEGIN = '<!-- atlas:begin -->';
|
|
23
24
|
const END = '<!-- atlas:end -->';
|
|
24
25
|
function defaultGit(args, cwd) {
|
|
@@ -50,9 +51,6 @@ function packageManagerOf(directory, pkg) {
|
|
|
50
51
|
const declared = typeof pkg?.packageManager === 'string' ? pkg.packageManager.split('@')[0] : null;
|
|
51
52
|
return declared === 'bun' || declared === 'pnpm' || declared === 'yarn' || declared === 'npm' ? declared : pkg ? 'npm' : null;
|
|
52
53
|
}
|
|
53
|
-
/** How the package manager runs a package's binary without installing it: `bunx`, `pnpm dlx`, `yarn dlx`, `npx`. */
|
|
54
|
-
const EXEC = { bun: 'bunx', pnpm: 'pnpm dlx', yarn: 'yarn dlx', npm: 'npx' };
|
|
55
|
-
const INSTALL_DEV = { bun: 'bun add -d', pnpm: 'pnpm add -D', yarn: 'yarn add -D', npm: 'npm install -D' };
|
|
56
54
|
/** The spec files of a project by kind, and the folder to pass as `--specs`. */
|
|
57
55
|
async function specsOf(directory) {
|
|
58
56
|
const candidates = ['specs', 'spec', 'features', 'docs/specs', 'test/features', 'tests/features'];
|
|
@@ -118,7 +116,7 @@ function readdirSafe(directory) {
|
|
|
118
116
|
/** The suite for a detected runner: how to run it so it writes a JUnit file (or the run manifest), and how to publish it. */
|
|
119
117
|
function suiteOf(detected, context) {
|
|
120
118
|
const specs = context.specs ? ` --specs ${context.specs}` : '';
|
|
121
|
-
const publishCommand = (producer, input, environment) => `${context.exec}
|
|
119
|
+
const publishCommand = (producer, input, environment) => `${context.exec} atlas report publish --producer ${producer} ${input}${specs}${environment ? ` --environment ${environment}` : ''}`;
|
|
122
120
|
const make = (id, output, run, environment, prerequisites = [], format = 'junit', stream = null) => ({
|
|
123
121
|
id, runner: detected.runner, evidence: detected.evidence, run, output, format, environment,
|
|
124
122
|
publish: publishCommand(id, format === 'manifest' ? `--manifest ${output} --upload-run` : `--${format} ${output}`, environment), stream, prerequisites
|
|
@@ -136,7 +134,7 @@ function suiteOf(detected, context) {
|
|
|
136
134
|
case 'bun-test': return make('bun-test', out('bun'), `bun test --reporter=junit --reporter-outfile=${out('bun')}`, null);
|
|
137
135
|
case 'pytest': return make('pytest', out('pytest'), `pytest --junitxml=${out('pytest')}`, null);
|
|
138
136
|
case 'go-test': return make('go-test', out('go'), `go test -v ./... 2>&1 | go-junit-report > ${out('go')}`, null, ['go install github.com/jstemmer/go-junit-report/v2@latest']);
|
|
139
|
-
default: return make('statecharts', 'atlas-runs/latest/manifest.json', `${context.exec}
|
|
137
|
+
default: return make('statecharts', 'atlas-runs/latest/manifest.json', `${context.exec} atlas run --mode fast`, null, [], 'manifest');
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
140
|
/** A suite that does not set its own exit-code handling must still publish: a failure is evidence. */
|
|
@@ -144,7 +142,7 @@ function githubWorkflow(suites, context) {
|
|
|
144
142
|
const setup = context.manager === 'bun'
|
|
145
143
|
? ' - uses: oven-sh/setup-bun@v2\n - run: bun install --frozen-lockfile\n'
|
|
146
144
|
: ` - uses: actions/setup-node@v4\n with: { node-version: 22 }\n - run: ${context.manager === 'pnpm' ? 'corepack enable && pnpm install --frozen-lockfile' : context.manager === 'yarn' ? 'corepack enable && yarn install --immutable' : 'npm ci'}\n`;
|
|
147
|
-
const exec =
|
|
145
|
+
const exec = PACKAGE_MANAGERS[context.manager ?? 'npm'].exec;
|
|
148
146
|
const jobs = suites.map(suite => ` ${suite.id.replace(/[^a-z0-9]+/g, '-')}:
|
|
149
147
|
runs-on: ubuntu-latest
|
|
150
148
|
steps:
|
|
@@ -157,13 +155,15 @@ ${setup} # A failing suite must still publish: a failure is evidence. The j
|
|
|
157
155
|
`).join('\n');
|
|
158
156
|
return `# Sends every suite's results to the project's Atlas server and gates on the combined report.
|
|
159
157
|
# Generated by \`atlas init\`; safe to edit. Needs the repository variable ATLAS_URL (https://atlas.example.com) and the
|
|
160
|
-
#
|
|
158
|
+
# variable ATLAS_PROJECT (a stable project slug), and secrets ATLAS_PUBLISH_TOKEN (a \`publish\` token) and
|
|
159
|
+
# ATLAS_READ_TOKEN (a \`read\` token) of that server.
|
|
161
160
|
name: atlas
|
|
162
161
|
on: [push, pull_request]
|
|
163
162
|
permissions:
|
|
164
163
|
contents: read
|
|
165
164
|
env:
|
|
166
165
|
ATLAS_STORE: \${{ vars.ATLAS_URL }}
|
|
166
|
+
ATLAS_PROJECT: \${{ vars.ATLAS_PROJECT }}
|
|
167
167
|
ATLAS_TOKEN: \${{ secrets.ATLAS_PUBLISH_TOKEN }}
|
|
168
168
|
jobs:
|
|
169
169
|
${jobs}
|
|
@@ -175,26 +175,27 @@ ${jobs}
|
|
|
175
175
|
ATLAS_TOKEN: \${{ secrets.ATLAS_READ_TOKEN }}
|
|
176
176
|
steps:
|
|
177
177
|
- uses: actions/checkout@v4
|
|
178
|
-
|
|
178
|
+
${setup.trimEnd()}
|
|
179
179
|
# This branch laid over ${context.defaultBranch}: what this run did not re-run is inherited from ${context.defaultBranch}'s latest
|
|
180
180
|
# results, and a laptop's work in progress or another branch's results cannot turn this red.
|
|
181
|
-
- run:
|
|
181
|
+
- run: ${exec} atlas report status --branch "\${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}" --branch ${context.defaultBranch} --ignore-modified${context.specs ? ' --require-all-specs' : ''}
|
|
182
182
|
`;
|
|
183
183
|
}
|
|
184
184
|
/** The `AGENTS.md` section: what Atlas is in this project and the commands to use, replaced between its markers. */
|
|
185
|
-
function agentsSection(plan) {
|
|
185
|
+
function agentsSection(plan, exec) {
|
|
186
186
|
const url = plan.server.url ?? '<the project\'s Atlas server>';
|
|
187
187
|
const lines = [
|
|
188
188
|
BEGIN,
|
|
189
189
|
'## Atlas: test evidence for this project',
|
|
190
190
|
'',
|
|
191
191
|
`This project sends the results of its test suites to an [Atlas](https://github.com/crvouga/atlas) server (${url}), which shows everyone one combined, live report. A check is identified by a stable id; the newest completed result of each check wins; unknown is never a pass. Set \`ATLAS_STORE=${url}\` and \`ATLAS_TOKEN\` (a \`publish\` token for sending, a \`read\` token for asking).`,
|
|
192
|
+
'Set `ATLAS_PROJECT` to the same stable project slug locally and in CI; this selects the project on a shared node. Use the installed `@crvouga/atlas` and the project\'s lockfile. Integration guide: https://github.com/crvouga/atlas/blob/main/docs/agents.md (version-matched copy: `node_modules/@crvouga/atlas/docs/agents.md`).',
|
|
192
193
|
'',
|
|
193
194
|
'- **Machine-readable API:** `' + `${url}/api/openapi.json` + '` (OpenAPI 3.1). **Guide for agents:** `' + `${url}/llms.txt` + '`.',
|
|
194
195
|
'- **After you change behaviour,** run the suite, then send its results (publish even when it fails: a failure is evidence).',
|
|
195
196
|
...plan.suites.flatMap(suite => ['', ` - **${suite.id}**: \`${suite.run}\``, ` then \`${suite.publish}\`` + (suite.stream ? ` (or stream: \`${suite.stream}\`)` : '')]),
|
|
196
197
|
'',
|
|
197
|
-
`- **Is it passing?**
|
|
198
|
+
`- **Is it passing?** \`${exec} atlas report status --branch ${plan.git.defaultBranch} --ignore-modified --format json\` (exit 0 passes, 1 does not, 2 means the command was wrong). The failing checks are in \`failing\`.`,
|
|
198
199
|
plan.specs.directory ? `- **Specs** live in \`${plan.specs.directory}/\` (${Object.entries(plan.specs.kinds).map(([kind, count]) => `${count} ${kind}`).join(', ')}). Results are laid over them by path.` : '- No spec files were found. Pass `--spec <path>` or `--specs <dir>` to `publish` to link checks to specs.',
|
|
199
200
|
'- Keep each check\'s `id` stable across runs: that is what lets a new result replace an old one.',
|
|
200
201
|
'- Evidence (screenshots, videos, traces, logs) rides on checks: https://github.com/crvouga/atlas/blob/main/docs/extending.md',
|
|
@@ -228,13 +229,14 @@ export async function planInit(options) {
|
|
|
228
229
|
const env = options.env ?? {};
|
|
229
230
|
const git = options.git ?? defaultGit;
|
|
230
231
|
const pkg = readJson(path.join(directory, 'package.json'));
|
|
232
|
+
const dependencies = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
231
233
|
const manager = packageManagerOf(directory, pkg);
|
|
232
234
|
const specs = await specsOf(directory);
|
|
233
235
|
const configured = env.ATLAS_STORE?.split(/[\n,]+/).map(item => item.trim()).find(item => /^https?:\/\//i.test(item));
|
|
234
236
|
const serverUrl = (options.server ?? configured ?? null)?.replace(/\/+$/, '') ?? null;
|
|
235
237
|
const warnings = [];
|
|
236
|
-
const exec =
|
|
237
|
-
const context = { exec, install:
|
|
238
|
+
const { exec, installDev } = PACKAGE_MANAGERS[manager ?? 'npm'];
|
|
239
|
+
const context = { exec, install: installDev, specs: specs.directory, server: serverUrl };
|
|
238
240
|
const suites = runnersOf(directory, pkg).map(found => suiteOf(found, context));
|
|
239
241
|
if (!suites.length)
|
|
240
242
|
warnings.push('No test runner was recognised (looked for vitest, jest, playwright, cypress, cucumber, mocha, codeceptjs, node --test, bun test, pytest, go test and Atlas statecharts). Add one suite by hand: run it so it writes a JUnit XML or CTRF file, then `atlas report publish --producer <name> --junit <file>`.');
|
|
@@ -252,7 +254,7 @@ export async function planInit(options) {
|
|
|
252
254
|
warnings.push(`${ci} detected: no workflow is generated for it. In its jobs run each suite, then its publish command (with ATLAS_STORE and ATLAS_TOKEN set from secrets), then the gate command from AGENTS.md.`);
|
|
253
255
|
const base = { suites, specs, server: { url: serverUrl, tokenEnv: 'ATLAS_TOKEN' }, git: { branch, defaultBranch } };
|
|
254
256
|
const wanted = [
|
|
255
|
-
{ path: 'AGENTS.md', purpose: 'Tells every agent working in this repository how to send and read test evidence.', next: current => withSection(current, agentsSection(base)) }
|
|
257
|
+
{ path: 'AGENTS.md', purpose: 'Tells every agent working in this repository how to send and read test evidence.', next: current => withSection(current, agentsSection(base, exec)) }
|
|
256
258
|
];
|
|
257
259
|
if (ci === 'github-actions' && suites.length)
|
|
258
260
|
wanted.push({ path: '.github/workflows/atlas.yml', purpose: 'Runs each suite in CI, sends its results to the server even when it fails, and gates on the combined report.', next: () => githubWorkflow(suites, { manager, defaultBranch, specs: specs.directory }) });
|
|
@@ -291,10 +293,11 @@ export async function planInit(options) {
|
|
|
291
293
|
else if (options.verify)
|
|
292
294
|
warnings.push('--verify needs a server: pass --server or set ATLAS_STORE.');
|
|
293
295
|
const nextSteps = [
|
|
296
|
+
...(dependencies['@crvouga/atlas'] ? [] : [`Install Atlas in this project before running the planned commands: ${context.install} @crvouga/atlas. Commit the manifest and lockfile so local runs and CI use the same version.`]),
|
|
294
297
|
...(serverUrl ? [] : ['Decide which server to send results to (run one with `atlas serve`), then run this again with --server <url>.']),
|
|
295
298
|
...(options.write ? [] : ['Review the plan, then run again with --write to create the files in `files`.']),
|
|
296
299
|
...suites.flatMap(suite => suite.prerequisites.map(command => `Install what ${suite.id} needs: ${command}`)),
|
|
297
|
-
...(serverUrl ? [`Set ATLAS_STORE=${serverUrl} and ATLAS_TOKEN (a publish token) in CI and on each machine that should send results.`, 'Run `atlas report doctor` to prove the server can be written to and read.'] : []),
|
|
300
|
+
...(serverUrl ? [`Set ATLAS_STORE=${serverUrl}, ATLAS_PROJECT (the same stable project slug) and ATLAS_TOKEN (a publish token) in CI and on each machine that should send results.`, 'Run `atlas report doctor` to prove the server can be written to and read.'] : []),
|
|
298
301
|
...suites.map(suite => `Prove ${suite.id}: run \`${suite.run}\`, then \`${suite.publish}\`, then open ${serverUrl ?? 'the server'} and find its results.`)
|
|
299
302
|
];
|
|
300
303
|
const ok = verification ? verification.reachable && verification.canPublish && verification.error === null : true;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Consumer package-manager commands
|
|
3
|
+
*
|
|
4
|
+
* One source for commands emitted by init, the running server's agent guide, and generated documentation.
|
|
5
|
+
* Execute the installed CLI so a consumer keeps the versions selected by its own manifest and lockfile.
|
|
6
|
+
*/
|
|
7
|
+
/** Supported consumer package managers, their development install command and installed-binary executor. */
|
|
8
|
+
export declare const PACKAGE_MANAGERS: {
|
|
9
|
+
readonly bun: {
|
|
10
|
+
readonly label: "Bun";
|
|
11
|
+
readonly installDev: "bun add -d";
|
|
12
|
+
readonly exec: "bunx";
|
|
13
|
+
};
|
|
14
|
+
readonly npm: {
|
|
15
|
+
readonly label: "npm";
|
|
16
|
+
readonly installDev: "npm install -D";
|
|
17
|
+
readonly exec: "npm exec --";
|
|
18
|
+
};
|
|
19
|
+
readonly pnpm: {
|
|
20
|
+
readonly label: "pnpm";
|
|
21
|
+
readonly installDev: "pnpm add -D";
|
|
22
|
+
readonly exec: "pnpm exec";
|
|
23
|
+
};
|
|
24
|
+
readonly yarn: {
|
|
25
|
+
readonly label: "Yarn";
|
|
26
|
+
readonly installDev: "yarn add -D";
|
|
27
|
+
readonly exec: "yarn exec";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Markdown installation table for consumer and server agent documentation.
|
|
32
|
+
* @example
|
|
33
|
+
* const markdown = packageManagerTable(); // the same commands `atlas init` emits
|
|
34
|
+
*/
|
|
35
|
+
export declare function packageManagerTable(): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Consumer package-manager commands
|
|
3
|
+
*
|
|
4
|
+
* One source for commands emitted by init, the running server's agent guide, and generated documentation.
|
|
5
|
+
* Execute the installed CLI so a consumer keeps the versions selected by its own manifest and lockfile.
|
|
6
|
+
*/
|
|
7
|
+
/** Supported consumer package managers, their development install command and installed-binary executor. */
|
|
8
|
+
export const PACKAGE_MANAGERS = {
|
|
9
|
+
bun: { label: 'Bun', installDev: 'bun add -d', exec: 'bunx' },
|
|
10
|
+
npm: { label: 'npm', installDev: 'npm install -D', exec: 'npm exec --' },
|
|
11
|
+
pnpm: { label: 'pnpm', installDev: 'pnpm add -D', exec: 'pnpm exec' },
|
|
12
|
+
yarn: { label: 'Yarn', installDev: 'yarn add -D', exec: 'yarn exec' }
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Markdown installation table for consumer and server agent documentation.
|
|
16
|
+
* @example
|
|
17
|
+
* const markdown = packageManagerTable(); // the same commands `atlas init` emits
|
|
18
|
+
*/
|
|
19
|
+
export function packageManagerTable() {
|
|
20
|
+
return ['| Package manager | Install | Run the installed CLI |', '| --- | --- | --- |',
|
|
21
|
+
...Object.values(PACKAGE_MANAGERS).map(manager => `| ${manager.label} | \`${manager.installDev} @crvouga/atlas\` | \`${manager.exec} atlas\` |`)
|
|
22
|
+
].join('\n');
|
|
23
|
+
}
|
|
@@ -147,10 +147,10 @@ export declare const InitPlanSchema: z.ZodObject<{
|
|
|
147
147
|
project: z.ZodObject<{
|
|
148
148
|
name: z.ZodNullable<z.ZodString>;
|
|
149
149
|
packageManager: z.ZodNullable<z.ZodEnum<{
|
|
150
|
-
|
|
150
|
+
npm: "npm";
|
|
151
151
|
pnpm: "pnpm";
|
|
152
|
+
bun: "bun";
|
|
152
153
|
yarn: "yarn";
|
|
153
|
-
npm: "npm";
|
|
154
154
|
}>>;
|
|
155
155
|
languages: z.ZodArray<z.ZodString>;
|
|
156
156
|
}, z.core.$loose>;
|
|
@@ -208,3 +208,234 @@ export declare const InitPlanSchema: z.ZodObject<{
|
|
|
208
208
|
nextSteps: z.ZodArray<z.ZodString>;
|
|
209
209
|
warnings: z.ZodArray<z.ZodString>;
|
|
210
210
|
}, z.core.$loose>;
|
|
211
|
+
/**
|
|
212
|
+
* JSON command contracts, shared by schema publication, agent documentation and executable command checks.
|
|
213
|
+
* A new result needs an entry here and a runtime scenario in the documentation contract tests.
|
|
214
|
+
*/
|
|
215
|
+
export declare const CLI_RESULTS: {
|
|
216
|
+
readonly init: {
|
|
217
|
+
readonly command: "atlas init";
|
|
218
|
+
readonly schemaName: "init-plan";
|
|
219
|
+
readonly schema: z.ZodObject<{
|
|
220
|
+
ok: z.ZodBoolean;
|
|
221
|
+
directory: z.ZodString;
|
|
222
|
+
project: z.ZodObject<{
|
|
223
|
+
name: z.ZodNullable<z.ZodString>;
|
|
224
|
+
packageManager: z.ZodNullable<z.ZodEnum<{
|
|
225
|
+
npm: "npm";
|
|
226
|
+
pnpm: "pnpm";
|
|
227
|
+
bun: "bun";
|
|
228
|
+
yarn: "yarn";
|
|
229
|
+
}>>;
|
|
230
|
+
languages: z.ZodArray<z.ZodString>;
|
|
231
|
+
}, z.core.$loose>;
|
|
232
|
+
git: z.ZodObject<{
|
|
233
|
+
branch: z.ZodNullable<z.ZodString>;
|
|
234
|
+
defaultBranch: z.ZodString;
|
|
235
|
+
}, z.core.$loose>;
|
|
236
|
+
ci: z.ZodEnum<{
|
|
237
|
+
none: "none";
|
|
238
|
+
"github-actions": "github-actions";
|
|
239
|
+
"gitlab-ci": "gitlab-ci";
|
|
240
|
+
circleci: "circleci";
|
|
241
|
+
}>;
|
|
242
|
+
server: z.ZodObject<{
|
|
243
|
+
url: z.ZodNullable<z.ZodString>;
|
|
244
|
+
tokenEnv: z.ZodLiteral<"ATLAS_TOKEN">;
|
|
245
|
+
}, z.core.$loose>;
|
|
246
|
+
specs: z.ZodObject<{
|
|
247
|
+
directory: z.ZodNullable<z.ZodString>;
|
|
248
|
+
kinds: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
249
|
+
}, z.core.$loose>;
|
|
250
|
+
suites: z.ZodArray<z.ZodObject<{
|
|
251
|
+
id: z.ZodString;
|
|
252
|
+
runner: z.ZodString;
|
|
253
|
+
evidence: z.ZodString;
|
|
254
|
+
run: z.ZodString;
|
|
255
|
+
output: z.ZodString;
|
|
256
|
+
format: z.ZodEnum<{
|
|
257
|
+
junit: "junit";
|
|
258
|
+
ctrf: "ctrf";
|
|
259
|
+
manifest: "manifest";
|
|
260
|
+
}>;
|
|
261
|
+
environment: z.ZodNullable<z.ZodString>;
|
|
262
|
+
publish: z.ZodString;
|
|
263
|
+
stream: z.ZodNullable<z.ZodString>;
|
|
264
|
+
prerequisites: z.ZodArray<z.ZodString>;
|
|
265
|
+
}, z.core.$loose>>;
|
|
266
|
+
files: z.ZodArray<z.ZodObject<{
|
|
267
|
+
path: z.ZodString;
|
|
268
|
+
action: z.ZodEnum<{
|
|
269
|
+
create: "create";
|
|
270
|
+
update: "update";
|
|
271
|
+
unchanged: "unchanged";
|
|
272
|
+
}>;
|
|
273
|
+
purpose: z.ZodString;
|
|
274
|
+
content: z.ZodString;
|
|
275
|
+
}, z.core.$loose>>;
|
|
276
|
+
written: z.ZodArray<z.ZodString>;
|
|
277
|
+
verification: z.ZodNullable<z.ZodObject<{
|
|
278
|
+
reachable: z.ZodBoolean;
|
|
279
|
+
role: z.ZodNullable<z.ZodString>;
|
|
280
|
+
canPublish: z.ZodBoolean;
|
|
281
|
+
error: z.ZodNullable<z.ZodString>;
|
|
282
|
+
}, z.core.$loose>>;
|
|
283
|
+
nextSteps: z.ZodArray<z.ZodString>;
|
|
284
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
285
|
+
}, z.core.$loose>;
|
|
286
|
+
};
|
|
287
|
+
readonly publish: {
|
|
288
|
+
readonly command: "atlas report publish";
|
|
289
|
+
readonly schemaName: "publish-result";
|
|
290
|
+
readonly schema: z.ZodObject<{
|
|
291
|
+
ok: z.ZodLiteral<true>;
|
|
292
|
+
store: z.ZodString;
|
|
293
|
+
layer: z.ZodObject<{
|
|
294
|
+
id: z.ZodString;
|
|
295
|
+
producer: z.ZodString;
|
|
296
|
+
environment: z.ZodNullable<z.ZodString>;
|
|
297
|
+
checks: z.ZodNumber;
|
|
298
|
+
passed: z.ZodNumber;
|
|
299
|
+
failed: z.ZodNumber;
|
|
300
|
+
notRun: z.ZodNumber;
|
|
301
|
+
linkedToSpec: z.ZodNumber;
|
|
302
|
+
freshness: z.ZodOptional<z.ZodEnum<{
|
|
303
|
+
unknown: "unknown";
|
|
304
|
+
modified: "modified";
|
|
305
|
+
}>>;
|
|
306
|
+
attachmentFiles: z.ZodNumber;
|
|
307
|
+
attachmentBytes: z.ZodNumber;
|
|
308
|
+
}, z.core.$loose>;
|
|
309
|
+
specSet: z.ZodOptional<z.ZodObject<{
|
|
310
|
+
id: z.ZodString;
|
|
311
|
+
files: z.ZodNumber;
|
|
312
|
+
branch: z.ZodNullable<z.ZodString>;
|
|
313
|
+
}, z.core.$loose>>;
|
|
314
|
+
run: z.ZodOptional<z.ZodObject<{
|
|
315
|
+
id: z.ZodString;
|
|
316
|
+
files: z.ZodNumber;
|
|
317
|
+
bytes: z.ZodNumber;
|
|
318
|
+
alreadyPublished: z.ZodBoolean;
|
|
319
|
+
}, z.core.$loose>>;
|
|
320
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
321
|
+
}, z.core.$loose>;
|
|
322
|
+
};
|
|
323
|
+
readonly stream: {
|
|
324
|
+
readonly command: "atlas report stream";
|
|
325
|
+
readonly schemaName: "stream-result";
|
|
326
|
+
readonly schema: z.ZodObject<{
|
|
327
|
+
ok: z.ZodBoolean;
|
|
328
|
+
producer: z.ZodString;
|
|
329
|
+
environment: z.ZodNullable<z.ZodString>;
|
|
330
|
+
layers: z.ZodNumber;
|
|
331
|
+
checks: z.ZodNumber;
|
|
332
|
+
lost: z.ZodNumber;
|
|
333
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
334
|
+
}, z.core.$loose>;
|
|
335
|
+
};
|
|
336
|
+
readonly merge: {
|
|
337
|
+
readonly command: "atlas report merge";
|
|
338
|
+
readonly schemaName: "merge-result";
|
|
339
|
+
readonly schema: z.ZodObject<{
|
|
340
|
+
ok: z.ZodLiteral<true>;
|
|
341
|
+
into: z.ZodString;
|
|
342
|
+
copied: z.ZodNumber;
|
|
343
|
+
skipped: z.ZodNumber;
|
|
344
|
+
ignored: z.ZodNumber;
|
|
345
|
+
}, z.core.$loose>;
|
|
346
|
+
};
|
|
347
|
+
readonly doctor: {
|
|
348
|
+
readonly command: "atlas report doctor";
|
|
349
|
+
readonly schemaName: "doctor-result";
|
|
350
|
+
readonly schema: z.ZodObject<{
|
|
351
|
+
ok: z.ZodBoolean;
|
|
352
|
+
stores: z.ZodArray<z.ZodObject<{
|
|
353
|
+
id: z.ZodString;
|
|
354
|
+
checks: z.ZodArray<z.ZodObject<{
|
|
355
|
+
name: z.ZodString;
|
|
356
|
+
ok: z.ZodBoolean;
|
|
357
|
+
detail: z.ZodString;
|
|
358
|
+
fix: z.ZodOptional<z.ZodString>;
|
|
359
|
+
}, z.core.$loose>>;
|
|
360
|
+
}, z.core.$loose>>;
|
|
361
|
+
}, z.core.$loose>;
|
|
362
|
+
};
|
|
363
|
+
readonly status: {
|
|
364
|
+
readonly command: "atlas report status";
|
|
365
|
+
readonly schemaName: "report-status";
|
|
366
|
+
readonly schema: z.ZodObject<{
|
|
367
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
368
|
+
ok: z.ZodBoolean;
|
|
369
|
+
reasons: z.ZodArray<z.ZodString>;
|
|
370
|
+
checks: z.ZodObject<{
|
|
371
|
+
passed: z.ZodNumber;
|
|
372
|
+
failed: z.ZodNumber;
|
|
373
|
+
running: z.ZodNumber;
|
|
374
|
+
'not-run': z.ZodNumber;
|
|
375
|
+
total: z.ZodNumber;
|
|
376
|
+
}, z.core.$strip>;
|
|
377
|
+
byEnvironment: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
378
|
+
passed: z.ZodNumber;
|
|
379
|
+
failed: z.ZodNumber;
|
|
380
|
+
running: z.ZodNumber;
|
|
381
|
+
'not-run': z.ZodNumber;
|
|
382
|
+
total: z.ZodNumber;
|
|
383
|
+
}, z.core.$strip>>;
|
|
384
|
+
layers: z.ZodNumber;
|
|
385
|
+
backends: z.ZodArray<z.ZodObject<{
|
|
386
|
+
id: z.ZodString;
|
|
387
|
+
label: z.ZodString;
|
|
388
|
+
available: z.ZodBoolean;
|
|
389
|
+
}, z.core.$strip>>;
|
|
390
|
+
notices: z.ZodArray<z.ZodString>;
|
|
391
|
+
specsWithoutEvidence: z.ZodArray<z.ZodString>;
|
|
392
|
+
specs: z.ZodArray<z.ZodObject<{
|
|
393
|
+
path: z.ZodNullable<z.ZodString>;
|
|
394
|
+
title: z.ZodString;
|
|
395
|
+
kind: z.ZodString;
|
|
396
|
+
checks: z.ZodObject<{
|
|
397
|
+
passed: z.ZodNumber;
|
|
398
|
+
failed: z.ZodNumber;
|
|
399
|
+
running: z.ZodNumber;
|
|
400
|
+
'not-run': z.ZodNumber;
|
|
401
|
+
total: z.ZodNumber;
|
|
402
|
+
}, z.core.$strip>;
|
|
403
|
+
byEnvironment: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
404
|
+
passed: z.ZodNumber;
|
|
405
|
+
failed: z.ZodNumber;
|
|
406
|
+
running: z.ZodNumber;
|
|
407
|
+
'not-run': z.ZodNumber;
|
|
408
|
+
total: z.ZodNumber;
|
|
409
|
+
}, z.core.$strip>>;
|
|
410
|
+
}, z.core.$loose>>;
|
|
411
|
+
failing: z.ZodArray<z.ZodObject<{
|
|
412
|
+
spec: z.ZodNullable<z.ZodString>;
|
|
413
|
+
check: z.ZodString;
|
|
414
|
+
title: z.ZodString;
|
|
415
|
+
status: z.ZodEnum<{
|
|
416
|
+
failed: "failed";
|
|
417
|
+
running: "running";
|
|
418
|
+
}>;
|
|
419
|
+
environment: z.ZodNullable<z.ZodString>;
|
|
420
|
+
producer: z.ZodString;
|
|
421
|
+
branch: z.ZodNullable<z.ZodString>;
|
|
422
|
+
sha: z.ZodNullable<z.ZodString>;
|
|
423
|
+
layer: z.ZodString;
|
|
424
|
+
backend: z.ZodString;
|
|
425
|
+
runUrl: z.ZodNullable<z.ZodString>;
|
|
426
|
+
detail: z.ZodString;
|
|
427
|
+
}, z.core.$loose>>;
|
|
428
|
+
}, z.core.$loose>;
|
|
429
|
+
};
|
|
430
|
+
readonly error: {
|
|
431
|
+
readonly command: "a command that was wrong (standard error)";
|
|
432
|
+
readonly schemaName: "command-error";
|
|
433
|
+
readonly schema: z.ZodObject<{
|
|
434
|
+
ok: z.ZodLiteral<false>;
|
|
435
|
+
error: z.ZodObject<{
|
|
436
|
+
message: z.ZodString;
|
|
437
|
+
code: z.ZodLiteral<"usage">;
|
|
438
|
+
}, z.core.$loose>;
|
|
439
|
+
}, z.core.$loose>;
|
|
440
|
+
};
|
|
441
|
+
};
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @see docs/agents.md
|
|
14
14
|
*/
|
|
15
15
|
import { z } from 'zod/v4';
|
|
16
|
+
import { ReportStatusSchema } from './layer.js';
|
|
16
17
|
/** What was written for one layer by `atlas report publish`. */
|
|
17
18
|
export const PublishedLayerSchema = z.looseObject({
|
|
18
19
|
id: z.string().describe('The layer id: 64 hex digits, the SHA-256 of its content. Publishing the same input again gives the same id'),
|
|
@@ -122,3 +123,16 @@ export const InitPlanSchema = z.looseObject({
|
|
|
122
123
|
nextSteps: z.array(z.string()).describe('What to do next, in order, as plain instructions an agent can follow'),
|
|
123
124
|
warnings: z.array(z.string())
|
|
124
125
|
});
|
|
126
|
+
/**
|
|
127
|
+
* JSON command contracts, shared by schema publication, agent documentation and executable command checks.
|
|
128
|
+
* A new result needs an entry here and a runtime scenario in the documentation contract tests.
|
|
129
|
+
*/
|
|
130
|
+
export const CLI_RESULTS = {
|
|
131
|
+
init: { command: 'atlas init', schemaName: 'init-plan', schema: InitPlanSchema },
|
|
132
|
+
publish: { command: 'atlas report publish', schemaName: 'publish-result', schema: PublishResultSchema },
|
|
133
|
+
stream: { command: 'atlas report stream', schemaName: 'stream-result', schema: StreamResultSchema },
|
|
134
|
+
merge: { command: 'atlas report merge', schemaName: 'merge-result', schema: MergeResultSchema },
|
|
135
|
+
doctor: { command: 'atlas report doctor', schemaName: 'doctor-result', schema: DoctorResultSchema },
|
|
136
|
+
status: { command: 'atlas report status', schemaName: 'report-status', schema: ReportStatusSchema },
|
|
137
|
+
error: { command: 'a command that was wrong (standard error)', schemaName: 'command-error', schema: CommandErrorSchema }
|
|
138
|
+
};
|
package/dist/server/src/llms.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* address, whether it needs a token), so what it says is true of the server that says it, and it points at the
|
|
7
7
|
* machine-readable description (`/api/openapi.json`) rather than repeating it.
|
|
8
8
|
*/
|
|
9
|
+
import { packageManagerTable } from '../../reports/src/package-managers.js';
|
|
9
10
|
/** The guide for one server, Markdown. Stable in structure: an agent may read it by its headings. */
|
|
10
11
|
export function llmsTxt(context) {
|
|
11
12
|
const { url } = context;
|
|
@@ -28,27 +29,35 @@ ${context.open
|
|
|
28
29
|
|
|
29
30
|
## Integrate a project (run in the project's repository)
|
|
30
31
|
|
|
32
|
+
Install \`@crvouga/atlas\` with the project's existing package manager first. The commands below use Bun;
|
|
33
|
+
substitute the executor for your package manager. Node also runs the CLI; Bun is optional. Read the installed
|
|
34
|
+
\`docs/agents.md\` for the supported runtime versions and full integration playbook.
|
|
35
|
+
|
|
36
|
+
${packageManagerTable()}
|
|
37
|
+
|
|
31
38
|
\`\`\`sh
|
|
39
|
+
bun add -d @crvouga/atlas
|
|
32
40
|
export ATLAS_STORE=${url}${context.open ? '' : '\nexport ATLAS_TOKEN=<a publish token>'}
|
|
33
|
-
|
|
34
|
-
bunx
|
|
35
|
-
bunx
|
|
41
|
+
export ATLAS_PROJECT=checkout-service # choose the same stable slug locally and in CI
|
|
42
|
+
bunx atlas init --server ${url} # detects test runners and specs, prints a plan (add --json for a machine-readable one)
|
|
43
|
+
bunx atlas init --server ${url} --write # writes the CI workflow and the AGENTS.md section the plan names
|
|
44
|
+
bunx atlas report doctor # proves the server can be written to and read
|
|
36
45
|
\`\`\`
|
|
37
46
|
|
|
38
47
|
After each suite, send its results (a JUnit or CTRF file, or JSON lines as checks finish):
|
|
39
48
|
|
|
40
49
|
\`\`\`sh
|
|
41
|
-
bunx
|
|
42
|
-
my-suite --json | bunx
|
|
50
|
+
bunx atlas report publish --producer <suite> --junit out.xml --specs ./specs --environment <env>
|
|
51
|
+
my-suite --json | bunx atlas report stream --producer <suite> --environment <env>
|
|
43
52
|
\`\`\`
|
|
44
53
|
|
|
45
|
-
Or with no tooling at all: \`POST ${url}/api/checks
|
|
54
|
+
Or with no tooling at all: \`POST ${url}/api/checks?project=<slug>\` with \`{"producer":"smoke","checks":[{"id":"login","title":"can log in","status":"passed"}]}\`.
|
|
46
55
|
|
|
47
56
|
## Ask whether it passes
|
|
48
57
|
|
|
49
58
|
\`\`\`sh
|
|
50
|
-
bunx
|
|
51
|
-
curl ${context.open ? '' : '-H "authorization: Bearer $ATLAS_TOKEN" '}${url}/api/status
|
|
59
|
+
bunx atlas report status --format json --branch main --ignore-modified # exit 0 pass, 1 does not, 2 wrong command
|
|
60
|
+
curl ${context.open ? '' : '-H "authorization: Bearer $ATLAS_TOKEN" '}"${url}/api/status?project=$ATLAS_PROJECT" # the same verdict; header X-Atlas-Ok: true|false
|
|
52
61
|
\`\`\`
|
|
53
62
|
|
|
54
63
|
## Rules worth knowing
|