Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,12 @@ Donna still prints newly created internal journal records immediately using the

Use `donna --help` for a quick reference.

You find detailed documentation in the agent instructions — they are readable and always accurate:
You find detailed documentation in the built-in skill documents — they are readable and always accurate:

- [CLI specification](./.agents/donna/usage/cli.donna.md) — full list of commands and how to use them.
- [Artifacts](./.agents/donna/usage/artifacts.donna.md) — what Donna artifacts are and how to use them.
- [Filesystem layout](./.agents/donna/usage/worlds.donna.md) — how Donna discovers and manages artifacts on the filesystem.
- `donna skill usage` — full list of commands and how to use them.
- `donna skill artifacts` — what Donna artifacts are and how to use them on the filesystem.
- `donna skill configuration` — how to configure `.donna/config.toml`.
- `donna skill initialization` — how to initialize or refresh a Donna workspace.

The documentation below covers aspects important to humans and partially duplicates the agent's instructions.

Expand Down Expand Up @@ -249,7 +250,7 @@ Artifact ids are project-relative filepaths prefixed with `@/`. Section ids appe
Examples:

- `@/specs/work/polish.donna.md`
- `@/.agents/donna/usage/cli.donna.md`
- `@/.agents/donna/work/polish.donna.md`
- `@/.donna/session/execute_rfc.donna.md:review_changes`

You and agents can `list`, `view`, and `validate` artifacts.
Expand Down Expand Up @@ -316,7 +317,7 @@ To execute a workflow, Donna uses a simplified virtual machine (VM) that maintai

### Operations

You can find detailed docs on built-in operations in the [artifacts documentation](./.agents/donna/usage/artifacts.donna.md).
You can find detailed docs on built-in operations in `donna skill artifacts`.

Here is a short list of them:

Expand Down Expand Up @@ -391,7 +392,7 @@ Donna defines a set of built-in Jinja2 functions that provide artifacts with the

Directives are used in the next way: `{{ python.import.path(<args>) }}`.

You can find a detailed documentation of all built-in directives in the [artifacts documentation](./.agents/donna/usage/artifacts.donna.md).
You can find detailed documentation of all built-in directives in `donna skill artifacts`.

Here they are:

Expand Down
38 changes: 5 additions & 33 deletions changes/unreleased.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,10 @@
### Migration

- Move project-specific specs from `.donna/project` to `specs`, or set an explicit `project` world path in your Donna workspace config before upgrading.
- Run `donna workspaces update` in existing projects so bundled Donna specs are installed into `.agents/donna` for the new filesystem-backed `donna` world.
- Update your scripts and specs to use external tools or direct file edits to create, update, move, copy, or delete world artifacts instead using removed Donna commands.
- Update artifact references from legacy ids like `specs:intro` to filepath ids like `@/specs/intro.md`, and include file extensions on all artifact references.
- Set `journal.cmd` in the `.donna/config.toml` as a list of command arguments if you relied on `donna journal` output or session journal files.
**This release is dedicated to a full rethinking of what Donna is and how it works.**

### Changes
The scope of tool functionality was reduced to interpreting state machines for agents (no artifact management, no session management, etc.).

- Added configurable artifact file filters. Donna will see only files that pass all of the filters.
- Replaced artifact ids with project-relative filepaths like `@/specs/intro.md` and `@/.donna/session/plans/plan.md:finish`.
- Changed the default location of project specs to `specs/`.
- Updated the default `project` world path to load from `specs/` instead of `.donna/project/`.
- Rewrote the moved project specs and repository docs to reference the new `specs/` location.
- `--tag` option is replaced with `--predicate` in all CLI commands that accept artifact patterns.
- Removed the Python donna world.
- Added workspace spec dumping into `.agents/donna` on `donna workspaces init` and `donna workspaces update`.
- Reconfigured the default `donna` world to load bundled specs from `.agents/donna` through the filesystem world and removed the Python world implementation.
- Removed world artifact mutation support.
- Removed `donna artifacts` mutation commands and the supporting artifact-mutation code paths.
- Removed `readonly` world-artifact mutability modeling from workspace config and world abstractions.
- Updated artifact and world usage specs to state that developers and external tools mutate world artifacts while Donna validates them.
- Removed `donna artifacts fetch` and `donna artifacts tmp` commands and all related code.
- Removed native journal CLI functionality in favor of external journal integrations.
That's why it is difficult to provide migration instructions or a proper list of changes.

### Breaking Changes
**Treat this version of Donna as a totally new tool** => read the documentation from scratch and adjust your usage accordingly.

- Donna artifact ids now use project-relative filepaths with required file extensions, and legacy colon-delimited artifact ids are no longer supported.
- Donna no longer exposes bundled specs through the Python-backed `donna` world; `donna workspaces init|update` now sync them into `.agents/donna`.
- `donna artifacts` no longer supports `update`, `copy`, `move`, or `remove`.
- Donna no longer mutates world artifacts through workspace APIs or world configuration.
- Donna no longer exposes the `donna journal` CLI command or session journal viewing/following.

### Removals

- Removed the Python world implementation and the `donna.artifacts` package-backed source of bundled Donna specs.
- Removed the `donna journal` CLI command and session journal JSONL read/follow support.
Sorry for the inconvenience. There should be no such breaking changes in the future.
1 change: 1 addition & 0 deletions donna/cli/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from donna.cli.application import app # noqa: F401
from donna.cli.commands import artifacts # noqa: F401
from donna.cli.commands import sessions # noqa: F401
from donna.cli.commands import skills # noqa: F401
from donna.cli.commands import version # noqa: F401
from donna.cli.commands import workspaces # noqa: F401

Expand Down
5 changes: 3 additions & 2 deletions donna/cli/application.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import typer

from donna.cli.entities import GLOBAL_OPTIONS_CONTEXT_KEY, GlobalOptions
from donna.cli.types import ProtocolModeOption, RootOption
from donna.cli.utils import try_initialize_donna
from donna.protocol.modes import Mode

app = typer.Typer(help="Donna CLI: manage hierarchical state machines to guide your AI agents.")


@app.callback()
def initialize(
context: typer.Context,
protocol: ProtocolModeOption = Mode.human,
root_dir: RootOption = None,
) -> None:
try_initialize_donna(project_dir=root_dir, protocol=protocol)
context.meta[GLOBAL_OPTIONS_CONTEXT_KEY] = GlobalOptions(protocol=protocol, root_dir=root_dir)
59 changes: 30 additions & 29 deletions donna/cli/commands/artifacts.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from collections.abc import Iterable

import typer

from donna.cli.application import app
from donna.cli.types import ArtifactIdPatternArgument, PredicateOption, validate_supported_artifact_pattern
from donna.cli.utils import cells_cli
from donna.cli.utils import command_context
from donna.context.context import context
from donna.domain.artifact_ids import ArtifactIdPattern
from donna.machine import journal as machine_journal
from donna.protocol.cell_shortcuts import operation_succeeded
from donna.protocol.cells import Cell
from donna.workspaces.artifacts import RENDER_CONTEXT_VIEW

artifacts_cli = typer.Typer()
Expand Down Expand Up @@ -38,32 +35,34 @@ def _log_operation_on_artifacts(
"and show their status summaries. Lists all artifacts by default."
)
)
@cells_cli
def list(
typer_context: typer.Context,
pattern: ArtifactIdPatternArgument = DEFAULT_ARTIFACT_PATTERN,
predicate: PredicateOption = None,
) -> Iterable[Cell]:
validate_supported_artifact_pattern(pattern)
_log_operation_on_artifacts("List artifacts", pattern, predicate)
) -> None:
with command_context(typer_context) as command:
validate_supported_artifact_pattern(pattern)
_log_operation_on_artifacts("List artifacts", pattern, predicate)

artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, predicate=predicate).unwrap()
artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, predicate=predicate).unwrap()

return [artifact.node().status() for artifact in artifacts]
command.write_cells(artifact.node().status() for artifact in artifacts)


@artifacts_cli.command(
help="Display artifacts matching a pattern or specific id that uses a supported source extension."
)
@cells_cli
def view(
typer_context: typer.Context,
pattern: ArtifactIdPatternArgument,
predicate: PredicateOption = None,
) -> Iterable[Cell]:
validate_supported_artifact_pattern(pattern)
_log_operation_on_artifacts("View artifacts", pattern, predicate)
) -> None:
with command_context(typer_context) as command:
validate_supported_artifact_pattern(pattern)
_log_operation_on_artifacts("View artifacts", pattern, predicate)

artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, predicate=predicate).unwrap()
return [artifact.node().info() for artifact in artifacts]
artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, predicate=predicate).unwrap()
command.write_cells(artifact.node().info() for artifact in artifacts)


@artifacts_cli.command(
Expand All @@ -72,27 +71,29 @@ def view(
"(defaults to all artifacts) and return any errors."
)
)
@cells_cli
def validate(
typer_context: typer.Context,
pattern: ArtifactIdPatternArgument = DEFAULT_ARTIFACT_PATTERN,
predicate: PredicateOption = None,
) -> Iterable[Cell]: # noqa: CCR001
validate_supported_artifact_pattern(pattern)
_log_operation_on_artifacts("Validate artifacts", pattern, predicate)
) -> None: # noqa: CCR001
with command_context(typer_context) as command:
validate_supported_artifact_pattern(pattern)
_log_operation_on_artifacts("Validate artifacts", pattern, predicate)

artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, predicate=predicate).unwrap()
artifacts = context().artifacts.list(pattern, RENDER_CONTEXT_VIEW, predicate=predicate).unwrap()

errors = []
errors = []

for artifact in artifacts:
result = artifact.validate_artifact()
if result.is_err():
errors.extend(result.unwrap_err())
for artifact in artifacts:
result = artifact.validate_artifact()
if result.is_err():
errors.extend(result.unwrap_err())

if errors:
return [error.node().info() for error in errors]
if errors:
command.write_cells(error.node().info() for error in errors)
return

return [operation_succeeded("All artifacts are valid")]
command.write_cells([operation_succeeded("All artifacts are valid")])


app.add_typer(
Expand Down
55 changes: 27 additions & 28 deletions donna/cli/commands/sessions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from collections.abc import Iterable

import typer

from donna.cli.application import app
Expand All @@ -10,62 +8,63 @@
validate_supported_artifact_id,
validate_supported_artifact_section_id,
)
from donna.cli.utils import cells_cli
from donna.cli.utils import command_context
from donna.machine import sessions
from donna.protocol.cells import Cell

sessions_cli = typer.Typer()


@sessions_cli.command(help="Start a new session, reset session state, remove all session artifacts.")
@cells_cli
def start() -> Iterable[Cell]:
return sessions.start().unwrap()
def start(context: typer.Context) -> None:
with command_context(context) as command:
command.write_cells(sessions.start().unwrap())


@sessions_cli.command(help="Reset the current session state, keeps session artifacts.")
@cells_cli
def reset() -> Iterable[Cell]:
return sessions.reset().unwrap()
def reset(context: typer.Context) -> None:
with command_context(context) as command:
command.write_cells(sessions.reset().unwrap())


@sessions_cli.command(
name="continue",
help="Continue the current session and emit the next queued action request(s).",
)
@cells_cli
def continue_() -> Iterable[Cell]:
return sessions.continue_().unwrap()
def continue_(context: typer.Context) -> None:
with command_context(context) as command:
command.write_cells(sessions.continue_().unwrap())


@sessions_cli.command(help="Show a concise status summary for the current session, including pending action requests.")
@cells_cli
def status() -> Iterable[Cell]:
return sessions.status().unwrap()
def status(context: typer.Context) -> None:
with command_context(context) as command:
command.write_cells(sessions.status().unwrap())


@sessions_cli.command(help="Show detailed session state, including action requests.")
@cells_cli
def details() -> Iterable[Cell]:
return sessions.details().unwrap()
def details(context: typer.Context) -> None:
with command_context(context) as command:
command.write_cells(sessions.details().unwrap())


@sessions_cli.command(help="Run a workflow from an artifact to drive the current session forward.")
@cells_cli
def run(workflow_id: ArtifactIdArgument) -> Iterable[Cell]:
validate_supported_artifact_id(workflow_id)
return sessions.start_workflow(workflow_id).unwrap()
def run(context: typer.Context, workflow_id: ArtifactIdArgument) -> None:
with command_context(context) as command:
validate_supported_artifact_id(workflow_id)
command.write_cells(sessions.start_workflow(workflow_id).unwrap())


@sessions_cli.command(
help="Mark an action request as completed and advance the workflow to the specified next operation."
)
@cells_cli
def action_request_completed(
request_id: ActionRequestIdArgument, next_operation_id: ArtifactSectionIdArgument
) -> Iterable[Cell]:
validate_supported_artifact_section_id(next_operation_id)
return sessions.complete_action_request(request_id, next_operation_id).unwrap()
context: typer.Context,
request_id: ActionRequestIdArgument,
next_operation_id: ArtifactSectionIdArgument,
) -> None:
with command_context(context) as command:
validate_supported_artifact_section_id(next_operation_id)
command.write_cells(sessions.complete_action_request(request_id, next_operation_id).unwrap())


app.add_typer(
Expand Down
23 changes: 23 additions & 0 deletions donna/cli/commands/skills.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Annotated

import typer

from donna.cli.application import app
from donna.cli.utils import command_context
from donna.protocol.cells import Cell
from donna.skills.entities import SkillDocument
from donna.skills.fixtures import load_skill_text


@app.command("skill", help="Print built-in Donna skill documentation.")
def skill(context: typer.Context, document: Annotated[SkillDocument, typer.Argument()] = SkillDocument.usage) -> None:
with command_context(context, load_environment=False) as command:
command.write_cells(
[
Cell.build_markdown(
kind="skill",
content=load_skill_text(document),
document=document.value,
)
]
)
34 changes: 11 additions & 23 deletions donna/cli/commands/workspaces.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,32 @@
import pathlib
from collections.abc import Iterable

import typer

from donna.cli.application import app
from donna.cli.types import SkillsOption, SpecsOption
from donna.cli.utils import cells_cli
from donna.cli.utils import command_context
from donna.protocol.cell_shortcuts import operation_succeeded
from donna.protocol.cells import Cell
from donna.workspaces import config as workspace_config
from donna.workspaces.initialization import initialize_workspace, update_workspace

workspaces_cli = typer.Typer()


def _resolve_target_dir() -> pathlib.Path:
if workspace_config.project_dir.is_set():
return workspace_config.project_dir()

return pathlib.Path.cwd()


@workspaces_cli.command(help="Initialize Donna workspace.")
@cells_cli
def init(skills: SkillsOption = True, specs: SpecsOption = True) -> Iterable[Cell]:
target_dir = _resolve_target_dir()
def init(context: typer.Context, skills: SkillsOption = True, specs: SpecsOption = True) -> None:
with command_context(context, load_environment=False) as command:
target_dir = command.target_dir()

initialize_workspace(target_dir, install_skills=skills, install_specs=specs).unwrap()
initialize_workspace(target_dir, install_skills=skills, install_specs=specs).unwrap()

return [operation_succeeded("Workspace initialized successfully")]
command.write_cells([operation_succeeded("Workspace initialized successfully")])


@workspaces_cli.command(help="Update Donna workspace files.")
@cells_cli
def update(skills: SkillsOption = True, specs: SpecsOption = True) -> Iterable[Cell]:
target_dir = _resolve_target_dir()
def update(context: typer.Context, skills: SkillsOption = True, specs: SpecsOption = True) -> None:
with command_context(context) as command:
target_dir = command.target_dir()

update_workspace(target_dir, install_skills=skills, install_specs=specs).unwrap()
update_workspace(target_dir, install_skills=skills, install_specs=specs).unwrap()

return [operation_succeeded("Workspace updated successfully")]
command.write_cells([operation_succeeded("Workspace updated successfully")])


app.add_typer(
Expand Down
Loading
Loading