norn-cli 0.8.2__tar.gz → 0.8.3__tar.gz
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.
- {norn_cli-0.8.2/src/norn_cli.egg-info → norn_cli-0.8.3}/PKG-INFO +48 -3
- {norn_cli-0.8.2 → norn_cli-0.8.3}/README.md +47 -2
- {norn_cli-0.8.2 → norn_cli-0.8.3}/pyproject.toml +1 -1
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/__init__.py +1 -1
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/cli.py +71 -0
- norn_cli-0.8.3/src/norf_harness/e2e_scaffold.py +710 -0
- norn_cli-0.8.3/src/norf_harness/implementation_artifact.py +188 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/pipeline.py +155 -64
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/grok.py +101 -26
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/skills/norn/SKILL.md +17 -2
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/skills/norn/references/cli.md +19 -2
- {norn_cli-0.8.2 → norn_cli-0.8.3/src/norn_cli.egg-info}/PKG-INFO +48 -3
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/SOURCES.txt +3 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_cli.py +1 -1
- norn_cli-0.8.3/tests/test_e2e_scaffold.py +331 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_pipeline.py +224 -39
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_providers.py +113 -7
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_skill_install.py +8 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/LICENSE +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/setup.cfg +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/config.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/doctor.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/e2e_evidence.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/gate.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/models.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/project_gates.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/__init__.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/base.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/claude.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/codex.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/command.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/registry.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/providers/router.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/reporting.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/default.yaml +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/e2e_forward_command.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/resources/skills/norn/agents/openai.yaml +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/run_store.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/skill_install.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/tcp_forward.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norf_harness/worktree.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/dependency_links.txt +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/entry_points.txt +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/requires.txt +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/src/norn_cli.egg-info/top_level.txt +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_config.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_e2e_evidence.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_gate.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_project_gates.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_run_store.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_tcp_forward.py +0 -0
- {norn_cli-0.8.2 → norn_cli-0.8.3}/tests/test_worktree.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: norn-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
4
4
|
Summary: Norn: a provider-neutral multi-agent development harness
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Repository, https://github.com/uruca-kk/norn
|
|
@@ -139,6 +139,43 @@ removed when testing ends.
|
|
|
139
139
|
For distributable projects, prefer a reviewed, repository-owned E2E command over
|
|
140
140
|
granting a model browser or host permissions:
|
|
141
141
|
|
|
142
|
+
```bash
|
|
143
|
+
# Adapt the one detected Playwright/Cypress suite. This is a dry run.
|
|
144
|
+
norn init-e2e --adapter existing --project /path/to/repository
|
|
145
|
+
|
|
146
|
+
# Or scaffold a new Playwright entrypoint in a selected workspace package.
|
|
147
|
+
norn init-e2e --preset playwright --package packages/web --project /path/to/repository
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`init-e2e` detects npm, pnpm, yarn, or bun and common E2E scripts/configs. It prints
|
|
151
|
+
the files, minimal Norn YAML, evidence limits, and an optional `git.worktree_setup`
|
|
152
|
+
copy step without changing the repository. Add `--write` after review. Use
|
|
153
|
+
`--external-server` for an already-running Docker/external server, or
|
|
154
|
+
`--server-script NAME` when the project script is not `dev`, `start`, or `serve`.
|
|
155
|
+
`--base-url` must be a loopback HTTP(S) URL.
|
|
156
|
+
|
|
157
|
+
The existing-suite adapter generates a repository-owned runner and, for Playwright,
|
|
158
|
+
a small config that extends the existing config while disabling its `webServer` so
|
|
159
|
+
Norn remains the only server owner. The Playwright preset additionally creates a
|
|
160
|
+
bounded evidence helper, a smoke spec, a JSON reporter, and low-volume artifact
|
|
161
|
+
defaults. It does not install Playwright unless `--install-dependency` is explicitly
|
|
162
|
+
combined with `--write`; existing framework versions and fixtures remain owned by
|
|
163
|
+
the project.
|
|
164
|
+
|
|
165
|
+
Re-running `--write` is idempotent. Existing generated files, package scripts, or
|
|
166
|
+
Norn config values are not replaced when they differ; `--force` is the explicit
|
|
167
|
+
migration path. Validate the checked-in contract without launching a browser:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
norn init-e2e --adapter existing --external-server --check --project /path/to/repository
|
|
171
|
+
norn init-e2e --preset playwright --package packages/web --check --project /path/to/repository
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The generated runner receives `NORN_E2E_BASE_URL` and
|
|
175
|
+
`NORN_E2E_EVIDENCE_DIR`, writes its own result record, and keeps evidence within
|
|
176
|
+
the 20-file / 64 MiB limit. Its corresponding configuration is a shell-free argv
|
|
177
|
+
list:
|
|
178
|
+
|
|
142
179
|
```yaml
|
|
143
180
|
e2e:
|
|
144
181
|
require_all_testers: false
|
|
@@ -153,13 +190,21 @@ Norn runs the four static tester roles in parallel, starts or checks the one sha
|
|
|
153
190
|
server, then executes `runner.command` exactly once as a shell-free argv list.
|
|
154
191
|
The implementer may add or update E2E specs but is explicitly told not to launch a
|
|
155
192
|
browser, start the server, or execute this runner itself.
|
|
193
|
+
The implementer writes its detailed outcome as a run-scoped Markdown artifact and
|
|
194
|
+
returns only a small structured control object containing `status` and the assigned
|
|
195
|
+
artifact path. Norn rejects missing, empty, stale, symlinked, hard-linked, oversized,
|
|
196
|
+
or out-of-contract reports before accepting the provider attempt; changed files are
|
|
197
|
+
derived from Git rather than trusted from the model response.
|
|
156
198
|
The command receives `NORN_E2E_BASE_URL`, `NORN_E2E_EVIDENCE_DIR`, `NORN_RUN_ID`,
|
|
157
199
|
and `NORN_IMPLEMENTATION_ROUND`. It must write at least one non-empty log,
|
|
158
200
|
screenshot, trace, or result file below `NORN_E2E_EVIDENCE_DIR`; Norn's own stdout
|
|
159
201
|
and stderr logs do not count as evidence. Norn owns the authoritative command,
|
|
160
202
|
timeout, exit code, and evidence validation, then gives those results to
|
|
161
|
-
`e2e_tester` for a read-only review with no command tools. The reviewer
|
|
162
|
-
|
|
203
|
+
`e2e_tester` for a read-only review with no command tools. The reviewer returns
|
|
204
|
+
only its summary and findings; Norn injects the authoritative test and E2E
|
|
205
|
+
records into the run artifact. Likewise, static testers do not echo a fixed E2E
|
|
206
|
+
placeholder or shared gate results in their structured responses. The reviewer
|
|
207
|
+
cannot change a failed run into a pass. Managed runners cannot be combined with
|
|
163
208
|
`e2e.tcp_forwards` or `--allow-e2e-danger-full-access` because execution occurs in
|
|
164
209
|
Norn's process, outside a tester provider sandbox.
|
|
165
210
|
|
|
@@ -114,6 +114,43 @@ removed when testing ends.
|
|
|
114
114
|
For distributable projects, prefer a reviewed, repository-owned E2E command over
|
|
115
115
|
granting a model browser or host permissions:
|
|
116
116
|
|
|
117
|
+
```bash
|
|
118
|
+
# Adapt the one detected Playwright/Cypress suite. This is a dry run.
|
|
119
|
+
norn init-e2e --adapter existing --project /path/to/repository
|
|
120
|
+
|
|
121
|
+
# Or scaffold a new Playwright entrypoint in a selected workspace package.
|
|
122
|
+
norn init-e2e --preset playwright --package packages/web --project /path/to/repository
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`init-e2e` detects npm, pnpm, yarn, or bun and common E2E scripts/configs. It prints
|
|
126
|
+
the files, minimal Norn YAML, evidence limits, and an optional `git.worktree_setup`
|
|
127
|
+
copy step without changing the repository. Add `--write` after review. Use
|
|
128
|
+
`--external-server` for an already-running Docker/external server, or
|
|
129
|
+
`--server-script NAME` when the project script is not `dev`, `start`, or `serve`.
|
|
130
|
+
`--base-url` must be a loopback HTTP(S) URL.
|
|
131
|
+
|
|
132
|
+
The existing-suite adapter generates a repository-owned runner and, for Playwright,
|
|
133
|
+
a small config that extends the existing config while disabling its `webServer` so
|
|
134
|
+
Norn remains the only server owner. The Playwright preset additionally creates a
|
|
135
|
+
bounded evidence helper, a smoke spec, a JSON reporter, and low-volume artifact
|
|
136
|
+
defaults. It does not install Playwright unless `--install-dependency` is explicitly
|
|
137
|
+
combined with `--write`; existing framework versions and fixtures remain owned by
|
|
138
|
+
the project.
|
|
139
|
+
|
|
140
|
+
Re-running `--write` is idempotent. Existing generated files, package scripts, or
|
|
141
|
+
Norn config values are not replaced when they differ; `--force` is the explicit
|
|
142
|
+
migration path. Validate the checked-in contract without launching a browser:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
norn init-e2e --adapter existing --external-server --check --project /path/to/repository
|
|
146
|
+
norn init-e2e --preset playwright --package packages/web --check --project /path/to/repository
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The generated runner receives `NORN_E2E_BASE_URL` and
|
|
150
|
+
`NORN_E2E_EVIDENCE_DIR`, writes its own result record, and keeps evidence within
|
|
151
|
+
the 20-file / 64 MiB limit. Its corresponding configuration is a shell-free argv
|
|
152
|
+
list:
|
|
153
|
+
|
|
117
154
|
```yaml
|
|
118
155
|
e2e:
|
|
119
156
|
require_all_testers: false
|
|
@@ -128,13 +165,21 @@ Norn runs the four static tester roles in parallel, starts or checks the one sha
|
|
|
128
165
|
server, then executes `runner.command` exactly once as a shell-free argv list.
|
|
129
166
|
The implementer may add or update E2E specs but is explicitly told not to launch a
|
|
130
167
|
browser, start the server, or execute this runner itself.
|
|
168
|
+
The implementer writes its detailed outcome as a run-scoped Markdown artifact and
|
|
169
|
+
returns only a small structured control object containing `status` and the assigned
|
|
170
|
+
artifact path. Norn rejects missing, empty, stale, symlinked, hard-linked, oversized,
|
|
171
|
+
or out-of-contract reports before accepting the provider attempt; changed files are
|
|
172
|
+
derived from Git rather than trusted from the model response.
|
|
131
173
|
The command receives `NORN_E2E_BASE_URL`, `NORN_E2E_EVIDENCE_DIR`, `NORN_RUN_ID`,
|
|
132
174
|
and `NORN_IMPLEMENTATION_ROUND`. It must write at least one non-empty log,
|
|
133
175
|
screenshot, trace, or result file below `NORN_E2E_EVIDENCE_DIR`; Norn's own stdout
|
|
134
176
|
and stderr logs do not count as evidence. Norn owns the authoritative command,
|
|
135
177
|
timeout, exit code, and evidence validation, then gives those results to
|
|
136
|
-
`e2e_tester` for a read-only review with no command tools. The reviewer
|
|
137
|
-
|
|
178
|
+
`e2e_tester` for a read-only review with no command tools. The reviewer returns
|
|
179
|
+
only its summary and findings; Norn injects the authoritative test and E2E
|
|
180
|
+
records into the run artifact. Likewise, static testers do not echo a fixed E2E
|
|
181
|
+
placeholder or shared gate results in their structured responses. The reviewer
|
|
182
|
+
cannot change a failed run into a pass. Managed runners cannot be combined with
|
|
138
183
|
`e2e.tcp_forwards` or `--allow-e2e-danger-full-access` because execution occurs in
|
|
139
184
|
Norn's process, outside a tester provider sandbox.
|
|
140
185
|
|
|
@@ -14,6 +14,13 @@ import yaml
|
|
|
14
14
|
from . import __version__
|
|
15
15
|
from .config import ConfigError, default_config_path, load_config, project_config_path
|
|
16
16
|
from .doctor import checks_as_dict, doctor_exit_code, run_doctor
|
|
17
|
+
from .e2e_scaffold import (
|
|
18
|
+
E2EScaffoldError,
|
|
19
|
+
apply_e2e_scaffold,
|
|
20
|
+
build_e2e_scaffold,
|
|
21
|
+
check_e2e_scaffold,
|
|
22
|
+
render_e2e_scaffold,
|
|
23
|
+
)
|
|
17
24
|
from .models import ExitCode, RunMode, RunStatus, StopReason
|
|
18
25
|
from .pipeline import execute_pipeline
|
|
19
26
|
from .providers import default_registry
|
|
@@ -31,6 +38,23 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
31
38
|
_project_option(init_parser)
|
|
32
39
|
init_parser.add_argument("--force", action="store_true")
|
|
33
40
|
|
|
41
|
+
e2e_init_parser = subparsers.add_parser(
|
|
42
|
+
"init-e2e", help="scaffold a project-owned managed E2E runner"
|
|
43
|
+
)
|
|
44
|
+
_project_option(e2e_init_parser)
|
|
45
|
+
setup_mode = e2e_init_parser.add_mutually_exclusive_group(required=True)
|
|
46
|
+
setup_mode.add_argument("--adapter", choices=("existing",))
|
|
47
|
+
setup_mode.add_argument("--preset", choices=("playwright",))
|
|
48
|
+
e2e_init_parser.add_argument("--package", type=Path)
|
|
49
|
+
e2e_init_parser.add_argument("--base-url", default="http://127.0.0.1:4173")
|
|
50
|
+
e2e_init_parser.add_argument("--server-script")
|
|
51
|
+
e2e_init_parser.add_argument("--external-server", action="store_true")
|
|
52
|
+
action = e2e_init_parser.add_mutually_exclusive_group()
|
|
53
|
+
action.add_argument("--write", action="store_true")
|
|
54
|
+
action.add_argument("--check", action="store_true")
|
|
55
|
+
e2e_init_parser.add_argument("--install-dependency", action="store_true")
|
|
56
|
+
e2e_init_parser.add_argument("--force", action="store_true")
|
|
57
|
+
|
|
34
58
|
skill_parser = subparsers.add_parser(
|
|
35
59
|
"install-skill", help="install the bundled Codex and/or Claude skill"
|
|
36
60
|
)
|
|
@@ -112,6 +136,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
112
136
|
try:
|
|
113
137
|
if args.command == "init":
|
|
114
138
|
return _init(args)
|
|
139
|
+
if args.command == "init-e2e":
|
|
140
|
+
return _init_e2e(args)
|
|
115
141
|
if args.command == "install-skill":
|
|
116
142
|
return _install_skill(args)
|
|
117
143
|
config = load_config(
|
|
@@ -146,6 +172,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
146
172
|
except SkillInstallError as exc:
|
|
147
173
|
print(f"SKILL ERROR: {exc}", file=sys.stderr)
|
|
148
174
|
return int(ExitCode.CONFIG_ERROR)
|
|
175
|
+
except E2EScaffoldError as exc:
|
|
176
|
+
print(f"E2E SCAFFOLD ERROR: {exc}", file=sys.stderr)
|
|
177
|
+
return int(ExitCode.CONFIG_ERROR)
|
|
149
178
|
except (RunStoreError, FileNotFoundError) as exc:
|
|
150
179
|
print(f"RUN ERROR: {exc}", file=sys.stderr)
|
|
151
180
|
return int(ExitCode.UNEXPECTED_FAILURE)
|
|
@@ -181,6 +210,48 @@ def _install_skill(args: argparse.Namespace) -> int:
|
|
|
181
210
|
return int(ExitCode.SUCCESS)
|
|
182
211
|
|
|
183
212
|
|
|
213
|
+
def _init_e2e(args: argparse.Namespace) -> int:
|
|
214
|
+
if args.external_server and args.server_script:
|
|
215
|
+
raise E2EScaffoldError("--external-server cannot be combined with --server-script")
|
|
216
|
+
if args.install_dependency and args.adapter:
|
|
217
|
+
raise E2EScaffoldError("--install-dependency is only available with --preset playwright")
|
|
218
|
+
if args.install_dependency and not args.write:
|
|
219
|
+
raise E2EScaffoldError("--install-dependency requires --write")
|
|
220
|
+
if args.check and args.force:
|
|
221
|
+
raise E2EScaffoldError("--check cannot be combined with --force")
|
|
222
|
+
|
|
223
|
+
plan = build_e2e_scaffold(
|
|
224
|
+
args.project,
|
|
225
|
+
mode=args.adapter or args.preset,
|
|
226
|
+
package=args.package,
|
|
227
|
+
base_url=args.base_url,
|
|
228
|
+
external_server=args.external_server,
|
|
229
|
+
server_script=args.server_script,
|
|
230
|
+
force=args.force,
|
|
231
|
+
)
|
|
232
|
+
if args.check:
|
|
233
|
+
errors = check_e2e_scaffold(plan)
|
|
234
|
+
if errors:
|
|
235
|
+
print("E2E scaffold check failed:", file=sys.stderr)
|
|
236
|
+
for error in errors:
|
|
237
|
+
print(f" - {error}", file=sys.stderr)
|
|
238
|
+
return int(ExitCode.CONFIG_ERROR)
|
|
239
|
+
print("E2E scaffold valid (browser was not launched)")
|
|
240
|
+
return int(ExitCode.SUCCESS)
|
|
241
|
+
|
|
242
|
+
print(render_e2e_scaffold(plan))
|
|
243
|
+
if not args.write:
|
|
244
|
+
print("Dry run only; rerun with --write to create the scaffold.")
|
|
245
|
+
return int(ExitCode.SUCCESS)
|
|
246
|
+
|
|
247
|
+
changed = apply_e2e_scaffold(plan, install_dependency=args.install_dependency)
|
|
248
|
+
if changed:
|
|
249
|
+
print(f"Wrote {len(changed)} file(s).")
|
|
250
|
+
else:
|
|
251
|
+
print("E2E scaffold already up to date.")
|
|
252
|
+
return int(ExitCode.SUCCESS)
|
|
253
|
+
|
|
254
|
+
|
|
184
255
|
def _ensure_gitignore(root: Path) -> None:
|
|
185
256
|
path = root / ".gitignore"
|
|
186
257
|
markers = (".norf/runs/", ".norf/worktrees/", ".norf/e2e-evidence/")
|