ww-agentic-workflows 1.0.0.dev3__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.
Files changed (167) hide show
  1. ww/__init__.py +18 -0
  2. ww/_bundled_extensions/ww/git/extension.py +1728 -0
  3. ww/action_execution.py +887 -0
  4. ww/actions/__init__.py +94 -0
  5. ww/actions/command.py +444 -0
  6. ww/actions/contracts.py +699 -0
  7. ww/actions/extension.py +197 -0
  8. ww/actions/mcp.py +84 -0
  9. ww/actions/prompt.py +74 -0
  10. ww/actions/skill.py +62 -0
  11. ww/actions/slash_command.py +63 -0
  12. ww/agents.py +151 -0
  13. ww/amendments.py +54 -0
  14. ww/artifacts.py +93 -0
  15. ww/assessments.py +181 -0
  16. ww/assets/__init__.py +2 -0
  17. ww/assets/agent_instructions.md +49 -0
  18. ww/assets/docs/examples.md +879 -0
  19. ww/assets/docs/features.md +4639 -0
  20. ww/assets/docs/specification.md +1876 -0
  21. ww/assets/noww_skill.md +11 -0
  22. ww/assets/workflows/catchall.yaml +26 -0
  23. ww/assets/workflows/onboarding.yaml +586 -0
  24. ww/assets/workflows/scriptize.yaml +130 -0
  25. ww/assets/ww-automate_skill.md +23 -0
  26. ww/assets/ww-deduce-feedback_skill.md +38 -0
  27. ww/assets/ww-feedback-rules_skill.md +48 -0
  28. ww/assets/ww-learn-project_skill.md +22 -0
  29. ww/assets/ww-refresh_skill.md +26 -0
  30. ww/assets/ww-rule_skill.md +83 -0
  31. ww/assets/ww-rules-from-artifacts_skill.md +22 -0
  32. ww/assets/ww-scriptize_skill.md +33 -0
  33. ww/assets/ww-setup_skill.md +94 -0
  34. ww/assets/ww-solve_skill.md +23 -0
  35. ww/assets/ww-suggest_skill.md +32 -0
  36. ww/assets/ww-wizard_skill.md +105 -0
  37. ww/assets/ww_skill.md +59 -0
  38. ww/assignments.py +283 -0
  39. ww/bootstrap.py +405 -0
  40. ww/builtin_workflows.py +215 -0
  41. ww/changes.py +225 -0
  42. ww/child_coordination.py +482 -0
  43. ww/children.py +106 -0
  44. ww/claude_permissions.py +115 -0
  45. ww/cli/__init__.py +7 -0
  46. ww/cli/__main__.py +6 -0
  47. ww/cli/audit.py +129 -0
  48. ww/cli/catalogs.py +131 -0
  49. ww/cli/discover.py +607 -0
  50. ww/cli/initialization.py +898 -0
  51. ww/cli/lookup.py +287 -0
  52. ww/cli/main.py +1768 -0
  53. ww/cli/parser.py +1200 -0
  54. ww/cli/prompts.py +217 -0
  55. ww/cli/updates.py +117 -0
  56. ww/completion_artifacts.py +156 -0
  57. ww/completion_inputs.py +39 -0
  58. ww/config/__init__.py +582 -0
  59. ww/config/actions.py +591 -0
  60. ww/config/composition.py +571 -0
  61. ww/config/rules.py +511 -0
  62. ww/config/steps.py +1220 -0
  63. ww/config/values.py +223 -0
  64. ww/config_files.py +191 -0
  65. ww/config_writes.py +264 -0
  66. ww/contracts.py +155 -0
  67. ww/control.py +41 -0
  68. ww/defaults.py +130 -0
  69. ww/design_docs.py +32 -0
  70. ww/discovery.py +104 -0
  71. ww/documents.py +217 -0
  72. ww/errors.py +18 -0
  73. ww/executable.py +43 -0
  74. ww/execution_models/__init__.py +64 -0
  75. ww/execution_models/construction.py +148 -0
  76. ww/execution_models/decoding.py +38 -0
  77. ww/execution_models/plan_codec.py +565 -0
  78. ww/execution_models/records.py +1206 -0
  79. ww/execution_models/runs.py +266 -0
  80. ww/extensions/__init__.py +40 -0
  81. ww/extensions/api.py +559 -0
  82. ww/extensions/registry.py +864 -0
  83. ww/extensions/store.py +78 -0
  84. ww/feedback.py +342 -0
  85. ww/handler_repairs.py +57 -0
  86. ww/hooks/__init__.py +40 -0
  87. ww/hooks/agents.py +380 -0
  88. ww/hooks/install.py +168 -0
  89. ww/hooks/notices.py +206 -0
  90. ww/hooks/records.py +209 -0
  91. ww/hooks/runtime.py +266 -0
  92. ww/hooks/transcripts.py +183 -0
  93. ww/inspect.py +896 -0
  94. ww/instructions/__init__.py +17 -0
  95. ww/instructions/builder.py +1682 -0
  96. ww/instructions/commands.py +335 -0
  97. ww/instructions/handoff.py +149 -0
  98. ww/instructions/models.py +686 -0
  99. ww/instructions/policy.py +219 -0
  100. ww/instructions/text.py +168 -0
  101. ww/interactions.py +187 -0
  102. ww/interpolation.py +37 -0
  103. ww/item_passes.py +167 -0
  104. ww/items.py +99 -0
  105. ww/locking.py +207 -0
  106. ww/metadata_publication.py +230 -0
  107. ww/onboarding.py +229 -0
  108. ww/open_work.py +236 -0
  109. ww/operations.py +193 -0
  110. ww/operator_ui/__init__.py +16 -0
  111. ww/operator_ui/page.html +351 -0
  112. ww/operator_ui/server.py +215 -0
  113. ww/operator_ui/session.py +389 -0
  114. ww/operator_ui/sheet.py +104 -0
  115. ww/operator_ui/view.py +109 -0
  116. ww/output.py +339 -0
  117. ww/output_adapters/__init__.py +12 -0
  118. ww/output_adapters/base.py +25 -0
  119. ww/output_adapters/json_adapter.py +37 -0
  120. ww/output_adapters/markdown.py +2293 -0
  121. ww/output_adapters/rule_pages.py +337 -0
  122. ww/output_adapters/terminal.py +21 -0
  123. ww/package_updates.py +167 -0
  124. ww/plan/__init__.py +38 -0
  125. ww/plan/actions.py +207 -0
  126. ww/plan/compiler.py +1492 -0
  127. ww/plan/constructs.py +456 -0
  128. ww/plan/models.py +665 -0
  129. ww/project_config.py +752 -0
  130. ww/recovery.py +401 -0
  131. ww/replanning.py +367 -0
  132. ww/results.py +77 -0
  133. ww/rule_checks.py +230 -0
  134. ww/rule_conversion.py +331 -0
  135. ww/rule_disputes.py +148 -0
  136. ww/rule_store.py +456 -0
  137. ww/rule_verification.py +714 -0
  138. ww/rule_views.py +447 -0
  139. ww/rule_writes.py +920 -0
  140. ww/run_coordination.py +158 -0
  141. ww/runtimes.py +105 -0
  142. ww/service.py +4405 -0
  143. ww/setup_apply.py +428 -0
  144. ww/step_values.py +20 -0
  145. ww/storage.py +447 -0
  146. ww/storage_adapters/__init__.py +36 -0
  147. ww/storage_adapters/base.py +540 -0
  148. ww/storage_adapters/filesystem.py +370 -0
  149. ww/storage_adapters/memory.py +195 -0
  150. ww/storage_adapters/project_metadata.py +69 -0
  151. ww/storage_adapters/task_document.py +484 -0
  152. ww/task_ids.py +114 -0
  153. ww/task_references.py +124 -0
  154. ww/transitions.py +1619 -0
  155. ww/updates.py +399 -0
  156. ww/upgrade.py +95 -0
  157. ww/validation.py +168 -0
  158. ww/variables.py +275 -0
  159. ww/workflow_config.py +854 -0
  160. ww/workflow_update.py +239 -0
  161. ww/workflow_validation.py +1260 -0
  162. ww/workspace.py +50 -0
  163. ww_agentic_workflows-1.0.0.dev3.dist-info/METADATA +690 -0
  164. ww_agentic_workflows-1.0.0.dev3.dist-info/RECORD +167 -0
  165. ww_agentic_workflows-1.0.0.dev3.dist-info/WHEEL +4 -0
  166. ww_agentic_workflows-1.0.0.dev3.dist-info/entry_points.txt +2 -0
  167. ww_agentic_workflows-1.0.0.dev3.dist-info/licenses/LICENSE +674 -0
ww/errors.py ADDED
@@ -0,0 +1,18 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ """Domain errors exposed as clear CLI failures."""
3
+
4
+
5
+ class WwError(Exception):
6
+ """Base error for expected workflow failures."""
7
+
8
+
9
+ class ConfigurationError(WwError):
10
+ """A workflow definition is missing or invalid."""
11
+
12
+
13
+ class StateError(WwError):
14
+ """A task state is missing, invalid, or cannot transition."""
15
+
16
+
17
+ class LockError(WwError):
18
+ """A file lock could not be acquired within the configured wait bound."""
ww/executable.py ADDED
@@ -0,0 +1,43 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ """How the commands ww prints invoke ww.
3
+
4
+ A project names its ww binary in ``ww.json`` (``executable``),
5
+ so two installs, such as one for developing ww and one pinned to ``dev``, can
6
+ live side by side under different global names. Every command ww prints
7
+ starts with that binary, or with the project launcher, ``./ww``, when the
8
+ project names none. The CLI sets it once per invocation.
9
+
10
+ This module imports nothing from ww, so any module may use it.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import shlex
16
+ from collections.abc import Iterator
17
+ from contextlib import contextmanager
18
+ from contextvars import ContextVar
19
+
20
+ PROJECT_LAUNCHER_COMMAND = "./ww"
21
+ # The binary a project uses when it names none, and the one the launcher
22
+ # falls back to.
23
+ DEFAULT_EXECUTABLE = "ww-agentic-workflows"
24
+ _EXECUTABLE: ContextVar[str] = ContextVar(
25
+ "ww_executable", default=PROJECT_LAUNCHER_COMMAND
26
+ )
27
+
28
+
29
+ @contextmanager
30
+ def printed_executable(executable: str | None) -> Iterator[None]:
31
+ """Print commands with ``executable``; ``None`` keeps the project launcher."""
32
+ token = _EXECUTABLE.set(
33
+ shlex.quote(executable) if executable else PROJECT_LAUNCHER_COMMAND
34
+ )
35
+ try:
36
+ yield
37
+ finally:
38
+ _EXECUTABLE.reset(token)
39
+
40
+
41
+ def ww_command() -> str:
42
+ """How a printed command invokes ww, as the command's first word."""
43
+ return _EXECUTABLE.get()
@@ -0,0 +1,64 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ """Persisted, plan-driven workflow execution models.
3
+
4
+ This facade preserves the public import surface while the persistence records,
5
+ run aggregates, plan codec, and state construction live in focused modules.
6
+ """
7
+
8
+ from .construction import (
9
+ build_step_projection,
10
+ initial_state,
11
+ new_item_execution,
12
+ operation_scope_for,
13
+ )
14
+ from .records import (
15
+ EXECUTION_SCHEMA_VERSION,
16
+ CheckReport,
17
+ CheckResult,
18
+ CommandExecution,
19
+ Dispute,
20
+ ExecutionState,
21
+ HeldCompletion,
22
+ InputRequest,
23
+ PlanItemExecution,
24
+ ProjectMetadataPublication,
25
+ RuleResolution,
26
+ RuleVerdict,
27
+ StepProgress,
28
+ VerificationRule,
29
+ )
30
+ from .runs import (
31
+ PLAN_COMPILER_VERSION,
32
+ PLAN_SCHEMA_VERSION,
33
+ PlanSnapshot,
34
+ TaskRunAggregate,
35
+ WorkflowRunSummary,
36
+ validate_task_runs,
37
+ )
38
+
39
+ __all__ = [
40
+ "CheckReport",
41
+ "CheckResult",
42
+ "EXECUTION_SCHEMA_VERSION",
43
+ "PLAN_COMPILER_VERSION",
44
+ "PLAN_SCHEMA_VERSION",
45
+ "CommandExecution",
46
+ "Dispute",
47
+ "ExecutionState",
48
+ "HeldCompletion",
49
+ "InputRequest",
50
+ "PlanItemExecution",
51
+ "PlanSnapshot",
52
+ "ProjectMetadataPublication",
53
+ "RuleResolution",
54
+ "RuleVerdict",
55
+ "StepProgress",
56
+ "VerificationRule",
57
+ "TaskRunAggregate",
58
+ "WorkflowRunSummary",
59
+ "build_step_projection",
60
+ "initial_state",
61
+ "new_item_execution",
62
+ "operation_scope_for",
63
+ "validate_task_runs",
64
+ ]
@@ -0,0 +1,148 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ """Persisted, plan-driven workflow execution models.
3
+
4
+ These models deliberately contain no YAML or handler-resolution concepts. A
5
+ task starts with a compiled :class:`WorkflowPlan` snapshot and progresses only
6
+ through the normalized plan items in that snapshot.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+ from ww.actions import CommandDefinition, PlannedAction, actions
14
+ from ww.plan import PlanItem, WorkflowPlan
15
+
16
+ from .records import CommandExecution, ExecutionState, PlanItemExecution, StepProgress
17
+ from .runs import PlanSnapshot
18
+
19
+
20
+ def initial_state(
21
+ snapshot: PlanSnapshot,
22
+ modes: tuple[str, ...],
23
+ now: str,
24
+ run_id: str,
25
+ execution_instance_id: str | None = None,
26
+ parent_task_id: str | None = None,
27
+ start_operation_id: str | None = None,
28
+ workflow_runtime: str = "single",
29
+ model: str = "auto",
30
+ reasoning: str = "auto",
31
+ ) -> ExecutionState:
32
+ plan = snapshot.plan
33
+ operation_scope = _operation_scope(run_id, execution_instance_id)
34
+ records = tuple(
35
+ new_item_execution(plan.task_id or "", operation_scope, item)
36
+ for item in plan.items
37
+ )
38
+ return ExecutionState(
39
+ task_id=plan.task_id or "",
40
+ workflow=plan.workflow,
41
+ agent=plan.agent,
42
+ modes=modes,
43
+ status="pending",
44
+ created_at=now,
45
+ updated_at=now,
46
+ snapshot_digest=snapshot.configuration_digest,
47
+ cursor=0,
48
+ active_item_id=None,
49
+ item_executions=records,
50
+ steps=build_step_projection(plan),
51
+ run_id=run_id,
52
+ execution_instance_id=execution_instance_id,
53
+ plan_revision=snapshot.plan_revision,
54
+ plan_digest=snapshot.plan_digest,
55
+ parent_task_id=parent_task_id,
56
+ start_operation_id=start_operation_id,
57
+ workflow_runtime=workflow_runtime,
58
+ model=model,
59
+ reasoning=reasoning,
60
+ )
61
+
62
+
63
+ def new_item_execution(
64
+ task_id: str, operation_scope: str, item: PlanItem
65
+ ) -> PlanItemExecution:
66
+ """Create the complete execution ledger for one concrete plan item."""
67
+ operation_id = f"{task_id}:{operation_scope}:{item.id}"
68
+ return PlanItemExecution(
69
+ plan_item_id=item.id,
70
+ position=item.position,
71
+ operation_id=operation_id,
72
+ commands=tuple(
73
+ CommandExecution(
74
+ index=index,
75
+ operation_id=f"{operation_id}:command:{index}",
76
+ )
77
+ for index, _ in enumerate(_commands_for(item), 1)
78
+ ),
79
+ )
80
+
81
+
82
+ def _commands_for(item: PlanItem) -> tuple[CommandDefinition, ...]:
83
+ """Return tracked command segments when the registered action supports them."""
84
+ if not isinstance(item.operation, PlannedAction):
85
+ return ()
86
+ implementation = actions.get(item.kind)
87
+ traits = implementation.traits(item.payload_as(implementation.planned_type))
88
+ return traits.command_segments or ()
89
+
90
+
91
+ def operation_scope_for(state: ExecutionState) -> str:
92
+ """Return the durable idempotency namespace for an execution instance."""
93
+ return _operation_scope(state.run_id or state.workflow, state.execution_instance_id)
94
+
95
+
96
+ def _operation_scope(run_id: str, execution_instance_id: str | None) -> str:
97
+ return (
98
+ f"{run_id}:{execution_instance_id}"
99
+ if execution_instance_id is not None
100
+ else run_id
101
+ )
102
+
103
+
104
+ def _persisted_step_chain(item: PlanItem) -> tuple[str, ...]:
105
+ """Return the hierarchy encoded by a plan item."""
106
+ chain = (*item.ancestors, item.step)
107
+ if item.parent and item.parent not in chain[:-1]:
108
+ return item.parent, item.step
109
+ return chain
110
+
111
+
112
+ def build_step_projection(
113
+ plan: WorkflowPlan, previous: tuple[StepProgress, ...] = ()
114
+ ) -> tuple[StepProgress, ...]:
115
+ """Build the recursive tree, retaining progress for paths that still exist."""
116
+ prior: dict[str, StepProgress] = {}
117
+
118
+ def remember(entries: tuple[StepProgress, ...]) -> None:
119
+ for entry in entries:
120
+ prior[entry.path] = entry
121
+ remember(entry.children)
122
+
123
+ remember(previous)
124
+ nodes: dict[str, dict[str, Any]] = {}
125
+ order: list[str] = []
126
+ for item in plan.items:
127
+ chain = _persisted_step_chain(item)
128
+ for index, path in enumerate(chain):
129
+ parent = chain[index - 1] if index else None
130
+ if path not in nodes:
131
+ nodes[path] = {"parent": parent, "children": []}
132
+ order.append(path)
133
+ if parent and path not in nodes[parent]["children"]:
134
+ nodes[parent]["children"].append(path)
135
+
136
+ def node(path: str) -> StepProgress:
137
+ info = nodes[path]
138
+ existing = prior.get(path)
139
+ return StepProgress(
140
+ path.rsplit("/", 1)[-1],
141
+ path,
142
+ status=existing.status if existing is not None else "pending",
143
+ started_at=existing.started_at if existing is not None else None,
144
+ completed_at=existing.completed_at if existing is not None else None,
145
+ children=tuple(node(child) for child in info["children"]),
146
+ )
147
+
148
+ return tuple(node(path) for path in order if nodes[path]["parent"] is None)
@@ -0,0 +1,38 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ """Decoding helpers specific to persisted execution records.
3
+
4
+ Primitive checks live in :mod:`ww.validation`; this module keeps only the
5
+ shapes that exist solely in saved task state.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Callable
11
+ from typing import Any, TypeVar
12
+
13
+ from ww.validation import is_positive_int
14
+
15
+ _T = TypeVar("_T")
16
+
17
+
18
+ def _from_path(function: Callable[[Any], _T], value: Any, path: str) -> _T:
19
+ try:
20
+ return function(value)
21
+ except (TypeError, ValueError) as error:
22
+ raise ValueError(f"{path}: {error}") from error
23
+
24
+
25
+ def _variables(value: Any, context: str) -> tuple[tuple[str, str], ...]:
26
+ if not isinstance(value, dict) or not all(
27
+ isinstance(name, str) and isinstance(item, str) for name, item in value.items()
28
+ ):
29
+ raise ValueError(f"{context} must be a mapping of strings")
30
+ return tuple(value.items())
31
+
32
+
33
+ def _positive_int_mapping(value: Any, context: str) -> tuple[tuple[str, int], ...]:
34
+ if not isinstance(value, dict) or not all(
35
+ isinstance(name, str) and is_positive_int(item) for name, item in value.items()
36
+ ):
37
+ raise ValueError(f"{context} must be a mapping of positive integers")
38
+ return tuple(value.items())