donna 0.2.0__py3-none-any.whl → 0.2.2__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/artifacts/intro.md +39 -0
- donna/artifacts/research/specs/report.md +163 -0
- donna/artifacts/research/work/research.md +198 -0
- donna/artifacts/rfc/specs/request_for_change.md +270 -0
- donna/artifacts/rfc/work/create.md +120 -0
- donna/artifacts/rfc/work/do.md +109 -0
- donna/artifacts/rfc/work/plan.md +68 -0
- donna/artifacts/usage/artifacts.md +55 -12
- donna/artifacts/usage/cli.md +114 -39
- donna/artifacts/usage/worlds.md +8 -2
- donna/cli/__main__.py +1 -1
- donna/cli/commands/artifacts.py +104 -17
- donna/cli/commands/sessions.py +8 -8
- donna/cli/commands/workspaces.py +42 -0
- donna/cli/errors.py +18 -0
- donna/cli/types.py +16 -9
- donna/cli/utils.py +2 -2
- donna/core/errors.py +1 -11
- donna/core/result.py +5 -8
- donna/core/utils.py +0 -3
- donna/lib/__init__.py +4 -0
- donna/lib/sources.py +1 -1
- donna/lib/worlds.py +2 -2
- donna/machine/action_requests.py +0 -5
- donna/machine/artifacts.py +8 -6
- donna/machine/primitives.py +5 -5
- donna/machine/sessions.py +13 -5
- donna/machine/state.py +4 -4
- donna/machine/tasks.py +4 -18
- donna/machine/templates.py +4 -2
- donna/primitives/artifacts/specification.py +13 -2
- donna/primitives/artifacts/workflow.py +11 -2
- donna/primitives/directives/list.py +86 -0
- donna/primitives/directives/view.py +52 -11
- donna/primitives/operations/finish_workflow.py +13 -2
- donna/primitives/operations/output.py +87 -0
- donna/primitives/operations/request_action.py +3 -9
- donna/primitives/operations/run_script.py +2 -2
- donna/protocol/utils.py +22 -0
- donna/workspaces/artifacts.py +238 -0
- donna/{world → workspaces}/artifacts_discovery.py +1 -1
- donna/{world → workspaces}/config.py +18 -11
- donna/{world → workspaces}/errors.py +55 -45
- donna/workspaces/initialization.py +78 -0
- donna/{world → workspaces}/markdown.py +21 -26
- donna/{world → workspaces}/sources/base.py +2 -2
- donna/{world → workspaces}/sources/markdown.py +8 -7
- donna/{world → workspaces}/templates.py +4 -4
- donna/workspaces/tmp.py +51 -0
- donna/{world → workspaces}/worlds/base.py +6 -3
- donna/{world → workspaces}/worlds/filesystem.py +30 -10
- donna/{world → workspaces}/worlds/python.py +12 -9
- donna-0.2.2.dist-info/METADATA +463 -0
- donna-0.2.2.dist-info/RECORD +92 -0
- {donna-0.2.0.dist-info → donna-0.2.2.dist-info}/WHEEL +1 -1
- donna/artifacts/work/do_it.md +0 -142
- donna/artifacts/work/do_it_fast.md +0 -98
- donna/artifacts/work/planning.md +0 -245
- donna/cli/commands/projects.py +0 -49
- donna/world/artifacts.py +0 -122
- donna/world/initialization.py +0 -42
- donna/world/tmp.py +0 -33
- donna/world/worlds/__init__.py +0 -0
- donna-0.2.0.dist-info/METADATA +0 -44
- donna-0.2.0.dist-info/RECORD +0 -85
- /donna/{artifacts/work → workspaces}/__init__.py +0 -0
- /donna/{world → workspaces}/sources/__init__.py +0 -0
- /donna/{world → workspaces/worlds}/__init__.py +0 -0
- {donna-0.2.0.dist-info → donna-0.2.2.dist-info}/entry_points.txt +0 -0
- {donna-0.2.0.dist-info → donna-0.2.2.dist-info}/licenses/LICENSE +0 -0
donna/artifacts/usage/worlds.md
CHANGED
|
@@ -22,7 +22,7 @@ s3 buckets, git repositories, databases, etc.
|
|
|
22
22
|
Default worlds and there locations are:
|
|
23
23
|
|
|
24
24
|
- `donna` — `donna.artifacts` — the subpackage with artifacts provided by Donna itself.
|
|
25
|
-
- `home` — `~/.donna/home` — the user-level donna artifacts, i.e. those that should be visible
|
|
25
|
+
- `home` — `~/.donna/home` — the user-level donna artifacts, i.e. those that should be visible for all workspaces on this machine.
|
|
26
26
|
- `project` — `<project-root>/.donna/project` — the project-level donna artifacts, i.e. those that are specific to this project.
|
|
27
27
|
- `session` — `<project-root>/.donna/session` — the session world that contains the current state of work performed by Donna.
|
|
28
28
|
|
|
@@ -33,4 +33,10 @@ All worlds have a free layout, defined by developers who own the particular worl
|
|
|
33
33
|
By default, worlds are read-only. Besides the next exceptions:
|
|
34
34
|
|
|
35
35
|
- `session` in the project world is read-write, Donna stores its current state of work here.
|
|
36
|
-
-
|
|
36
|
+
- `project` is read-write when the developer explicitly asks Donna to change it. For example, to add the result of performed work into docs.
|
|
37
|
+
|
|
38
|
+
## `<world>:intro` artifact
|
|
39
|
+
|
|
40
|
+
It is a recommended practice to provide a short introductory artifact `intro.md` at the root of each world.
|
|
41
|
+
|
|
42
|
+
So, the agent can load descriptions of all worlds in a single command like `donna -p llm artifacts view "*:intro"`.
|
donna/cli/__main__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from donna.cli.application import app # noqa: F401
|
|
2
2
|
from donna.cli.commands import artifacts # noqa: F401
|
|
3
|
-
from donna.cli.commands import projects # noqa: F401
|
|
4
3
|
from donna.cli.commands import sessions # noqa: F401
|
|
4
|
+
from donna.cli.commands import workspaces # noqa: F401
|
|
5
5
|
|
|
6
6
|
app()
|
donna/cli/commands/artifacts.py
CHANGED
|
@@ -1,18 +1,42 @@
|
|
|
1
|
+
import builtins
|
|
1
2
|
from collections.abc import Iterable
|
|
2
3
|
|
|
3
4
|
import typer
|
|
4
5
|
|
|
6
|
+
from donna.cli import errors as cli_errors
|
|
5
7
|
from donna.cli.application import app
|
|
6
|
-
from donna.cli.types import
|
|
8
|
+
from donna.cli.types import (
|
|
9
|
+
FullArtifactIdArgument,
|
|
10
|
+
FullArtifactIdPatternArgument,
|
|
11
|
+
InputPathArgument,
|
|
12
|
+
OutputPathOption,
|
|
13
|
+
TagOption,
|
|
14
|
+
)
|
|
7
15
|
from donna.cli.utils import cells_cli, try_initialize_donna
|
|
16
|
+
from donna.core.errors import ErrorsList
|
|
17
|
+
from donna.core.result import Err, Ok, Result
|
|
8
18
|
from donna.domain.ids import FullArtifactIdPattern
|
|
9
19
|
from donna.protocol.cell_shortcuts import operation_succeeded
|
|
10
20
|
from donna.protocol.cells import Cell
|
|
11
|
-
from donna.
|
|
12
|
-
from donna.
|
|
21
|
+
from donna.workspaces import artifacts as world_artifacts
|
|
22
|
+
from donna.workspaces import tmp as world_tmp
|
|
13
23
|
|
|
14
24
|
artifacts_cli = typer.Typer()
|
|
15
25
|
|
|
26
|
+
DEFAULT_ARTIFACT_PATTERN = FullArtifactIdPattern.parse("**").unwrap()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _parse_slug_with_extension(value: str) -> Result[tuple[str, str], ErrorsList]:
|
|
30
|
+
normalized = value.strip()
|
|
31
|
+
if "." not in normalized:
|
|
32
|
+
return Err([cli_errors.InvalidSlugWithExtension(value=normalized)])
|
|
33
|
+
|
|
34
|
+
slug, extension = normalized.rsplit(".", 1)
|
|
35
|
+
if not slug or not extension:
|
|
36
|
+
return Err([cli_errors.InvalidSlugWithExtension(value=normalized)])
|
|
37
|
+
|
|
38
|
+
return Ok((slug, extension))
|
|
39
|
+
|
|
16
40
|
|
|
17
41
|
@artifacts_cli.callback(invoke_without_command=True)
|
|
18
42
|
def initialize(ctx: typer.Context) -> None:
|
|
@@ -28,20 +52,23 @@ def initialize(ctx: typer.Context) -> None:
|
|
|
28
52
|
help="List artifacts matching a pattern and show their status summaries. Lists all all artifacts by default."
|
|
29
53
|
)
|
|
30
54
|
@cells_cli
|
|
31
|
-
def list(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
artifacts = world_artifacts.list_artifacts(pattern).unwrap()
|
|
55
|
+
def list(
|
|
56
|
+
pattern: FullArtifactIdPatternArgument = DEFAULT_ARTIFACT_PATTERN,
|
|
57
|
+
tags: TagOption = None,
|
|
58
|
+
) -> Iterable[Cell]:
|
|
59
|
+
artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
|
|
36
60
|
|
|
37
61
|
return [artifact.node().status() for artifact in artifacts]
|
|
38
62
|
|
|
39
63
|
|
|
40
|
-
@artifacts_cli.command(help="Displays a
|
|
64
|
+
@artifacts_cli.command(help="Displays artifacts matching a pattern or a specific id")
|
|
41
65
|
@cells_cli
|
|
42
|
-
def view(
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
def view(
|
|
67
|
+
pattern: FullArtifactIdPatternArgument,
|
|
68
|
+
tags: TagOption = None,
|
|
69
|
+
) -> Iterable[Cell]:
|
|
70
|
+
artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
|
|
71
|
+
return [artifact.node().info() for artifact in artifacts]
|
|
45
72
|
|
|
46
73
|
|
|
47
74
|
@artifacts_cli.command(
|
|
@@ -63,6 +90,24 @@ def fetch(id: FullArtifactIdArgument, output: OutputPathOption = None) -> Iterab
|
|
|
63
90
|
]
|
|
64
91
|
|
|
65
92
|
|
|
93
|
+
@artifacts_cli.command(
|
|
94
|
+
help="Create a temporary file for artifact-related work and print its path. Use it to create new artifacts"
|
|
95
|
+
)
|
|
96
|
+
@cells_cli
|
|
97
|
+
def tmp(
|
|
98
|
+
slug_with_extension: str = typer.Argument(..., help="Temporary file slug with extension (example: 'draft.md').")
|
|
99
|
+
) -> Iterable[Cell]:
|
|
100
|
+
slug, extension = _parse_slug_with_extension(slug_with_extension).unwrap()
|
|
101
|
+
output = world_tmp.create_file_for_slug(slug, extension)
|
|
102
|
+
|
|
103
|
+
return [
|
|
104
|
+
operation_succeeded(
|
|
105
|
+
f"Temporary file created at '{output}'",
|
|
106
|
+
output_path=str(output),
|
|
107
|
+
)
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
|
|
66
111
|
@artifacts_cli.command(help="Create or replace the artifact with the contents of a file.")
|
|
67
112
|
@cells_cli
|
|
68
113
|
def update(id: FullArtifactIdArgument, input: InputPathArgument) -> Iterable[Cell]:
|
|
@@ -70,6 +115,48 @@ def update(id: FullArtifactIdArgument, input: InputPathArgument) -> Iterable[Cel
|
|
|
70
115
|
return [operation_succeeded(f"Artifact `{id}` updated from '{input}'", artifact_id=str(id), input_path=str(input))]
|
|
71
116
|
|
|
72
117
|
|
|
118
|
+
@artifacts_cli.command(help="Copy an artifact to another artifact ID (possibly across worlds).")
|
|
119
|
+
@cells_cli
|
|
120
|
+
def copy(source_id: FullArtifactIdArgument, target_id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
121
|
+
world_artifacts.copy_artifact(source_id, target_id).unwrap()
|
|
122
|
+
return [
|
|
123
|
+
operation_succeeded(
|
|
124
|
+
f"Artifact `{source_id}` copied to `{target_id}`",
|
|
125
|
+
source_id=str(source_id),
|
|
126
|
+
target_id=str(target_id),
|
|
127
|
+
)
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@artifacts_cli.command(help="Move an artifact to another artifact ID (possibly across worlds).")
|
|
132
|
+
@cells_cli
|
|
133
|
+
def move(source_id: FullArtifactIdArgument, target_id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
134
|
+
world_artifacts.move_artifact(source_id, target_id).unwrap()
|
|
135
|
+
return [
|
|
136
|
+
operation_succeeded(
|
|
137
|
+
f"Artifact `{source_id}` moved to `{target_id}`",
|
|
138
|
+
source_id=str(source_id),
|
|
139
|
+
target_id=str(target_id),
|
|
140
|
+
)
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@artifacts_cli.command(help="Remove artifacts matching a pattern.")
|
|
145
|
+
@cells_cli
|
|
146
|
+
def remove(
|
|
147
|
+
pattern: FullArtifactIdPatternArgument,
|
|
148
|
+
tags: TagOption = None,
|
|
149
|
+
) -> Iterable[Cell]:
|
|
150
|
+
artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
|
|
151
|
+
|
|
152
|
+
cells: builtins.list[Cell] = []
|
|
153
|
+
for artifact in artifacts:
|
|
154
|
+
world_artifacts.remove_artifact(artifact.id).unwrap()
|
|
155
|
+
cells.append(operation_succeeded(f"Artifact `{artifact.id}` removed", artifact_id=str(artifact.id)))
|
|
156
|
+
|
|
157
|
+
return cells
|
|
158
|
+
|
|
159
|
+
|
|
73
160
|
@artifacts_cli.command(help="Validate an artifact and return any validation errors.")
|
|
74
161
|
@cells_cli
|
|
75
162
|
def validate(id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
@@ -84,11 +171,11 @@ def validate(id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
|
84
171
|
help="Validate all artifacts matching a pattern (defaults to all artifacts) and return any errors."
|
|
85
172
|
)
|
|
86
173
|
@cells_cli
|
|
87
|
-
def validate_all(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
artifacts = world_artifacts.list_artifacts(pattern).unwrap()
|
|
174
|
+
def validate_all(
|
|
175
|
+
pattern: FullArtifactIdPatternArgument = DEFAULT_ARTIFACT_PATTERN,
|
|
176
|
+
tags: TagOption = None,
|
|
177
|
+
) -> Iterable[Cell]: # noqa: CCR001
|
|
178
|
+
artifacts = world_artifacts.list_artifacts(pattern, tags=tags).unwrap()
|
|
92
179
|
|
|
93
180
|
errors = []
|
|
94
181
|
|
donna/cli/commands/sessions.py
CHANGED
|
@@ -21,12 +21,18 @@ def initialize(ctx: typer.Context) -> None:
|
|
|
21
21
|
try_initialize_donna()
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
@sessions_cli.command(help="Start a new session
|
|
24
|
+
@sessions_cli.command(help="Start a new session, reset session state, remove all session artifacts.")
|
|
25
25
|
@cells_cli
|
|
26
26
|
def start() -> Iterable[Cell]:
|
|
27
27
|
return sessions.start()
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
@sessions_cli.command(help="Reset the current session state, keeps session artifacts.")
|
|
31
|
+
@cells_cli
|
|
32
|
+
def reset() -> Iterable[Cell]:
|
|
33
|
+
return sessions.reset()
|
|
34
|
+
|
|
35
|
+
|
|
30
36
|
@sessions_cli.command(
|
|
31
37
|
name="continue",
|
|
32
38
|
help="Continue the current session and emit the next queued action request(s).",
|
|
@@ -48,7 +54,7 @@ def details() -> Iterable[Cell]:
|
|
|
48
54
|
return sessions.details()
|
|
49
55
|
|
|
50
56
|
|
|
51
|
-
@sessions_cli.command(help="Run a workflow
|
|
57
|
+
@sessions_cli.command(help="Run a workflow from an artifact to drive the current session forward.")
|
|
52
58
|
@cells_cli
|
|
53
59
|
def run(workflow_id: FullArtifactIdArgument) -> Iterable[Cell]:
|
|
54
60
|
return sessions.start_workflow(workflow_id)
|
|
@@ -64,12 +70,6 @@ def action_request_completed(
|
|
|
64
70
|
return sessions.complete_action_request(request_id, next_operation_id)
|
|
65
71
|
|
|
66
72
|
|
|
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
73
|
app.add_typer(
|
|
74
74
|
sessions_cli,
|
|
75
75
|
name="sessions",
|
|
@@ -0,0 +1,42 @@
|
|
|
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 ProjectDirArgument
|
|
8
|
+
from donna.cli.utils import cells_cli, try_initialize_donna
|
|
9
|
+
from donna.protocol.cell_shortcuts import operation_succeeded
|
|
10
|
+
from donna.protocol.cells import Cell
|
|
11
|
+
from donna.workspaces.initialization import initialize_workspace
|
|
12
|
+
|
|
13
|
+
workspaces_cli = typer.Typer()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@workspaces_cli.callback(invoke_without_command=True)
|
|
17
|
+
def initialize_callback(ctx: typer.Context) -> None:
|
|
18
|
+
cmd = ctx.invoked_subcommand
|
|
19
|
+
|
|
20
|
+
if cmd is None:
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
if cmd in {"init"}:
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
try_initialize_donna()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@workspaces_cli.command(help="Initialize Donna workspace.")
|
|
30
|
+
@cells_cli
|
|
31
|
+
def init(project_dir: ProjectDirArgument = None) -> Iterable[Cell]:
|
|
32
|
+
target_dir = project_dir or pathlib.Path.cwd()
|
|
33
|
+
initialize_workspace(target_dir).unwrap()
|
|
34
|
+
|
|
35
|
+
return [operation_succeeded("Workspace initialized successfully")]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
app.add_typer(
|
|
39
|
+
workspaces_cli,
|
|
40
|
+
name="workspaces",
|
|
41
|
+
help="Initialize and manage Donna workspace.",
|
|
42
|
+
)
|
donna/cli/errors.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from donna.core import errors as core_errors
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class InternalError(core_errors.InternalError):
|
|
5
|
+
"""Base class for internal errors in donna.cli."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CliError(core_errors.EnvironmentError):
|
|
9
|
+
cell_kind: str = "cli_error"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class InvalidSlugWithExtension(CliError):
|
|
13
|
+
code: str = "donna.cli.invalid_slug_with_extension"
|
|
14
|
+
message: str = "Invalid slug with extension: '{error.value}'."
|
|
15
|
+
ways_to_fix: list[str] = [
|
|
16
|
+
"Use the format '<slug>.<extension>' (for example: 'draft.md').",
|
|
17
|
+
]
|
|
18
|
+
value: str
|
donna/cli/types.py
CHANGED
|
@@ -73,14 +73,22 @@ FullArtifactIdArgument = Annotated[
|
|
|
73
73
|
]
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
FullArtifactIdPattern
|
|
78
|
-
typer.
|
|
76
|
+
FullArtifactIdPatternArgument = Annotated[
|
|
77
|
+
FullArtifactIdPattern,
|
|
78
|
+
typer.Argument(
|
|
79
79
|
parser=_parse_full_artifact_id_pattern,
|
|
80
80
|
help="Artifact pattern (supports '*' and '**', e.g. 'project:*' or '**:intro').",
|
|
81
81
|
),
|
|
82
82
|
]
|
|
83
83
|
|
|
84
|
+
TagOption = Annotated[
|
|
85
|
+
list[str] | None,
|
|
86
|
+
typer.Option(
|
|
87
|
+
"--tag",
|
|
88
|
+
help="Filter artifacts by tag. Repeatable.",
|
|
89
|
+
),
|
|
90
|
+
]
|
|
91
|
+
|
|
84
92
|
|
|
85
93
|
FullArtifactSectionIdArgument = Annotated[
|
|
86
94
|
FullArtifactSectionId,
|
|
@@ -125,14 +133,13 @@ OutputPathOption = Annotated[
|
|
|
125
133
|
),
|
|
126
134
|
]
|
|
127
135
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
typer.Option(
|
|
132
|
-
resolve_path=True,
|
|
136
|
+
ProjectDirArgument = Annotated[
|
|
137
|
+
pathlib.Path | None,
|
|
138
|
+
typer.Argument(
|
|
133
139
|
exists=True,
|
|
134
140
|
file_okay=False,
|
|
135
141
|
dir_okay=True,
|
|
136
|
-
|
|
142
|
+
resolve_path=True,
|
|
143
|
+
help="Optional project directory. Defaults to the current working directory.",
|
|
137
144
|
),
|
|
138
145
|
]
|
donna/cli/utils.py
CHANGED
|
@@ -9,7 +9,7 @@ from donna.core.errors import EnvironmentError
|
|
|
9
9
|
from donna.core.result import UnwrapError
|
|
10
10
|
from donna.protocol.cells import Cell
|
|
11
11
|
from donna.protocol.modes import get_cell_formatter
|
|
12
|
-
from donna.
|
|
12
|
+
from donna.workspaces.initialization import initialize_runtime
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def output_cells(cells: Iterable[Cell]) -> None:
|
|
@@ -43,7 +43,7 @@ def cells_cli(func: Callable[P, Iterable[Cell]]) -> Callable[P, None]:
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def try_initialize_donna() -> None:
|
|
46
|
-
result =
|
|
46
|
+
result = initialize_runtime()
|
|
47
47
|
|
|
48
48
|
if result.is_ok():
|
|
49
49
|
return
|
donna/core/errors.py
CHANGED
|
@@ -53,17 +53,7 @@ class ProjectDirNotFound(CoreEnvironmentError):
|
|
|
53
53
|
message: str = "Could not find a project directory containing `{error.donna_dir_name}`."
|
|
54
54
|
ways_to_fix: list[str] = [
|
|
55
55
|
"Run Donna from within a project directory that contains the donna directory.",
|
|
56
|
-
"Create the donna
|
|
57
|
-
]
|
|
58
|
-
donna_dir_name: str
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class ProjectDirIsHome(CoreEnvironmentError):
|
|
62
|
-
code: str = "donna.core.project_dir_is_home"
|
|
63
|
-
message: str = "The discovered `{error.donna_dir_name}` directory is the home directory, not a project directory."
|
|
64
|
-
ways_to_fix: list[str] = [
|
|
65
|
-
"Run Donna from within a project directory that contains the donna directory.",
|
|
66
|
-
"Move the donna directory out of the home folder into the project root if appropriate.",
|
|
56
|
+
"Create the donna workspace via CLI command if it does not exist yet.",
|
|
67
57
|
]
|
|
68
58
|
donna_dir_name: str
|
|
69
59
|
|
donna/core/result.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import functools
|
|
4
|
-
from dataclasses import dataclass
|
|
5
4
|
from typing import Callable, Generic, ParamSpec, TypeVar, cast
|
|
6
5
|
|
|
7
6
|
from donna.core.errors import InternalError
|
|
@@ -25,10 +24,12 @@ class UnwrapErrError(ResultError):
|
|
|
25
24
|
message: str = "Called unwrap_err on an Ok value."
|
|
26
25
|
|
|
27
26
|
|
|
28
|
-
@dataclass(frozen=True, slots=True)
|
|
29
27
|
class Result(Generic[T, E]):
|
|
30
|
-
_is_ok
|
|
31
|
-
|
|
28
|
+
__slots__ = ("_is_ok", "_value")
|
|
29
|
+
|
|
30
|
+
def __init__(self, is_ok: bool, value: T | E) -> None:
|
|
31
|
+
self._is_ok = is_ok
|
|
32
|
+
self._value = value
|
|
32
33
|
|
|
33
34
|
def is_ok(self) -> bool:
|
|
34
35
|
return self._is_ok
|
|
@@ -82,10 +83,6 @@ def Err(error: E) -> Result[T, E]:
|
|
|
82
83
|
return Result(False, error)
|
|
83
84
|
|
|
84
85
|
|
|
85
|
-
def ok(result: Result[T, E]) -> bool:
|
|
86
|
-
return result.is_ok()
|
|
87
|
-
|
|
88
|
-
|
|
89
86
|
def unwrap_to_error(func: Callable[P, Result[T, E]]) -> Callable[P, Result[T, E]]:
|
|
90
87
|
|
|
91
88
|
@functools.wraps(func)
|
donna/core/utils.py
CHANGED
|
@@ -31,7 +31,4 @@ def discover_project_dir(donna_dir_name: str) -> Result[pathlib.Path, core_error
|
|
|
31
31
|
if donna_dir is None:
|
|
32
32
|
return Err([core_errors.ProjectDirNotFound(donna_dir_name=donna_dir_name)])
|
|
33
33
|
|
|
34
|
-
if donna_dir == donna_home_dir(donna_dir_name):
|
|
35
|
-
return Err([core_errors.ProjectDirIsHome(donna_dir_name=donna_dir_name)])
|
|
36
|
-
|
|
37
34
|
return Ok(donna_dir)
|
donna/lib/__init__.py
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
from donna.primitives.artifacts.specification import Specification, Text
|
|
4
4
|
from donna.primitives.artifacts.workflow import Workflow
|
|
5
5
|
from donna.primitives.directives.goto import GoTo
|
|
6
|
+
from donna.primitives.directives.list import List
|
|
6
7
|
from donna.primitives.directives.task_variable import TaskVariable
|
|
7
8
|
from donna.primitives.directives.view import View
|
|
8
9
|
from donna.primitives.operations.finish_workflow import FinishWorkflow
|
|
10
|
+
from donna.primitives.operations.output import Output
|
|
9
11
|
from donna.primitives.operations.request_action import RequestAction
|
|
10
12
|
from donna.primitives.operations.run_script import RunScript
|
|
11
13
|
|
|
@@ -14,8 +16,10 @@ workflow = Workflow()
|
|
|
14
16
|
text = Text()
|
|
15
17
|
request_action = RequestAction()
|
|
16
18
|
finish = FinishWorkflow()
|
|
19
|
+
output = Output()
|
|
17
20
|
run_script = RunScript()
|
|
18
21
|
|
|
19
22
|
view = View(analyze_id="view")
|
|
23
|
+
list = List(analyze_id="list")
|
|
20
24
|
goto = GoTo(analyze_id="goto")
|
|
21
25
|
task_variable = TaskVariable(analyze_id="task_variable")
|
donna/lib/sources.py
CHANGED
donna/lib/worlds.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Shared world constructor instances for default configuration."""
|
|
2
2
|
|
|
3
|
-
from donna.
|
|
4
|
-
from donna.
|
|
3
|
+
from donna.workspaces.worlds.filesystem import FilesystemWorldConstructor
|
|
4
|
+
from donna.workspaces.worlds.python import PythonWorldConstructor
|
|
5
5
|
|
|
6
6
|
filesystem = FilesystemWorldConstructor()
|
|
7
7
|
python = PythonWorldConstructor()
|
donna/machine/action_requests.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import textwrap
|
|
2
2
|
|
|
3
|
-
import pydantic
|
|
4
|
-
|
|
5
3
|
from donna.core.entities import BaseEntity
|
|
6
4
|
from donna.domain.ids import ActionRequestId, FullArtifactSectionId
|
|
7
5
|
from donna.protocol.cells import Cell
|
|
@@ -13,9 +11,6 @@ class ActionRequest(BaseEntity):
|
|
|
13
11
|
request: str
|
|
14
12
|
operation_id: FullArtifactSectionId
|
|
15
13
|
|
|
16
|
-
# TODO: we may want to make queue items frozen later
|
|
17
|
-
model_config = pydantic.ConfigDict(frozen=False)
|
|
18
|
-
|
|
19
14
|
@classmethod
|
|
20
15
|
def build(cls, request: str, operation_id: FullArtifactSectionId) -> "ActionRequest":
|
|
21
16
|
return cls(
|
donna/machine/artifacts.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any
|
|
2
2
|
|
|
3
|
+
import pydantic
|
|
4
|
+
|
|
3
5
|
from donna.core.entities import BaseEntity
|
|
4
6
|
from donna.core.errors import ErrorsList
|
|
5
7
|
from donna.core.result import Err, Ok, Result, unwrap_to_error
|
|
@@ -13,12 +15,13 @@ from donna.protocol.cells import Cell
|
|
|
13
15
|
from donna.protocol.nodes import Node
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING:
|
|
16
|
-
from donna.
|
|
18
|
+
from donna.workspaces.artifacts import ArtifactRenderContext
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class ArtifactSectionConfig(BaseEntity):
|
|
20
22
|
id: ArtifactSectionId
|
|
21
23
|
kind: PythonImportPath
|
|
24
|
+
tags: list[str] = pydantic.Field(default_factory=list)
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class ArtifactSectionMeta(BaseEntity):
|
|
@@ -32,6 +35,7 @@ class ArtifactSection(BaseEntity):
|
|
|
32
35
|
kind: PythonImportPath
|
|
33
36
|
title: str
|
|
34
37
|
description: str
|
|
38
|
+
tags: list[str] = pydantic.Field(default_factory=list)
|
|
35
39
|
primary: bool = False
|
|
36
40
|
|
|
37
41
|
meta: ArtifactSectionMeta
|
|
@@ -138,12 +142,12 @@ class ArtifactNode(Node):
|
|
|
138
142
|
return primary_section_result.unwrap_err()[0].node().status()
|
|
139
143
|
|
|
140
144
|
primary_section = primary_section_result.unwrap()
|
|
141
|
-
return Cell.
|
|
145
|
+
return Cell.build_markdown(
|
|
142
146
|
kind="artifact_status",
|
|
143
147
|
artifact_id=str(self._artifact.id),
|
|
144
148
|
artifact_kind=str(primary_section.kind),
|
|
145
149
|
artifact_title=primary_section.title,
|
|
146
|
-
|
|
150
|
+
content=primary_section.description,
|
|
147
151
|
)
|
|
148
152
|
|
|
149
153
|
def info(self) -> Cell:
|
|
@@ -161,8 +165,6 @@ class ArtifactNode(Node):
|
|
|
161
165
|
content="\n".join(blocks_result.unwrap()),
|
|
162
166
|
artifact_id=str(self._artifact.id),
|
|
163
167
|
artifact_kind=str(primary_section.kind),
|
|
164
|
-
artifact_title=primary_section.title,
|
|
165
|
-
artifact_description=primary_section.description,
|
|
166
168
|
)
|
|
167
169
|
|
|
168
170
|
def components(self) -> list["Node"]:
|
|
@@ -191,7 +193,7 @@ class ArtifactSectionNode(Node):
|
|
|
191
193
|
def resolve(
|
|
192
194
|
target_id: FullArtifactSectionId, render_context: "ArtifactRenderContext"
|
|
193
195
|
) -> Result[ArtifactSection, ErrorsList]:
|
|
194
|
-
from donna.
|
|
196
|
+
from donna.workspaces import artifacts as world_artifacts
|
|
195
197
|
|
|
196
198
|
artifact = world_artifacts.load_artifact(target_id.full_artifact_id, render_context).unwrap()
|
|
197
199
|
|
donna/machine/primitives.py
CHANGED
|
@@ -14,13 +14,13 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from donna.machine.artifacts import Artifact, ArtifactSection
|
|
15
15
|
from donna.machine.changes import Change
|
|
16
16
|
from donna.machine.tasks import Task, WorkUnit
|
|
17
|
-
from donna.
|
|
18
|
-
from donna.
|
|
19
|
-
from donna.
|
|
20
|
-
from donna.
|
|
17
|
+
from donna.workspaces.config import SourceConfig as SourceConfigModel
|
|
18
|
+
from donna.workspaces.config import WorldConfig
|
|
19
|
+
from donna.workspaces.sources.base import SourceConfig as SourceConfigValue
|
|
20
|
+
from donna.workspaces.worlds.base import World
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
# TODO: Currently
|
|
23
|
+
# TODO: Currently it is a kind of God interface. It is convenient for now.
|
|
24
24
|
# However, in future we should move these methods into specific subclasses.
|
|
25
25
|
class Primitive(BaseEntity):
|
|
26
26
|
config_class: ClassVar[type[ArtifactSectionConfig]] = ArtifactSectionConfig
|
donna/machine/sessions.py
CHANGED
|
@@ -9,10 +9,10 @@ from donna.machine.operations import OperationMeta
|
|
|
9
9
|
from donna.machine.state import ConsistentState, MutableState
|
|
10
10
|
from donna.protocol.cell_shortcuts import operation_succeeded
|
|
11
11
|
from donna.protocol.cells import Cell
|
|
12
|
-
from donna.
|
|
13
|
-
from donna.
|
|
14
|
-
from donna.
|
|
15
|
-
from donna.
|
|
12
|
+
from donna.workspaces import artifacts
|
|
13
|
+
from donna.workspaces import tmp as world_tmp
|
|
14
|
+
from donna.workspaces.config import config
|
|
15
|
+
from donna.workspaces.worlds.base import World
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def _errors_to_cells(errors: ErrorsList) -> list[Cell]:
|
|
@@ -47,7 +47,7 @@ def _save_state(state: ConsistentState) -> Result[None, ErrorsList]:
|
|
|
47
47
|
@unwrap_to_error
|
|
48
48
|
def _state_run(mutator: MutableState) -> Result[None, ErrorsList]:
|
|
49
49
|
while mutator.has_work():
|
|
50
|
-
mutator.
|
|
50
|
+
mutator.execute_next_work_unit().unwrap()
|
|
51
51
|
_save_state(mutator.freeze()).unwrap()
|
|
52
52
|
|
|
53
53
|
return Ok(None)
|
|
@@ -90,6 +90,14 @@ def start() -> list[Cell]:
|
|
|
90
90
|
return [operation_succeeded("Started new session.")]
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
def reset() -> list[Cell]:
|
|
94
|
+
save_result = _save_state(MutableState.build().freeze())
|
|
95
|
+
if save_result.is_err():
|
|
96
|
+
return _errors_to_cells(save_result.unwrap_err())
|
|
97
|
+
|
|
98
|
+
return [operation_succeeded("Session state reset.")]
|
|
99
|
+
|
|
100
|
+
|
|
93
101
|
def clear() -> list[Cell]:
|
|
94
102
|
world_tmp.clear()
|
|
95
103
|
session_result = _session()
|
donna/machine/state.py
CHANGED
|
@@ -118,8 +118,8 @@ class MutableState(BaseState):
|
|
|
118
118
|
self.started = True
|
|
119
119
|
|
|
120
120
|
def add_action_request(self, action_request: ActionRequest) -> None:
|
|
121
|
-
action_request.id
|
|
122
|
-
self.action_requests.append(
|
|
121
|
+
full_request = action_request.replace(id=self.next_action_request_id())
|
|
122
|
+
self.action_requests.append(full_request)
|
|
123
123
|
|
|
124
124
|
def add_work_unit(self, work_unit: WorkUnit) -> None:
|
|
125
125
|
self.work_units.append(work_unit)
|
|
@@ -160,7 +160,7 @@ class MutableState(BaseState):
|
|
|
160
160
|
self.apply_changes(changes)
|
|
161
161
|
|
|
162
162
|
@unwrap_to_error
|
|
163
|
-
def
|
|
163
|
+
def execute_next_work_unit(self) -> Result[None, ErrorsList]:
|
|
164
164
|
next_work_unit = self.get_next_work_unit()
|
|
165
165
|
assert next_work_unit is not None
|
|
166
166
|
|
|
@@ -190,7 +190,7 @@ class StateNode(Node):
|
|
|
190
190
|
"""
|
|
191
191
|
The session is IDLE. There are no active tasks.
|
|
192
192
|
|
|
193
|
-
- If the developer asked you to start working on a new task, you can do so by
|
|
193
|
+
- If the developer asked you to start working on a new task, you can do so by running a new workflow.
|
|
194
194
|
- If you have been working on a task, consider it completed and REPORT THE RESULTS TO THE DEVELOPER.
|
|
195
195
|
"""
|
|
196
196
|
)
|