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.
- ww/__init__.py +18 -0
- ww/_bundled_extensions/ww/git/extension.py +1728 -0
- ww/action_execution.py +887 -0
- ww/actions/__init__.py +94 -0
- ww/actions/command.py +444 -0
- ww/actions/contracts.py +699 -0
- ww/actions/extension.py +197 -0
- ww/actions/mcp.py +84 -0
- ww/actions/prompt.py +74 -0
- ww/actions/skill.py +62 -0
- ww/actions/slash_command.py +63 -0
- ww/agents.py +151 -0
- ww/amendments.py +54 -0
- ww/artifacts.py +93 -0
- ww/assessments.py +181 -0
- ww/assets/__init__.py +2 -0
- ww/assets/agent_instructions.md +49 -0
- ww/assets/docs/examples.md +879 -0
- ww/assets/docs/features.md +4639 -0
- ww/assets/docs/specification.md +1876 -0
- ww/assets/noww_skill.md +11 -0
- ww/assets/workflows/catchall.yaml +26 -0
- ww/assets/workflows/onboarding.yaml +586 -0
- ww/assets/workflows/scriptize.yaml +130 -0
- ww/assets/ww-automate_skill.md +23 -0
- ww/assets/ww-deduce-feedback_skill.md +38 -0
- ww/assets/ww-feedback-rules_skill.md +48 -0
- ww/assets/ww-learn-project_skill.md +22 -0
- ww/assets/ww-refresh_skill.md +26 -0
- ww/assets/ww-rule_skill.md +83 -0
- ww/assets/ww-rules-from-artifacts_skill.md +22 -0
- ww/assets/ww-scriptize_skill.md +33 -0
- ww/assets/ww-setup_skill.md +94 -0
- ww/assets/ww-solve_skill.md +23 -0
- ww/assets/ww-suggest_skill.md +32 -0
- ww/assets/ww-wizard_skill.md +105 -0
- ww/assets/ww_skill.md +59 -0
- ww/assignments.py +283 -0
- ww/bootstrap.py +405 -0
- ww/builtin_workflows.py +215 -0
- ww/changes.py +225 -0
- ww/child_coordination.py +482 -0
- ww/children.py +106 -0
- ww/claude_permissions.py +115 -0
- ww/cli/__init__.py +7 -0
- ww/cli/__main__.py +6 -0
- ww/cli/audit.py +129 -0
- ww/cli/catalogs.py +131 -0
- ww/cli/discover.py +607 -0
- ww/cli/initialization.py +898 -0
- ww/cli/lookup.py +287 -0
- ww/cli/main.py +1768 -0
- ww/cli/parser.py +1200 -0
- ww/cli/prompts.py +217 -0
- ww/cli/updates.py +117 -0
- ww/completion_artifacts.py +156 -0
- ww/completion_inputs.py +39 -0
- ww/config/__init__.py +582 -0
- ww/config/actions.py +591 -0
- ww/config/composition.py +571 -0
- ww/config/rules.py +511 -0
- ww/config/steps.py +1220 -0
- ww/config/values.py +223 -0
- ww/config_files.py +191 -0
- ww/config_writes.py +264 -0
- ww/contracts.py +155 -0
- ww/control.py +41 -0
- ww/defaults.py +130 -0
- ww/design_docs.py +32 -0
- ww/discovery.py +104 -0
- ww/documents.py +217 -0
- ww/errors.py +18 -0
- ww/executable.py +43 -0
- ww/execution_models/__init__.py +64 -0
- ww/execution_models/construction.py +148 -0
- ww/execution_models/decoding.py +38 -0
- ww/execution_models/plan_codec.py +565 -0
- ww/execution_models/records.py +1206 -0
- ww/execution_models/runs.py +266 -0
- ww/extensions/__init__.py +40 -0
- ww/extensions/api.py +559 -0
- ww/extensions/registry.py +864 -0
- ww/extensions/store.py +78 -0
- ww/feedback.py +342 -0
- ww/handler_repairs.py +57 -0
- ww/hooks/__init__.py +40 -0
- ww/hooks/agents.py +380 -0
- ww/hooks/install.py +168 -0
- ww/hooks/notices.py +206 -0
- ww/hooks/records.py +209 -0
- ww/hooks/runtime.py +266 -0
- ww/hooks/transcripts.py +183 -0
- ww/inspect.py +896 -0
- ww/instructions/__init__.py +17 -0
- ww/instructions/builder.py +1682 -0
- ww/instructions/commands.py +335 -0
- ww/instructions/handoff.py +149 -0
- ww/instructions/models.py +686 -0
- ww/instructions/policy.py +219 -0
- ww/instructions/text.py +168 -0
- ww/interactions.py +187 -0
- ww/interpolation.py +37 -0
- ww/item_passes.py +167 -0
- ww/items.py +99 -0
- ww/locking.py +207 -0
- ww/metadata_publication.py +230 -0
- ww/onboarding.py +229 -0
- ww/open_work.py +236 -0
- ww/operations.py +193 -0
- ww/operator_ui/__init__.py +16 -0
- ww/operator_ui/page.html +351 -0
- ww/operator_ui/server.py +215 -0
- ww/operator_ui/session.py +389 -0
- ww/operator_ui/sheet.py +104 -0
- ww/operator_ui/view.py +109 -0
- ww/output.py +339 -0
- ww/output_adapters/__init__.py +12 -0
- ww/output_adapters/base.py +25 -0
- ww/output_adapters/json_adapter.py +37 -0
- ww/output_adapters/markdown.py +2293 -0
- ww/output_adapters/rule_pages.py +337 -0
- ww/output_adapters/terminal.py +21 -0
- ww/package_updates.py +167 -0
- ww/plan/__init__.py +38 -0
- ww/plan/actions.py +207 -0
- ww/plan/compiler.py +1492 -0
- ww/plan/constructs.py +456 -0
- ww/plan/models.py +665 -0
- ww/project_config.py +752 -0
- ww/recovery.py +401 -0
- ww/replanning.py +367 -0
- ww/results.py +77 -0
- ww/rule_checks.py +230 -0
- ww/rule_conversion.py +331 -0
- ww/rule_disputes.py +148 -0
- ww/rule_store.py +456 -0
- ww/rule_verification.py +714 -0
- ww/rule_views.py +447 -0
- ww/rule_writes.py +920 -0
- ww/run_coordination.py +158 -0
- ww/runtimes.py +105 -0
- ww/service.py +4405 -0
- ww/setup_apply.py +428 -0
- ww/step_values.py +20 -0
- ww/storage.py +447 -0
- ww/storage_adapters/__init__.py +36 -0
- ww/storage_adapters/base.py +540 -0
- ww/storage_adapters/filesystem.py +370 -0
- ww/storage_adapters/memory.py +195 -0
- ww/storage_adapters/project_metadata.py +69 -0
- ww/storage_adapters/task_document.py +484 -0
- ww/task_ids.py +114 -0
- ww/task_references.py +124 -0
- ww/transitions.py +1619 -0
- ww/updates.py +399 -0
- ww/upgrade.py +95 -0
- ww/validation.py +168 -0
- ww/variables.py +275 -0
- ww/workflow_config.py +854 -0
- ww/workflow_update.py +239 -0
- ww/workflow_validation.py +1260 -0
- ww/workspace.py +50 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/METADATA +690 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/RECORD +167 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/WHEEL +4 -0
- ww_agentic_workflows-1.0.0.dev3.dist-info/entry_points.txt +2 -0
- 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
|