dirigent-cli 0.24.0__tar.gz → 0.25.1__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.
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/PKG-INFO +9 -9
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/pyproject.toml +9 -9
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/pyproject.toml.orig +9 -9
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/commands.py +98 -15
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/init_form.py +2 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/messages.py +4 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/project.py +86 -1
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/summaries.py +1 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/LICENSE +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/README.md +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/__init__.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/aliases.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/context.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/explain.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/formatters.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/graph.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/health.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/local.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/main.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/output.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/params.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/profiles.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/py.typed +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/reaper.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/scaffold.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/schemas.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/seeding.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/source_commands.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/sources.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/starters.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/stream.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/timing.py +0 -0
- {dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/triggers.py +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dirigent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.1
|
|
4
4
|
Summary: The dirigent command line interface (dirigent / dg).
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
-
Requires-Dist: dirigent-block-execute==0.
|
|
10
|
-
Requires-Dist: dirigent-blocks==0.
|
|
11
|
-
Requires-Dist: dirigent-client==0.
|
|
12
|
-
Requires-Dist: dirigent-common==0.
|
|
13
|
-
Requires-Dist: dirigent-core==0.
|
|
14
|
-
Requires-Dist: dirigent-examples==0.
|
|
15
|
-
Requires-Dist: dirigent-plugin==0.
|
|
16
|
-
Requires-Dist: dirigent-server==0.
|
|
9
|
+
Requires-Dist: dirigent-block-execute==0.25.1
|
|
10
|
+
Requires-Dist: dirigent-blocks==0.25.1
|
|
11
|
+
Requires-Dist: dirigent-client==0.25.1
|
|
12
|
+
Requires-Dist: dirigent-common==0.25.1
|
|
13
|
+
Requires-Dist: dirigent-core==0.25.1
|
|
14
|
+
Requires-Dist: dirigent-examples==0.25.1
|
|
15
|
+
Requires-Dist: dirigent-plugin==0.25.1
|
|
16
|
+
Requires-Dist: dirigent-server==0.25.1
|
|
17
17
|
Requires-Dist: httpx2>=2.12.0
|
|
18
18
|
Requires-Dist: python-dotenv>=1.1.0
|
|
19
19
|
Requires-Dist: pyyaml>=6.0.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.25.1"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -11,14 +11,14 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.13",
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"dirigent-block-execute==0.
|
|
15
|
-
"dirigent-blocks==0.
|
|
16
|
-
"dirigent-client==0.
|
|
17
|
-
"dirigent-common==0.
|
|
18
|
-
"dirigent-core==0.
|
|
19
|
-
"dirigent-examples==0.
|
|
20
|
-
"dirigent-plugin==0.
|
|
21
|
-
"dirigent-server==0.
|
|
14
|
+
"dirigent-block-execute==0.25.1",
|
|
15
|
+
"dirigent-blocks==0.25.1",
|
|
16
|
+
"dirigent-client==0.25.1",
|
|
17
|
+
"dirigent-common==0.25.1",
|
|
18
|
+
"dirigent-core==0.25.1",
|
|
19
|
+
"dirigent-examples==0.25.1",
|
|
20
|
+
"dirigent-plugin==0.25.1",
|
|
21
|
+
"dirigent-server==0.25.1",
|
|
22
22
|
"httpx2>=2.12.0",
|
|
23
23
|
"python-dotenv>=1.1.0",
|
|
24
24
|
"pyyaml>=6.0.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.25.1"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -11,14 +11,14 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.13",
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"dirigent-block-execute==0.
|
|
15
|
-
"dirigent-blocks==0.
|
|
16
|
-
"dirigent-client==0.
|
|
17
|
-
"dirigent-common==0.
|
|
18
|
-
"dirigent-core==0.
|
|
19
|
-
"dirigent-examples==0.
|
|
20
|
-
"dirigent-plugin==0.
|
|
21
|
-
"dirigent-server==0.
|
|
14
|
+
"dirigent-block-execute==0.25.1",
|
|
15
|
+
"dirigent-blocks==0.25.1",
|
|
16
|
+
"dirigent-client==0.25.1",
|
|
17
|
+
"dirigent-common==0.25.1",
|
|
18
|
+
"dirigent-core==0.25.1",
|
|
19
|
+
"dirigent-examples==0.25.1",
|
|
20
|
+
"dirigent-plugin==0.25.1",
|
|
21
|
+
"dirigent-server==0.25.1",
|
|
22
22
|
"httpx2>=2.12.0",
|
|
23
23
|
"python-dotenv>=1.1.0",
|
|
24
24
|
"pyyaml>=6.0.3",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""The commands that talk to a server: definitions, execution, catalog, connections, admin."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import json
|
|
4
5
|
import os
|
|
5
6
|
import sys
|
|
6
7
|
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
@@ -58,6 +59,7 @@ from dirigent_cli.messages import (
|
|
|
58
59
|
NO_FAILURE_TO_RETRY,
|
|
59
60
|
NO_FIRST_PASSWORD,
|
|
60
61
|
NO_REPORT_DOCUMENT,
|
|
62
|
+
NO_SUCH_BLOCK,
|
|
61
63
|
NO_SUCH_EXAMPLE,
|
|
62
64
|
NOT_A_BLOCK_KIND,
|
|
63
65
|
NOT_A_PRIORITY,
|
|
@@ -122,6 +124,7 @@ from dirigent_cli.project import (
|
|
|
122
124
|
DEFAULT_PIPELINES_DIR,
|
|
123
125
|
InitChoices,
|
|
124
126
|
ProjectError,
|
|
127
|
+
agent_notes,
|
|
125
128
|
check_choices,
|
|
126
129
|
find_project,
|
|
127
130
|
scaffold,
|
|
@@ -500,10 +503,14 @@ def validate_command(
|
|
|
500
503
|
bool, typer.Option("--explain", help="Also say what the work will cost: how wide, how many tries, how long.")
|
|
501
504
|
] = False,
|
|
502
505
|
) -> None:
|
|
503
|
-
"""Check a document
|
|
506
|
+
"""Check a document against the blocks installed beside dg, or against a server's with --server.
|
|
504
507
|
|
|
505
|
-
Offline,
|
|
506
|
-
|
|
508
|
+
Offline, every step's block and config is checked against the blocks installed beside dg,
|
|
509
|
+
and a connection or pipeline a document names is not, since only an instance knows what it
|
|
510
|
+
holds; a triggers document is checked at the schema level, its clocks and its codes.
|
|
511
|
+
`--server` checks against the instance's own catalog instead, and adds the connections and
|
|
512
|
+
pipelines it holds, the pipeline a triggers document names, and the parameters its
|
|
513
|
+
schedules pin.
|
|
507
514
|
|
|
508
515
|
`--explain` adds the shape of the work ahead for every pipeline document that is valid:
|
|
509
516
|
the fan-out widths, the retry budgets and the waits. With `--server` too, a sensor that
|
|
@@ -525,11 +532,18 @@ def validate_command(
|
|
|
525
532
|
problems=[issue.model_dump(mode="json") for issue in error.problems],
|
|
526
533
|
)
|
|
527
534
|
failures += 1
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
535
|
+
from dirigent_common import format_checker_with
|
|
536
|
+
from dirigent_core.plugins import load_plugin_host
|
|
537
|
+
|
|
538
|
+
host = load_plugin_host()
|
|
539
|
+
catalog = host.catalog()
|
|
540
|
+
blocks: Mapping[str, Any] | None = host.blocks
|
|
541
|
+
checker = format_checker_with(host.formats)
|
|
542
|
+
connections: list[str] | None = None
|
|
543
|
+
pipelines: list[str] | None = None
|
|
531
544
|
targets: dict[str, TriggerTarget] = {}
|
|
532
545
|
if server:
|
|
546
|
+
blocks = None
|
|
533
547
|
with client_for(state) as dg:
|
|
534
548
|
catalog = dg.call(dg.blocks.catalog())
|
|
535
549
|
connections = [
|
|
@@ -543,10 +557,11 @@ def validate_command(
|
|
|
543
557
|
for definition, label in parsed:
|
|
544
558
|
issues = validate_against_catalog(
|
|
545
559
|
definition,
|
|
546
|
-
catalog
|
|
560
|
+
catalog,
|
|
547
561
|
connections=connections,
|
|
548
562
|
pipelines=pipelines,
|
|
549
|
-
|
|
563
|
+
blocks=blocks,
|
|
564
|
+
format_checker=checker,
|
|
550
565
|
target=targets.get(definition.pipeline) if isinstance(definition, TriggersDefinition) else None,
|
|
551
566
|
)
|
|
552
567
|
if issues:
|
|
@@ -560,7 +575,7 @@ def validate_command(
|
|
|
560
575
|
)
|
|
561
576
|
failures += 1
|
|
562
577
|
continue
|
|
563
|
-
checked = "document and catalog" if
|
|
578
|
+
checked = "document and catalog" if server else "document and installed blocks"
|
|
564
579
|
emit_fact(
|
|
565
580
|
"validation",
|
|
566
581
|
message="valid",
|
|
@@ -630,6 +645,10 @@ def init_command(
|
|
|
630
645
|
] = None,
|
|
631
646
|
admin: Annotated[str, typer.Option(help="The first admin account's username.")] = "admin",
|
|
632
647
|
password: Annotated[str | None, typer.Option(help="Its password; asked for when omitted.")] = None,
|
|
648
|
+
agents: Annotated[
|
|
649
|
+
bool,
|
|
650
|
+
typer.Option("--agents", help="Write only AGENTS.md and CLAUDE.md, into a project that already exists."),
|
|
651
|
+
] = False,
|
|
633
652
|
) -> None:
|
|
634
653
|
"""Initialise a uv project and the instance it addresses, ready for `dg dev`.
|
|
635
654
|
|
|
@@ -640,7 +659,8 @@ def init_command(
|
|
|
640
659
|
and mints it a token and a secret key, both kept in `.env`. `--template compose` writes the
|
|
641
660
|
documents and a container stack instead, and initialises nothing locally: the instance is
|
|
642
661
|
the containers. `--template documents` writes the documents alone, against an instance
|
|
643
|
-
somebody else runs.
|
|
662
|
+
somebody else runs. `--agents` writes a coding agent's notes alone, into a project made
|
|
663
|
+
before `dg init` wrote them, and keeps any the project already has.
|
|
644
664
|
"""
|
|
645
665
|
import asyncio
|
|
646
666
|
|
|
@@ -649,6 +669,20 @@ def init_command(
|
|
|
649
669
|
root = directory.resolve()
|
|
650
670
|
version = cli_version()
|
|
651
671
|
unasked = template is None and service is None and pack is None and pipeline is None and not workflow
|
|
672
|
+
if agents:
|
|
673
|
+
if not unasked or password is not None or admin != "admin":
|
|
674
|
+
_init_fail(INIT_REFUSED, detail="--agents writes the notes alone and takes no other option")
|
|
675
|
+
if not directory.is_dir():
|
|
676
|
+
_init_fail(INIT_REFUSED, detail=f"{directory} is not a directory; --agents writes into a project")
|
|
677
|
+
notes = agent_notes(directory)
|
|
678
|
+
emit_fact(
|
|
679
|
+
"project.scaffolded",
|
|
680
|
+
message="scaffolded",
|
|
681
|
+
directory=str(directory),
|
|
682
|
+
files=[_within(path, directory) for path in notes.files],
|
|
683
|
+
**({"skipped": [_within(path, directory) for path in notes.skipped]} if notes.skipped else {}),
|
|
684
|
+
)
|
|
685
|
+
return
|
|
652
686
|
asked = unasked and sys.stdin.isatty() and sys.stdout.isatty()
|
|
653
687
|
# Refusing happens before anything is written or asked for, so a run that cannot finish
|
|
654
688
|
# has not half-created a project, and nobody fills a form for a command that was going
|
|
@@ -963,6 +997,29 @@ def pipeline_validate(
|
|
|
963
997
|
raise typer.Exit(code=1)
|
|
964
998
|
|
|
965
999
|
|
|
1000
|
+
@pipeline_app.command("schema")
|
|
1001
|
+
def pipeline_schema(
|
|
1002
|
+
file: Annotated[
|
|
1003
|
+
Path | None, typer.Option("-f", "--file", help="Write the schema to this file instead of into the record.")
|
|
1004
|
+
] = None,
|
|
1005
|
+
) -> None:
|
|
1006
|
+
"""Compose the JSON Schema a document is written against, from the blocks installed beside dg.
|
|
1007
|
+
|
|
1008
|
+
It is the format and every installed block's config in one Draft 2020-12 schema, which an
|
|
1009
|
+
editor or a language model checks a document against before anything validates it. Written
|
|
1010
|
+
to a file, it is what a ``# yaml-language-server: $schema=`` comment names.
|
|
1011
|
+
"""
|
|
1012
|
+
from dirigent_core.documentschema import document_schema
|
|
1013
|
+
|
|
1014
|
+
catalog = installed_catalog()
|
|
1015
|
+
schema = document_schema(catalog)
|
|
1016
|
+
if file is not None:
|
|
1017
|
+
file.parent.mkdir(parents=True, exist_ok=True)
|
|
1018
|
+
file.write_text(json.dumps(schema, indent=2) + "\n")
|
|
1019
|
+
return emit_fact("pipeline.schema", message="written", path=str(file), blocks=len(catalog.blocks))
|
|
1020
|
+
emit_fact("pipeline.schema", message="composed", blocks=len(catalog.blocks), schema=schema)
|
|
1021
|
+
|
|
1022
|
+
|
|
966
1023
|
@pipeline_app.command("activate")
|
|
967
1024
|
def pipeline_activate(ctx: typer.Context, code: Annotated[str, typer.Argument()]) -> None:
|
|
968
1025
|
"""Make a pipeline runnable again."""
|
|
@@ -2308,16 +2365,35 @@ def blocks_new(
|
|
|
2308
2365
|
console.print(" [bold]uv sync && uv run pytest[/]")
|
|
2309
2366
|
|
|
2310
2367
|
|
|
2368
|
+
#: What ``--local`` means on the block commands.
|
|
2369
|
+
LOCAL_BLOCKS = "Read the blocks installed beside dg, instead of the instance's own."
|
|
2370
|
+
|
|
2371
|
+
|
|
2372
|
+
def installed_catalog() -> Catalog:
|
|
2373
|
+
"""Read the catalog every plugin installed beside this `dg` contributes, without addressing any server."""
|
|
2374
|
+
from dirigent_core.plugins import load_plugin_host
|
|
2375
|
+
|
|
2376
|
+
return load_plugin_host().catalog()
|
|
2377
|
+
|
|
2378
|
+
|
|
2311
2379
|
@blocks_app.command("list")
|
|
2312
2380
|
def blocks_list(
|
|
2313
2381
|
ctx: typer.Context,
|
|
2314
2382
|
kind: Annotated[str | None, typer.Option(help="Only operators, or only sensors.")] = None,
|
|
2383
|
+
local: Annotated[bool, typer.Option("--local", help=LOCAL_BLOCKS)] = False,
|
|
2315
2384
|
) -> None:
|
|
2316
|
-
"""List the blocks this instance can run."""
|
|
2385
|
+
"""List the blocks this instance can run, or with --local the ones installed beside dg."""
|
|
2317
2386
|
if kind is not None and kind not in set(BlockKind):
|
|
2318
2387
|
fail(NOT_A_BLOCK_KIND, kind=repr(kind), allowed=", ".join(sorted(BlockKind)))
|
|
2319
|
-
|
|
2320
|
-
|
|
2388
|
+
wanted = BlockKind(kind) if kind else None
|
|
2389
|
+
if local:
|
|
2390
|
+
catalog = installed_catalog()
|
|
2391
|
+
catalog = catalog.model_copy(
|
|
2392
|
+
update={"blocks": [block for block in catalog.blocks if wanted is None or block.kind is wanted]}
|
|
2393
|
+
)
|
|
2394
|
+
else:
|
|
2395
|
+
with client_for(state_of(ctx)) as dg:
|
|
2396
|
+
catalog = dg.call(dg.blocks.catalog(kind=wanted))
|
|
2321
2397
|
if state_of(ctx).json_output:
|
|
2322
2398
|
emit_records("block", catalog.blocks)
|
|
2323
2399
|
emit_records("storage_scheme", catalog.storage_schemes)
|
|
@@ -2338,10 +2414,17 @@ def blocks_list(
|
|
|
2338
2414
|
def blocks_show(
|
|
2339
2415
|
ctx: typer.Context,
|
|
2340
2416
|
block_id: Annotated[str, typer.Argument(help="The block to describe.")],
|
|
2417
|
+
local: Annotated[bool, typer.Option("--local", help=LOCAL_BLOCKS)] = False,
|
|
2341
2418
|
) -> None:
|
|
2342
2419
|
"""Show one block's config and output schemas."""
|
|
2343
|
-
|
|
2344
|
-
|
|
2420
|
+
if local:
|
|
2421
|
+
found = [block for block in installed_catalog().blocks if block.id == block_id]
|
|
2422
|
+
if not found:
|
|
2423
|
+
fail(NO_SUCH_BLOCK, block=repr(block_id))
|
|
2424
|
+
entry = found[0]
|
|
2425
|
+
else:
|
|
2426
|
+
with client_for(state_of(ctx)) as dg:
|
|
2427
|
+
entry = dg.call(dg.blocks.get(block_id))
|
|
2345
2428
|
if state_of(ctx).json_output:
|
|
2346
2429
|
return emit_one("block", entry)
|
|
2347
2430
|
fields(
|
|
@@ -335,3 +335,7 @@ SOURCE_NOT_CALLABLE = CLI.define(
|
|
|
335
335
|
)
|
|
336
336
|
|
|
337
337
|
SOURCE_UNREACHABLE = CLI.define("source_unreachable", "{url} could not be called: {detail}")
|
|
338
|
+
|
|
339
|
+
NO_SUCH_BLOCK = CLI.define(
|
|
340
|
+
"no_such_block", "no block {block} is installed beside dg; dg blocks list --local names them"
|
|
341
|
+
)
|
|
@@ -842,7 +842,8 @@ README_TEMPLATE = """\
|
|
|
842
842
|
# __NAME__
|
|
843
843
|
|
|
844
844
|
A dirigent project. `pipelines/` holds the pipeline documents and `dirigent.yaml` the
|
|
845
|
-
project's settings.
|
|
845
|
+
project's settings. `AGENTS.md` says how to find, check and run a document, for a person or a
|
|
846
|
+
coding agent working here.
|
|
846
847
|
|
|
847
848
|
`pyproject.toml` pins the dirigent runtime, so the `dg` this project runs on is that pinned
|
|
848
849
|
one and `uv run` is how it is reached.
|
|
@@ -854,6 +855,75 @@ __RUN__
|
|
|
854
855
|
```
|
|
855
856
|
"""
|
|
856
857
|
|
|
858
|
+
AGENTS_TEMPLATE = """\
|
|
859
|
+
# Working in __NAME__
|
|
860
|
+
|
|
861
|
+
This is a dirigent project: pipelines are YAML documents under `pipelines/`, checked and run by
|
|
862
|
+
the `dg` command line this project pins. These notes are for whoever edits it, a person or a
|
|
863
|
+
coding agent.
|
|
864
|
+
|
|
865
|
+
## The loop
|
|
866
|
+
|
|
867
|
+
Every command goes through `uv run dg`, so it runs the dirigent version `pyproject.toml` pins.
|
|
868
|
+
|
|
869
|
+
1. **Find what exists before writing anything.**
|
|
870
|
+
- `uv run dg examples list --local` lists the documents every installed plugin ships, and
|
|
871
|
+
`uv run dg examples show CODE --local` prints one verbatim. Copy from the closest one.
|
|
872
|
+
- `uv run dg pipeline new STARTER` copies a starter into `pipelines/` as a pipeline of its own;
|
|
873
|
+
`uv run dg examples list --local --starter` names the starters.
|
|
874
|
+
- `uv run dg blocks list --local` lists every block, and `uv run dg blocks show BLOCK --local`
|
|
875
|
+
gives its config and output schemas.
|
|
876
|
+
- `uv run dg pipeline schema -f .dirigent/document.schema.json` writes the JSON Schema a
|
|
877
|
+
document is checked against, every installed block's config included. A document whose first
|
|
878
|
+
line is `# yaml-language-server: $schema=../.dirigent/document.schema.json` is checked by any
|
|
879
|
+
editor that reads that comment.
|
|
880
|
+
2. **Check it offline:** `uv run dg validate pipelines/NAME.yaml`. `--explain` adds how wide the
|
|
881
|
+
work is, how many tries it may take and how long it may run.
|
|
882
|
+
3. **Run it in a throwaway instance:** `uv run dg run --local pipelines/NAME.yaml`, with `-v` to
|
|
883
|
+
see every step's output. A pipeline that starts at a source takes `--input '{...}'`, or runs
|
|
884
|
+
with its source's `sample`.
|
|
885
|
+
4. **Apply it to an instance** once it runs: `uv run dg apply`, then `uv run dg run CODE --watch`.
|
|
886
|
+
`uv run dg dev` starts a local instance with its web UI; it keeps running, so a person starts it
|
|
887
|
+
in a terminal of its own.
|
|
888
|
+
|
|
889
|
+
## Reading what dg says
|
|
890
|
+
|
|
891
|
+
Outside a terminal, which is every agent's shell, every `dg` command writes NDJSON: one JSON
|
|
892
|
+
record per line, each with a `kind`. Select with `jq`, for example
|
|
893
|
+
`uv run dg run --local pipelines/NAME.yaml | jq -c 'select(.kind == "run")'`.
|
|
894
|
+
|
|
895
|
+
- A document that does not validate is a `validation` record with `message` `invalid` and
|
|
896
|
+
`problems[]`, each with a stable `code`, a `message` and the `location` in the document it is
|
|
897
|
+
about. Fix what `location` names, then validate again.
|
|
898
|
+
- Anything else refused is a record of kind `error`, with a stable `code`, the `params` it was
|
|
899
|
+
built from, and `problems[]` where there is more than one thing to say.
|
|
900
|
+
- A run's stream is `run`, `step`, `log` and `output` records, and the closing `run` record
|
|
901
|
+
carries every step's outcome, its output and the failures. `exit_code` is the command's own.
|
|
902
|
+
- `uv run dg runs show RUN_ID`, `uv run dg runs logs RUN_ID` and `uv run dg runs profile RUN_ID`
|
|
903
|
+
read a run an instance holds.
|
|
904
|
+
|
|
905
|
+
## Rules that are easy to get wrong
|
|
906
|
+
|
|
907
|
+
- A document starts `format: dirigent/v1`, `kind: pipeline`, and a `code` in lowercase with
|
|
908
|
+
dashes. A step is exactly one of `block`, `source` or `reply`.
|
|
909
|
+
- Values move between steps by reference, never by copying: `${params.NAME}`,
|
|
910
|
+
`${steps.STEP.output.FIELD}`, and `${item}` inside a `for_each`. A step that reads another's
|
|
911
|
+
output lists it under `depends_on`.
|
|
912
|
+
- Secrets never go in a document. A connection holds them, created once with
|
|
913
|
+
`uv run dg connection create KIND CODE`, and a step names it by its code.
|
|
914
|
+
- `.dirigent/state/` and `.env` belong to the local instance; they are not edited by hand.
|
|
915
|
+
|
|
916
|
+
## Reading further
|
|
917
|
+
|
|
918
|
+
The documentation is at https://winterop-com.github.io/dirigent/, and
|
|
919
|
+
https://winterop-com.github.io/dirigent/llms.txt lists every page with what it covers. The
|
|
920
|
+
installed examples are the most reliable reference, because each one is run by dirigent's own
|
|
921
|
+
tests.
|
|
922
|
+
"""
|
|
923
|
+
|
|
924
|
+
#: Claude Code reads CLAUDE.md, and this makes it read the same notes every other agent does.
|
|
925
|
+
CLAUDE_TEMPLATE = "@AGENTS.md\n"
|
|
926
|
+
|
|
857
927
|
#: The commands a scaffolded project's README opens with, per template.
|
|
858
928
|
README_RUN: Final = {
|
|
859
929
|
"local": (
|
|
@@ -909,6 +979,7 @@ def scaffold(directory: Path, choices: InitChoices, *, version: str = "0.0.0") -
|
|
|
909
979
|
_record(directory / "pyproject.toml", pyproject_document(name, choices, version), written, skipped)
|
|
910
980
|
readme = README_TEMPLATE.replace("__NAME__", name).replace("__RUN__", "\n".join(README_RUN[choices.template]))
|
|
911
981
|
_record(directory / "README.md", readme, written, skipped)
|
|
982
|
+
_record_agent_notes(directory, written, skipped)
|
|
912
983
|
_merge_ignore(directory / ".gitignore", ROOT_IGNORE_TEMPLATE, written, skipped)
|
|
913
984
|
if choices.workflow:
|
|
914
985
|
written.append(_write(directory / ".github" / "workflows" / "dirigent.yaml", WORKFLOW_TEMPLATE))
|
|
@@ -919,6 +990,20 @@ def scaffold(directory: Path, choices: InitChoices, *, version: str = "0.0.0") -
|
|
|
919
990
|
return Scaffolded(files=written, skipped=skipped)
|
|
920
991
|
|
|
921
992
|
|
|
993
|
+
def agent_notes(directory: Path) -> Scaffolded:
|
|
994
|
+
"""Write a coding agent's notes into a project, leaving any it already has alone."""
|
|
995
|
+
written: list[Path] = []
|
|
996
|
+
skipped: list[Path] = []
|
|
997
|
+
_record_agent_notes(directory, written, skipped)
|
|
998
|
+
return Scaffolded(files=written, skipped=skipped)
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
def _record_agent_notes(directory: Path, written: list[Path], skipped: list[Path]) -> None:
|
|
1002
|
+
"""Write AGENTS.md, and the CLAUDE.md that points at it, unless the project has its own."""
|
|
1003
|
+
_record(directory / "AGENTS.md", AGENTS_TEMPLATE.replace("__NAME__", project_name(directory)), written, skipped)
|
|
1004
|
+
_record(directory / "CLAUDE.md", CLAUDE_TEMPLATE, written, skipped)
|
|
1005
|
+
|
|
1006
|
+
|
|
922
1007
|
def write_instance_env(directory: Path, token: str) -> Path:
|
|
923
1008
|
"""Write the ``.env`` holding a new instance's token and key, readable by its owner alone."""
|
|
924
1009
|
document = INSTANCE_ENV_TEMPLATE.replace("__TOKEN__", token).replace("__SECRET_KEY__", _an_instance_key())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/__init__.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/operator.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.24.0 → dirigent_cli-0.25.1}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|