donna 0.2.0__py3-none-any.whl
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.
- donna/__init__.py +1 -0
- donna/artifacts/__init__.py +0 -0
- donna/artifacts/usage/__init__.py +0 -0
- donna/artifacts/usage/artifacts.md +224 -0
- donna/artifacts/usage/cli.md +117 -0
- donna/artifacts/usage/worlds.md +36 -0
- donna/artifacts/work/__init__.py +0 -0
- donna/artifacts/work/do_it.md +142 -0
- donna/artifacts/work/do_it_fast.md +98 -0
- donna/artifacts/work/planning.md +245 -0
- donna/cli/__init__.py +0 -0
- donna/cli/__main__.py +6 -0
- donna/cli/application.py +17 -0
- donna/cli/commands/__init__.py +0 -0
- donna/cli/commands/artifacts.py +110 -0
- donna/cli/commands/projects.py +49 -0
- donna/cli/commands/sessions.py +77 -0
- donna/cli/types.py +138 -0
- donna/cli/utils.py +53 -0
- donna/core/__init__.py +0 -0
- donna/core/entities.py +27 -0
- donna/core/errors.py +126 -0
- donna/core/result.py +99 -0
- donna/core/utils.py +37 -0
- donna/domain/__init__.py +0 -0
- donna/domain/errors.py +47 -0
- donna/domain/ids.py +497 -0
- donna/lib/__init__.py +21 -0
- donna/lib/sources.py +5 -0
- donna/lib/worlds.py +7 -0
- donna/machine/__init__.py +0 -0
- donna/machine/action_requests.py +50 -0
- donna/machine/artifacts.py +200 -0
- donna/machine/changes.py +91 -0
- donna/machine/errors.py +122 -0
- donna/machine/operations.py +31 -0
- donna/machine/primitives.py +77 -0
- donna/machine/sessions.py +215 -0
- donna/machine/state.py +244 -0
- donna/machine/tasks.py +89 -0
- donna/machine/templates.py +83 -0
- donna/primitives/__init__.py +1 -0
- donna/primitives/artifacts/__init__.py +0 -0
- donna/primitives/artifacts/specification.py +20 -0
- donna/primitives/artifacts/workflow.py +195 -0
- donna/primitives/directives/__init__.py +0 -0
- donna/primitives/directives/goto.py +44 -0
- donna/primitives/directives/task_variable.py +73 -0
- donna/primitives/directives/view.py +45 -0
- donna/primitives/operations/__init__.py +0 -0
- donna/primitives/operations/finish_workflow.py +37 -0
- donna/primitives/operations/request_action.py +89 -0
- donna/primitives/operations/run_script.py +250 -0
- donna/protocol/__init__.py +0 -0
- donna/protocol/cell_shortcuts.py +9 -0
- donna/protocol/cells.py +44 -0
- donna/protocol/errors.py +17 -0
- donna/protocol/formatters/__init__.py +0 -0
- donna/protocol/formatters/automation.py +25 -0
- donna/protocol/formatters/base.py +15 -0
- donna/protocol/formatters/human.py +36 -0
- donna/protocol/formatters/llm.py +39 -0
- donna/protocol/modes.py +40 -0
- donna/protocol/nodes.py +59 -0
- donna/world/__init__.py +0 -0
- donna/world/artifacts.py +122 -0
- donna/world/artifacts_discovery.py +90 -0
- donna/world/config.py +198 -0
- donna/world/errors.py +232 -0
- donna/world/initialization.py +42 -0
- donna/world/markdown.py +267 -0
- donna/world/sources/__init__.py +1 -0
- donna/world/sources/base.py +62 -0
- donna/world/sources/markdown.py +260 -0
- donna/world/templates.py +181 -0
- donna/world/tmp.py +33 -0
- donna/world/worlds/__init__.py +0 -0
- donna/world/worlds/base.py +68 -0
- donna/world/worlds/filesystem.py +189 -0
- donna/world/worlds/python.py +196 -0
- donna-0.2.0.dist-info/METADATA +44 -0
- donna-0.2.0.dist-info/RECORD +85 -0
- donna-0.2.0.dist-info/WHEEL +4 -0
- donna-0.2.0.dist-info/entry_points.txt +3 -0
- donna-0.2.0.dist-info/licenses/LICENSE +28 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Session Planning Guidelines
|
|
2
|
+
|
|
3
|
+
```toml donna
|
|
4
|
+
kind = "donna.lib.specification"
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
This document describes how Donna MUST plan work on a session with the help of workflows. The
|
|
8
|
+
document describes the process of planning, kinds of involved entities and requirements for them.
|
|
9
|
+
|
|
10
|
+
**This requirements MUST be applied to all planning workflows that Donna uses.**
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
Donna's workflows create a plan of work on a session by iteratively polishing the set of session-level artifacts.
|
|
15
|
+
|
|
16
|
+
Artifacts are:
|
|
17
|
+
|
|
18
|
+
- `session:work_scope` — a specification that describes the scope of work to be done on the session.
|
|
19
|
+
- `session:work_execution` — a workflow that describes the step-by-step plan of work to be done on the session.
|
|
20
|
+
|
|
21
|
+
The agent MUST create and iteratively polish these artifacts until they meet all quality criteria described in this document. After the plan is ready, the agent MUST run it as a workflow.
|
|
22
|
+
|
|
23
|
+
## Work Scope Specification
|
|
24
|
+
|
|
25
|
+
The work scope specification has a standard name `session:work_scope` and describes the work to be done in the context of the current session.
|
|
26
|
+
|
|
27
|
+
The specification MUST contain the following sections:
|
|
28
|
+
|
|
29
|
+
1. `Developer request` — a copy of the original description of the work from the developer.
|
|
30
|
+
2. `Work description` — a high-level description of the work to be done, created by Donna
|
|
31
|
+
based on the developer request.
|
|
32
|
+
3. `Goals` — a list of goals that work strives to achieve.
|
|
33
|
+
4. `Objectives` — a list of specific objectives that need to be completed to achieve the goals.
|
|
34
|
+
5. `Known constraints` — a list of constraints for the session.
|
|
35
|
+
6. `Acceptance criteria` — a list of acceptance criteria for the resulted work.
|
|
36
|
+
7. `Deliverables / Artifacts` — a list of concrete deliverables / artifacts that MUST be produced.
|
|
37
|
+
|
|
38
|
+
Sections `Developer request` and `Detailed work description` are free-form text sections.
|
|
39
|
+
Sections `Goals`, `Objectives` should contain lists of items.
|
|
40
|
+
|
|
41
|
+
### "Developer Request" section requirements
|
|
42
|
+
|
|
43
|
+
- This section MUST contain the original request from the developer. The request MUST NOT be modified by Donna.
|
|
44
|
+
|
|
45
|
+
### "Work Description" section requirements
|
|
46
|
+
|
|
47
|
+
- The section MUST contain a clear professional high-level description of the work to be done based
|
|
48
|
+
on the developer's request.
|
|
49
|
+
- The section MUST be limited to a single paragraph with a few sentences.
|
|
50
|
+
- The sectino MUST explain what someone gains after these changes and how they can see it working.
|
|
51
|
+
State the user-visible behavior the task will enable.
|
|
52
|
+
|
|
53
|
+
### "Goals" section requirements
|
|
54
|
+
|
|
55
|
+
- The section MUST contain a list of high-level goals that the work strives to achieve.
|
|
56
|
+
|
|
57
|
+
The goal quality criteria:
|
|
58
|
+
|
|
59
|
+
- A goal describes a desired end state, outcome or result.
|
|
60
|
+
- A goal defines what should ultimately be true, not how to achieve it.
|
|
61
|
+
- A goal must not be defined via listing cases, states, outcomes, etc. Instead, use one of the next
|
|
62
|
+
approaches:
|
|
63
|
+
a) summarize top-layer items into a single goal;
|
|
64
|
+
b) split the goal into multiple more specific goals;
|
|
65
|
+
c) reformulate to a list of second-layer items as required properties of the top-level goal.
|
|
66
|
+
- Each goal must has clear scope and boundaries.
|
|
67
|
+
|
|
68
|
+
### "Objectives" section requirements
|
|
69
|
+
|
|
70
|
+
- The section MUST contain a list of specific objectives that need to be completed to achieve the goals.
|
|
71
|
+
|
|
72
|
+
Objective quality criteria:
|
|
73
|
+
|
|
74
|
+
- An objective MUST describe an achieved state or capability not the act of describing it.
|
|
75
|
+
- An objective MUST be phrased as "X exists / is implemented / is defined / is executable /
|
|
76
|
+
is enforced / …"
|
|
77
|
+
- An objective MUST be atomic: it MUST result in exactly one concrete deliverable: one artifact,
|
|
78
|
+
one executable, one schema, one test suite, etc.
|
|
79
|
+
- An objective is a single clear, externally observable condition of the system (not a description,
|
|
80
|
+
explanation, or analysis) that, when met, moves you closer to achieving a specific goal.
|
|
81
|
+
- An objective is a top-level unit of work whose completion results in a concrete artifact,
|
|
82
|
+
behavior, or state change that can be independently verified without reading prose.
|
|
83
|
+
- Each goal MUST have a set of objectives that, when all achieved, ensure the goal is met.
|
|
84
|
+
- Each goal MUST have 2–6 objectives, unless the goal is demonstrably trivial (≤1 artifact, no dependencies).
|
|
85
|
+
|
|
86
|
+
### "Known Constraints" section requirements
|
|
87
|
+
|
|
88
|
+
- The section MUST contain a list of known constraints that the work MUST respect.
|
|
89
|
+
|
|
90
|
+
Constraint quality criteria:
|
|
91
|
+
|
|
92
|
+
- A known constraint describes a non-negotiable limitation or requirement that the work MUST
|
|
93
|
+
respect (technical, organizational, legal, temporal, compatibility, security, operational).
|
|
94
|
+
- Each constraint MUST be derived from explicitly available inputs (the developer request, existed
|
|
95
|
+
specifications, existed code, information provided by workflows). Donna MUST NOT invent
|
|
96
|
+
constraints.
|
|
97
|
+
- Each constraint MUST be phrased as a verifiable rule using normative language: "MUST / MUST NOT /
|
|
98
|
+
SHOULD / SHOULD NOT".
|
|
99
|
+
- Each constraint MUST be atomic: one rule per record (no "and/or" bundles). If multiple rules
|
|
100
|
+
exist, split into multiple constraint records.
|
|
101
|
+
- Each constraint MUST be externally binding (something the plan must accommodate), not an
|
|
102
|
+
internal preference.
|
|
103
|
+
- Constraints MUST NOT restate goals/objectives in different words. They are bounds, not outcomes.
|
|
104
|
+
- Constraints MUST NOT contain implementation steps, designs, or proposed solutions.
|
|
105
|
+
- Constraints MUST NOT include risks, unknowns, or speculative issues.
|
|
106
|
+
- Constraints MUST be written so a reviewer can answer true/false for compliance by inspecting
|
|
107
|
+
artifacts, behavior, or configuration (not by reading explanatory prose).
|
|
108
|
+
- The section MAY be empty only if no constraints are explicitly known.
|
|
109
|
+
|
|
110
|
+
Examples:
|
|
111
|
+
|
|
112
|
+
- Good: `MUST remain compatible with Python 3.10`
|
|
113
|
+
- Good: `MUST not change public CLI flags`
|
|
114
|
+
- Good: `MUST avoid network access`
|
|
115
|
+
- Good: `MUST run on Windows + Linux`
|
|
116
|
+
- Bad: `We should do it cleanly`
|
|
117
|
+
- Bad: `Prefer elegant code`
|
|
118
|
+
- Bad: `Try to keep it simple`
|
|
119
|
+
|
|
120
|
+
### "Acceptance criteria" section requirements
|
|
121
|
+
|
|
122
|
+
- The section MUST contain a list of acceptance criteria that define how to evaluate the session's results.
|
|
123
|
+
|
|
124
|
+
Acceptance criteria quality criteria:
|
|
125
|
+
|
|
126
|
+
- An acceptance criterion describes a pass/fail condition that determines whether the session's
|
|
127
|
+
results are acceptable to a reviewer, user, or automated gate.
|
|
128
|
+
- Each criterion MUST be derived from explicitly available inputs (developer request, previous
|
|
129
|
+
sections of the plan). Donna MUST NOT invent new scope, features, constraints, or assumptions.
|
|
130
|
+
- Each criterion MUST be phrased as an externally observable, verifiable rule, using normative
|
|
131
|
+
language ("MUST / MUST NOT / SHOULD / SHOULD NOT") or an equivalent test form such as
|
|
132
|
+
Given/When/Then (Gherkin-style).
|
|
133
|
+
- Each criterion MUST be independently checkable by inspecting artifacts, running a command,
|
|
134
|
+
executing tests, or observing runtime behavior/output — not by reading explanatory prose.
|
|
135
|
+
- Each criterion MUST be atomic: one condition per record (no "and/or" bundles). If multiple
|
|
136
|
+
conditions exist, split into multiple criteria records.
|
|
137
|
+
- Criteria MUST NOT describe implementation steps, internal design decisions, or "how" to achieve the result.
|
|
138
|
+
- Criteria MUST NOT restate goals/objectives verbatim. Instead, they must state how success is
|
|
139
|
+
demonstrated (e.g., observable behavior, produced files, enforced rules, test outcomes).
|
|
140
|
+
|
|
141
|
+
Coverage rules:
|
|
142
|
+
|
|
143
|
+
- Each objective MUST have ≥1 acceptance criterion that validates it.
|
|
144
|
+
- Each acceptance criterion MUST map to at least one objective (directly or via a goal that the objective serves).
|
|
145
|
+
- Where relevant, criteria SHOULD specify concrete evaluation conditions, such as:
|
|
146
|
+
- exact CLI output/exit codes, produced artifacts and their locations;
|
|
147
|
+
- supported platforms/versions, configuration prerequisites;
|
|
148
|
+
- measurable thresholds (latency, memory, size limits), if such requirements are explicitly implied or stated.
|
|
149
|
+
- etc.
|
|
150
|
+
|
|
151
|
+
Regression rules:
|
|
152
|
+
|
|
153
|
+
- If the developer request or known constraints imply preserving existing behavior, acceptance
|
|
154
|
+
criteria SHOULD include explicit non-regression checks (what must remain unchanged).
|
|
155
|
+
- The section MUST NOT be empty.
|
|
156
|
+
|
|
157
|
+
### "Deliverables / Artifacts" section requirements
|
|
158
|
+
|
|
159
|
+
- The section MUST contain a list of concrete deliverables/artifacts that MUST be produced by the work.
|
|
160
|
+
|
|
161
|
+
Deliverable/artifact quality criteria:
|
|
162
|
+
|
|
163
|
+
- A deliverable/artifact record MUST name a concrete output that will exist after the work is
|
|
164
|
+
complete (a file, module, package, binary, schema, configuration, test suite, generated report,
|
|
165
|
+
published document, metric in the metrics storage, dashbord, etc.).
|
|
166
|
+
- Each deliverable/artifact MUST be derived from explicitly available inputs (developer request,
|
|
167
|
+
prior sections of this plan). Donna MUST NOT invent new deliverables that introduce new scope.
|
|
168
|
+
- Each deliverable/artifact MUST be externally verifiable by inspecting the repository/workspace,
|
|
169
|
+
produced files, or runtime outputs — not by reading explanatory prose.
|
|
170
|
+
- Each deliverable/artifact record MUST be phrased as an existence statement using normative
|
|
171
|
+
language, e.g. "MUST include …", "MUST produce …", "MUST add …".
|
|
172
|
+
- Each deliverable/artifact record MUST be atomic: exactly one deliverable per record (no
|
|
173
|
+
"and/or", no bundles). If multiple outputs are required, split into multiple records.
|
|
174
|
+
- Each deliverable/artifact MUST specify at least one of:
|
|
175
|
+
- an exact path/location (preferred), or
|
|
176
|
+
- an exact artifact identifier (module/package name, command name, schema name, etc.).
|
|
177
|
+
- If the deliverable is generated by running a command, the record SHOULD specify the command
|
|
178
|
+
entrypoint (e.g., via a CLI (Command-Line Interface) command name) and the expected output
|
|
179
|
+
location, without describing internal implementation steps.
|
|
180
|
+
- Deliverables MUST NOT be vague (e.g., "updated code", "better docs"). They MUST be concrete
|
|
181
|
+
enough that a reviewer can confirm presence/absence.
|
|
182
|
+
- Deliverables MUST NOT restate goals, objectives, constraints, or acceptance criteria. They must
|
|
183
|
+
list *outputs*, not outcomes or pass/fail checks.
|
|
184
|
+
- The section MUST NOT be empty.
|
|
185
|
+
|
|
186
|
+
Source files as artifacts:
|
|
187
|
+
|
|
188
|
+
- Explicitly add source files (paths) as deliverables/artifacts only if the task is specifically
|
|
189
|
+
about creating or modifying those files (e.g., "MUST add docs/cli.md …").
|
|
190
|
+
- Do not add source files as deliverables/artifacts if they are unknown at planning time (i.e. we
|
|
191
|
+
do not know which files will be changed/added). In such cases, focus on higher-level deliverables
|
|
192
|
+
(e.g., "MUST add CLI documentation" instead of listing specific files).
|
|
193
|
+
|
|
194
|
+
## Work Execution Workflow
|
|
195
|
+
|
|
196
|
+
The work execution workflow has a standard name `session:work_execution` and describes the step-by-step plan of work to be done in the context of the current session.
|
|
197
|
+
|
|
198
|
+
The workflow MUST be an artifact of kind `workflow`, see details `{{ donna.lib.view("donna:usage:artifacts") }}`. I.e. the final workflow must be a valid FSM that agent will execute with the help of `donna` tool.
|
|
199
|
+
|
|
200
|
+
Primary requirement:
|
|
201
|
+
|
|
202
|
+
1. **You MUST prefer non-linear or cyclic workflows for complex tasks instead of long linear sequences.** I.e. use loops, conditionals, parallel branches, etc. where appropriate. That should help you to apply changes iteratively, validate them early and often and to polish the results step by step. I.e. prefere an incremental/evolutionary approach over a big-bang one.
|
|
203
|
+
2. However, prefere multiple short loops over a single long loop. The approach `do everything then repeat from scratch` is a bad practice. Instead, break down the work into smaller steps that can be done, verified and polished independently.
|
|
204
|
+
3. The resulted workflow MUST ensure that there is at least 1 refine iteration will always be applied (for each loop).
|
|
205
|
+
4. Describe each operation with effort: add details, examples, behavior for edge cases, etc. Formulate each term and action precisely.
|
|
206
|
+
|
|
207
|
+
General requirements:
|
|
208
|
+
|
|
209
|
+
- Each workflow step should describe an operation dedicated to one semantically atomic task.
|
|
210
|
+
- Each operation of the workflow MUST be derived from explicitly available inputs (developer request +
|
|
211
|
+
prior plan sections). It MUST NOT introduce new scope, features, constraints, or deliverables.
|
|
212
|
+
- Each workflow operation item MUST map to ≥1 objective.
|
|
213
|
+
- Each objective MUST be covered by ≥1 workflow operation that produces the required change/artifact,
|
|
214
|
+
and where relevant by additional item(s) that validate it (tests, checks, demo run).
|
|
215
|
+
- Each workflow operation MUST be atomic: one primary action per item (no "and/or" bundles). If
|
|
216
|
+
multiple actions are needed, split into multiple items.
|
|
217
|
+
- Each workflow operation MUST be actionable and specific enough that a agent can execute it
|
|
218
|
+
without needing additional prose:
|
|
219
|
+
- It SHOULD name the component/module/subsystem affected, if known.
|
|
220
|
+
- It SHOULD name the concrete artifact(s) it will create/modify when those artifacts are already
|
|
221
|
+
known from the "Deliverables / Artifacts" section (do not invent file paths).
|
|
222
|
+
- If a command is required (e.g., a CLI (Command-Line Interface) invocation, test runner command),
|
|
223
|
+
operation SHOULD include the exact command.
|
|
224
|
+
- Workflow operation MUST NOT be vague (e.g., "Improve code quality", "Handle edge cases", "Do the thing").
|
|
225
|
+
- Workflow operations MUST respect all "Known constraints".
|
|
226
|
+
- If workflow operation includes research/design work, there results MUST be represented as concrete artifacts or changes in the `session:work_scope` and `session:work_execution` artifacts.
|
|
227
|
+
|
|
228
|
+
Verification steps:
|
|
229
|
+
|
|
230
|
+
- The workflow SHOULD include explicit verification operations that demonstrate acceptance
|
|
231
|
+
criteria, such as:
|
|
232
|
+
- adding or updating automated tests;
|
|
233
|
+
- running tests/lint/static checks (if such gates exist in the project inputs);
|
|
234
|
+
- running a minimal end-to-end command or scenario that shows the user-visible behavior.
|
|
235
|
+
- Verification MUST be phrased as executable checks (commands, test suites, observable outputs), not
|
|
236
|
+
as "Review the code" or "Make sure it works".
|
|
237
|
+
|
|
238
|
+
Examples:
|
|
239
|
+
|
|
240
|
+
- Good: "Implement the `foo` subcommand behavior to emit the required summary line for each generation."
|
|
241
|
+
- Good: "Add automated tests that assert the `foo` subcommand exit code and exact stdout lines for the sample fixture."
|
|
242
|
+
- Good: "Run `pytest -q` and confirm the new tests pass."
|
|
243
|
+
- Bad: "Implement feature and update tests and docs."
|
|
244
|
+
- Bad: "Consider performance implications."
|
|
245
|
+
- Bad: "Document the approach in detail."
|
donna/cli/__init__.py
ADDED
|
File without changes
|
donna/cli/__main__.py
ADDED
donna/cli/application.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from donna.cli.types import ProtocolModeOption
|
|
4
|
+
from donna.protocol.modes import set_mode
|
|
5
|
+
|
|
6
|
+
app = typer.Typer(help="Donna CLI: manage hierarchical state machines to guide your AI agents.")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@app.callback()
|
|
10
|
+
def initialize(
|
|
11
|
+
protocol: ProtocolModeOption = None,
|
|
12
|
+
) -> None:
|
|
13
|
+
if protocol is None:
|
|
14
|
+
typer.echo("Error: protocol is required. Examples: --protocol=llm or -p llm.", err=True)
|
|
15
|
+
raise typer.Exit(code=2)
|
|
16
|
+
|
|
17
|
+
set_mode(protocol)
|
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from donna.cli.application import app
|
|
6
|
+
from donna.cli.types import FullArtifactIdArgument, FullArtifactIdPatternOption, InputPathArgument, OutputPathOption
|
|
7
|
+
from donna.cli.utils import cells_cli, try_initialize_donna
|
|
8
|
+
from donna.domain.ids import FullArtifactIdPattern
|
|
9
|
+
from donna.protocol.cell_shortcuts import operation_succeeded
|
|
10
|
+
from donna.protocol.cells import Cell
|
|
11
|
+
from donna.world import artifacts as world_artifacts
|
|
12
|
+
from donna.world import tmp as world_tmp
|
|
13
|
+
|
|
14
|
+
artifacts_cli = typer.Typer()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@artifacts_cli.callback(invoke_without_command=True)
|
|
18
|
+
def initialize(ctx: typer.Context) -> None:
|
|
19
|
+
cmd = ctx.invoked_subcommand
|
|
20
|
+
|
|
21
|
+
if cmd is None:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
try_initialize_donna()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@artifacts_cli.command(
|
|
28
|
+
help="List artifacts matching a pattern and show their status summaries. Lists all all artifacts by default."
|
|
29
|
+
)
|
|
30
|
+
@cells_cli
|
|
31
|
+
def list(pattern: FullArtifactIdPatternOption = None) -> Iterable[Cell]:
|
|
32
|
+
if pattern is None:
|
|
33
|
+
pattern = FullArtifactIdPattern.parse("**").unwrap()
|
|
34
|
+
|
|
35
|
+
artifacts = world_artifacts.list_artifacts(pattern).unwrap()
|
|
36
|
+
|
|
37
|
+
return [artifact.node().status() for artifact in artifacts]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@artifacts_cli.command(help="Displays a single artifact.")
|
|
41
|
+
@cells_cli
|
|
42
|
+
def view(id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
43
|
+
artifact = world_artifacts.load_artifact(id).unwrap()
|
|
44
|
+
return [artifact.node().info()]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@artifacts_cli.command(
|
|
48
|
+
help=(
|
|
49
|
+
"Fetch an artifact source into a local file. When --output is omitted, "
|
|
50
|
+
"a temporary file will be created in the project's temp directory."
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
@cells_cli
|
|
54
|
+
def fetch(id: FullArtifactIdArgument, output: OutputPathOption = None) -> Iterable[Cell]:
|
|
55
|
+
if output is None:
|
|
56
|
+
extension = world_artifacts.artifact_file_extension(id).unwrap()
|
|
57
|
+
output = world_tmp.file_for_artifact(id, extension)
|
|
58
|
+
|
|
59
|
+
world_artifacts.fetch_artifact(id, output).unwrap()
|
|
60
|
+
|
|
61
|
+
return [
|
|
62
|
+
operation_succeeded(f"Artifact `{id}` fetched to '{output}'", artifact_id=str(id), output_path=str(output))
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@artifacts_cli.command(help="Create or replace the artifact with the contents of a file.")
|
|
67
|
+
@cells_cli
|
|
68
|
+
def update(id: FullArtifactIdArgument, input: InputPathArgument) -> Iterable[Cell]:
|
|
69
|
+
world_artifacts.update_artifact(id, input).unwrap()
|
|
70
|
+
return [operation_succeeded(f"Artifact `{id}` updated from '{input}'", artifact_id=str(id), input_path=str(input))]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@artifacts_cli.command(help="Validate an artifact and return any validation errors.")
|
|
74
|
+
@cells_cli
|
|
75
|
+
def validate(id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
76
|
+
artifact = world_artifacts.load_artifact(id).unwrap()
|
|
77
|
+
|
|
78
|
+
artifact.validate_artifact().unwrap()
|
|
79
|
+
|
|
80
|
+
return [operation_succeeded(f"Artifact `{id}` is valid", artifact_id=str(id))]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@artifacts_cli.command(
|
|
84
|
+
help="Validate all artifacts matching a pattern (defaults to all artifacts) and return any errors."
|
|
85
|
+
)
|
|
86
|
+
@cells_cli
|
|
87
|
+
def validate_all(pattern: FullArtifactIdPatternOption = None) -> Iterable[Cell]: # noqa: CCR001
|
|
88
|
+
if pattern is None:
|
|
89
|
+
pattern = FullArtifactIdPattern.parse("**").unwrap()
|
|
90
|
+
|
|
91
|
+
artifacts = world_artifacts.list_artifacts(pattern).unwrap()
|
|
92
|
+
|
|
93
|
+
errors = []
|
|
94
|
+
|
|
95
|
+
for artifact in artifacts:
|
|
96
|
+
result = artifact.validate_artifact()
|
|
97
|
+
if result.is_err():
|
|
98
|
+
errors.extend(result.unwrap_err())
|
|
99
|
+
|
|
100
|
+
if errors:
|
|
101
|
+
return [error.node().info() for error in errors]
|
|
102
|
+
|
|
103
|
+
return [operation_succeeded("All artifacts are valid")]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
app.add_typer(
|
|
107
|
+
artifacts_cli,
|
|
108
|
+
name="artifacts",
|
|
109
|
+
help="Inspect, fetch, update, and validate stored artifacts across all Donna worlds.",
|
|
110
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from donna.cli.application import app
|
|
7
|
+
from donna.cli.types import WorkdirOption
|
|
8
|
+
from donna.cli.utils import cells_cli, try_initialize_donna
|
|
9
|
+
from donna.domain.ids import WorldId
|
|
10
|
+
from donna.protocol.cell_shortcuts import operation_succeeded
|
|
11
|
+
from donna.protocol.cells import Cell
|
|
12
|
+
from donna.world.config import config
|
|
13
|
+
|
|
14
|
+
projects_cli = typer.Typer()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@projects_cli.callback(invoke_without_command=True)
|
|
18
|
+
def initialize_callback(ctx: typer.Context) -> None:
|
|
19
|
+
cmd = ctx.invoked_subcommand
|
|
20
|
+
|
|
21
|
+
if cmd is None:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
if cmd in ["initialize"]:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
try_initialize_donna()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@projects_cli.command(help="Initialize Donna project.")
|
|
31
|
+
@cells_cli
|
|
32
|
+
def initialize(workdir: WorkdirOption = pathlib.Path.cwd()) -> Iterable[Cell]:
|
|
33
|
+
# TODO: use workdir attribute
|
|
34
|
+
project = config().get_world(WorldId("project")).unwrap()
|
|
35
|
+
|
|
36
|
+
project.initialize()
|
|
37
|
+
|
|
38
|
+
session = config().get_world(WorldId("session")).unwrap()
|
|
39
|
+
|
|
40
|
+
session.initialize()
|
|
41
|
+
|
|
42
|
+
return [operation_succeeded("Project initialized successfully")]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
app.add_typer(
|
|
46
|
+
projects_cli,
|
|
47
|
+
name="projects",
|
|
48
|
+
help="Initialize and manage Donna project.",
|
|
49
|
+
)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from donna.cli.application import app
|
|
6
|
+
from donna.cli.types import ActionRequestIdArgument, FullArtifactIdArgument, FullArtifactSectionIdArgument
|
|
7
|
+
from donna.cli.utils import cells_cli, try_initialize_donna
|
|
8
|
+
from donna.machine import sessions
|
|
9
|
+
from donna.protocol.cells import Cell
|
|
10
|
+
|
|
11
|
+
sessions_cli = typer.Typer()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@sessions_cli.callback(invoke_without_command=True)
|
|
15
|
+
def initialize(ctx: typer.Context) -> None:
|
|
16
|
+
cmd = ctx.invoked_subcommand
|
|
17
|
+
|
|
18
|
+
if cmd is None:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
try_initialize_donna()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@sessions_cli.command(help="Start a new session and emit the initial session status and action requests.")
|
|
25
|
+
@cells_cli
|
|
26
|
+
def start() -> Iterable[Cell]:
|
|
27
|
+
return sessions.start()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@sessions_cli.command(
|
|
31
|
+
name="continue",
|
|
32
|
+
help="Continue the current session and emit the next queued action request(s).",
|
|
33
|
+
)
|
|
34
|
+
@cells_cli
|
|
35
|
+
def continue_() -> Iterable[Cell]:
|
|
36
|
+
return sessions.continue_()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@sessions_cli.command(help="Show a concise status summary for the current session, including pending action requests.")
|
|
40
|
+
@cells_cli
|
|
41
|
+
def status() -> Iterable[Cell]:
|
|
42
|
+
return sessions.status()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@sessions_cli.command(help="Show detailed session state, including action requests.")
|
|
46
|
+
@cells_cli
|
|
47
|
+
def details() -> Iterable[Cell]:
|
|
48
|
+
return sessions.details()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@sessions_cli.command(help="Run a workflow fron an artifact to drive the current session forward.")
|
|
52
|
+
@cells_cli
|
|
53
|
+
def run(workflow_id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
54
|
+
return sessions.start_workflow(workflow_id)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@sessions_cli.command(
|
|
58
|
+
help="Mark an action request as completed and advance the workflow to the specified next operation."
|
|
59
|
+
)
|
|
60
|
+
@cells_cli
|
|
61
|
+
def action_request_completed(
|
|
62
|
+
request_id: ActionRequestIdArgument, next_operation_id: FullArtifactSectionIdArgument
|
|
63
|
+
) -> Iterable[Cell]:
|
|
64
|
+
return sessions.complete_action_request(request_id, next_operation_id)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@sessions_cli.command(help="Clear the current session state.")
|
|
68
|
+
@cells_cli
|
|
69
|
+
def clear() -> Iterable[Cell]:
|
|
70
|
+
return sessions.clear()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
app.add_typer(
|
|
74
|
+
sessions_cli,
|
|
75
|
+
name="sessions",
|
|
76
|
+
help="Manage Donna session lifecycle.",
|
|
77
|
+
)
|
donna/cli/types.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from donna.cli.utils import output_cells
|
|
7
|
+
from donna.core.errors import ErrorsList
|
|
8
|
+
from donna.domain.ids import ActionRequestId, FullArtifactId, FullArtifactIdPattern, FullArtifactSectionId
|
|
9
|
+
from donna.protocol.modes import Mode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _exit_with_errors(errors: ErrorsList) -> None:
|
|
13
|
+
output_cells([error.node().info() for error in errors])
|
|
14
|
+
raise typer.Exit(code=0)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _parse_full_artifact_id(value: str) -> FullArtifactId:
|
|
18
|
+
result = FullArtifactId.parse(value)
|
|
19
|
+
errors = result.err()
|
|
20
|
+
if errors is not None:
|
|
21
|
+
_exit_with_errors(errors)
|
|
22
|
+
|
|
23
|
+
return result.unwrap()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _parse_full_artifact_id_pattern(value: str) -> FullArtifactIdPattern:
|
|
27
|
+
result = FullArtifactIdPattern.parse(value)
|
|
28
|
+
errors = result.err()
|
|
29
|
+
if errors is not None:
|
|
30
|
+
_exit_with_errors(errors)
|
|
31
|
+
|
|
32
|
+
return result.unwrap()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _parse_full_artifact_section_id(value: str) -> FullArtifactSectionId:
|
|
36
|
+
result = FullArtifactSectionId.parse(value)
|
|
37
|
+
errors = result.err()
|
|
38
|
+
if errors is not None:
|
|
39
|
+
_exit_with_errors(errors)
|
|
40
|
+
|
|
41
|
+
return result.unwrap()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _parse_action_request_id(value: str) -> ActionRequestId:
|
|
45
|
+
if not ActionRequestId.validate(value):
|
|
46
|
+
raise typer.BadParameter("Invalid action request ID format (expected '<prefix>-<number>-<crc>').")
|
|
47
|
+
return ActionRequestId(value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _parse_protocol_mode(value: str) -> Mode:
|
|
51
|
+
try:
|
|
52
|
+
return Mode(value)
|
|
53
|
+
except ValueError as exc:
|
|
54
|
+
allowed = ", ".join(mode.value for mode in Mode)
|
|
55
|
+
raise typer.BadParameter(f"Unsupported protocol mode '{value}'. Expected one of: {allowed}.") from exc
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
ActionRequestIdArgument = Annotated[
|
|
59
|
+
ActionRequestId,
|
|
60
|
+
typer.Argument(
|
|
61
|
+
parser=_parse_action_request_id,
|
|
62
|
+
help="Action request ID (for example: AR-12-x).",
|
|
63
|
+
),
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
FullArtifactIdArgument = Annotated[
|
|
68
|
+
FullArtifactId,
|
|
69
|
+
typer.Argument(
|
|
70
|
+
parser=_parse_full_artifact_id,
|
|
71
|
+
help="Full artifact ID in the form 'world:artifact[:path]' (e.g., 'project:intro').",
|
|
72
|
+
),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
FullArtifactIdPatternOption = Annotated[
|
|
77
|
+
FullArtifactIdPattern | None,
|
|
78
|
+
typer.Option(
|
|
79
|
+
parser=_parse_full_artifact_id_pattern,
|
|
80
|
+
help="Artifact pattern (supports '*' and '**', e.g. 'project:*' or '**:intro').",
|
|
81
|
+
),
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
FullArtifactSectionIdArgument = Annotated[
|
|
86
|
+
FullArtifactSectionId,
|
|
87
|
+
typer.Argument(
|
|
88
|
+
parser=_parse_full_artifact_section_id,
|
|
89
|
+
help="Full artifact section ID in the form 'world:artifact:section'.",
|
|
90
|
+
),
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
ProtocolModeOption = Annotated[
|
|
95
|
+
Mode | None,
|
|
96
|
+
typer.Option(
|
|
97
|
+
"--protocol",
|
|
98
|
+
"-p",
|
|
99
|
+
parser=_parse_protocol_mode,
|
|
100
|
+
help="Protocol mode to use (required). Examples: --protocol=llm, -p llm.",
|
|
101
|
+
),
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
InputPathArgument = Annotated[
|
|
106
|
+
pathlib.Path,
|
|
107
|
+
typer.Argument(
|
|
108
|
+
exists=True,
|
|
109
|
+
file_okay=True,
|
|
110
|
+
dir_okay=False,
|
|
111
|
+
readable=True,
|
|
112
|
+
resolve_path=True,
|
|
113
|
+
help="Path to an existing local file used as input.",
|
|
114
|
+
),
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
OutputPathOption = Annotated[
|
|
119
|
+
pathlib.Path | None,
|
|
120
|
+
typer.Option(
|
|
121
|
+
resolve_path=True,
|
|
122
|
+
dir_okay=False,
|
|
123
|
+
file_okay=True,
|
|
124
|
+
help="Optional output file path (file only). Defaults to a temporary file if omitted.",
|
|
125
|
+
),
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
WorkdirOption = Annotated[
|
|
130
|
+
pathlib.Path,
|
|
131
|
+
typer.Option(
|
|
132
|
+
resolve_path=True,
|
|
133
|
+
exists=True,
|
|
134
|
+
file_okay=False,
|
|
135
|
+
dir_okay=True,
|
|
136
|
+
help="Project root directory to initialize (defaults to current working directory).",
|
|
137
|
+
),
|
|
138
|
+
]
|