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/task_references.py ADDED
@@ -0,0 +1,124 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ """Map what a person calls a task onto the task IDs this project uses.
3
+
4
+ People rarely type an ID the way ww stores it. With ``task_format:
5
+ FOOBAR-{{digit}}`` they say "12345" or "foobar-12345" and mean ``FOOBAR-12345``;
6
+ with tracker keys (``task_format: explicit``) "12345" usually means the one
7
+ existing task whose key ends in it. Resolution is deterministic and read-only:
8
+ it proposes, and the caller decides what to do with the answer.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ from collections.abc import Iterable
15
+ from dataclasses import dataclass
16
+
17
+ from ww.errors import StateError
18
+ from ww.task_ids import (
19
+ EXPLICIT_TASK_FORMAT,
20
+ GENERATED_TASK_PREFIX,
21
+ validate_task_id,
22
+ )
23
+
24
+ # A task format placeholder, e.g. "{{digit}}" in "TASK-{{digit}}".
25
+ _PLACEHOLDER = re.compile(r"\{\{(?:digit|timestamp|uuid)\}\}")
26
+ # The values each placeholder generates; a reference fills a slot only with one.
27
+ _SLOT_VALUES = {
28
+ # A counter, e.g. "42".
29
+ "{{digit}}": re.compile(r"\d+"),
30
+ # A 14-digit timestamp with an optional "-N" suffix, e.g. "20261001120000-2".
31
+ "{{timestamp}}": re.compile(r"\d{14}(?:-\d+)?"),
32
+ # A UUID with an optional "-N" suffix, e.g.
33
+ # "123e4567-e89b-12d3-a456-426614174000".
34
+ "{{uuid}}": re.compile(
35
+ r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?:-\d+)?",
36
+ re.IGNORECASE,
37
+ ),
38
+ }
39
+ # What separates a tracker's project key from the number people quote.
40
+ _SEPARATORS = ("-", "_", "/")
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class TaskResolution:
45
+ """The tasks a reference may mean.
46
+
47
+ ``matches`` are existing tasks, best first. ``proposed`` is the ID a new
48
+ task would get for this reference, when the reference can name one.
49
+ """
50
+
51
+ reference: str
52
+ matches: tuple[str, ...]
53
+ proposed: str | None
54
+
55
+ @property
56
+ def resolved(self) -> str | None:
57
+ """The one existing task the reference means, if it is unambiguous."""
58
+ return self.matches[0] if len(self.matches) == 1 else None
59
+
60
+
61
+ def resolve_task_reference(
62
+ reference: str, task_format: str | None, known: Iterable[str]
63
+ ) -> TaskResolution:
64
+ """Resolve ``reference`` against ``task_format`` and the ``known`` task IDs.
65
+
66
+ An exact ID, the same ID in other letter case, and the ID the task format
67
+ builds from the reference are strong matches, and the first found wins.
68
+ Only without one do existing IDs ending in the reference after a
69
+ separator count, so "12345" finds ``FOOBAR-12345`` but not ``FOOBAR-112345``.
70
+ """
71
+ text = reference.strip()
72
+ if not text:
73
+ raise StateError("a task reference must not be empty")
74
+ ids = tuple(known)
75
+ by_folded = {task_id.casefold(): task_id for task_id in ids}
76
+ formatted = _formatted(text, task_format)
77
+ strong = [
78
+ found
79
+ for candidate in (text, formatted)
80
+ if candidate is not None
81
+ and (found := by_folded.get(candidate.casefold())) is not None
82
+ ]
83
+ if strong:
84
+ return TaskResolution(text, (strong[0],), None)
85
+ folded = text.casefold()
86
+ weak = tuple(
87
+ task_id
88
+ for task_id in ids
89
+ if any(
90
+ task_id.casefold().endswith(separator + folded) for separator in _SEPARATORS
91
+ )
92
+ )
93
+ return TaskResolution(text, weak, _valid(formatted or text))
94
+
95
+
96
+ def _formatted(text: str, task_format: str | None) -> str | None:
97
+ """The ID ``task_format`` gives the reference, when it has one slot.
98
+
99
+ A reference already carrying the format's prefix keeps its own value and
100
+ takes the format's spelling of the prefix.
101
+ """
102
+ if task_format == EXPLICIT_TASK_FORMAT:
103
+ return None
104
+ template = task_format or GENERATED_TASK_PREFIX + "{{timestamp}}"
105
+ slots = _PLACEHOLDER.findall(template)
106
+ if len(slots) != 1:
107
+ return None
108
+ prefix, suffix = _PLACEHOLDER.split(template)
109
+ value = text
110
+ if prefix and value.casefold().startswith(prefix.casefold()):
111
+ value = value[len(prefix) :]
112
+ if suffix and value.casefold().endswith(suffix.casefold()):
113
+ value = value[: -len(suffix)]
114
+ if not _SLOT_VALUES[slots[0]].fullmatch(value):
115
+ return None
116
+ return f"{prefix}{value}{suffix}"
117
+
118
+
119
+ def _valid(task_id: str) -> str | None:
120
+ try:
121
+ validate_task_id(task_id)
122
+ except StateError:
123
+ return None
124
+ return task_id