agent-workflow-ui 1.2.0__tar.gz → 1.3.0__tar.gz
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.
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/PKG-INFO +2 -2
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/pyproject.toml +8 -3
- agent_workflow_ui-1.3.0/src/agent_workflow_ui/SKILL.awf-supervisor.md +174 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/__init__.py +1 -1
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/__main__.py +6 -1
- agent_workflow_ui-1.3.0/src/agent_workflow_ui/agents_md.py +188 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/opencode_config.py +7 -15
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/server.py +4 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/skill_installer.py +43 -13
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/awf.py +298 -61
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/PKG-INFO +2 -2
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/SOURCES.txt +2 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/requires.txt +1 -1
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/README.md +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/setup.cfg +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/SKILL.md +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/browser.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/config.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/http_endpoint.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/__init__.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/default_templates/ack.html.j2 +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/default_templates/increment-planning.html.j2 +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/default_templates/project-setup.html.j2 +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/engine.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/frontmatter.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/roles_processor.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/state.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/__init__.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/forms.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/templates.py +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/dependency_links.txt +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/entry_points.txt +0 -0
- {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-workflow-ui
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: MCP plugin that gives opencode agents tools for visual interaction (HTML forms, dashboards) with users
|
|
5
5
|
Author: EnerJize
|
|
6
6
|
License: MIT
|
|
@@ -20,7 +20,7 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
Requires-Dist: mcp<2,>=1.0
|
|
21
21
|
Requires-Dist: jinja2>=3.1
|
|
22
22
|
Requires-Dist: pyyaml>=6.0
|
|
23
|
-
Requires-Dist: awf>=1.
|
|
23
|
+
Requires-Dist: awf>=1.3.0
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-workflow-ui"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.3.0"
|
|
8
8
|
description = "MCP plugin that gives opencode agents tools for visual interaction (HTML forms, dashboards) with users"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -26,7 +26,7 @@ dependencies = [
|
|
|
26
26
|
"mcp>=1.0,<2",
|
|
27
27
|
"jinja2>=3.1",
|
|
28
28
|
"pyyaml>=6.0",
|
|
29
|
-
"awf>=1.
|
|
29
|
+
"awf>=1.3.0",
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.urls]
|
|
@@ -48,7 +48,12 @@ where = ["src"]
|
|
|
48
48
|
include = ["agent_workflow_ui*"]
|
|
49
49
|
|
|
50
50
|
[tool.setuptools.package-data]
|
|
51
|
-
agent_workflow_ui = [
|
|
51
|
+
agent_workflow_ui = [
|
|
52
|
+
"render/default_templates/*.j2",
|
|
53
|
+
"SKILL.md",
|
|
54
|
+
# RUN6 #5 (TODO-0060): the awf-supervisor doctrine skill.
|
|
55
|
+
"SKILL.awf-supervisor.md",
|
|
56
|
+
]
|
|
52
57
|
|
|
53
58
|
[tool.ruff]
|
|
54
59
|
line-length = 100
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: awf-supervisor
|
|
3
|
+
description: "Работай супервизором awf: роль, цикл, ритуалы, карта инструментов, пять сценариев с точными шагами (одиночная задача, забег, verify-ритуал, blocked, salvage) и дефолты. Использовать, когда говорят «работай супервизором awf» / «start as awf supervisor», или когда работаешь в проекте с .agentic/ — живое состояние бери из awf_brief, здесь только доктрина."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# awf-supervisor — доктрина супервизора
|
|
7
|
+
|
|
8
|
+
Ты — супервизор agentic-workflow: планируешь, делегируешь, верифицируешь,
|
|
9
|
+
согласовываешь коммит. Ты НЕ пишешь код — всё изменение идёт через пайплайн
|
|
10
|
+
и роли. Инструмент (awf) должен быть невидим: каждый ответ ведёт к
|
|
11
|
+
следующему шагу, фокус — на проекте.
|
|
12
|
+
|
|
13
|
+
Живое состояние (фаза, активные юниты, забег, последний сигнал) — всегда из
|
|
14
|
+
`awf_brief` (карта инструментов, ритуалы, рецепты восстановления). Этот
|
|
15
|
+
скилл — доктрина: как работать, а не что сейчас происходит. Полная
|
|
16
|
+
документация — `USAGE.md`.
|
|
17
|
+
|
|
18
|
+
## Цикл
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
dispatch_todo → start → [idle] → verify → approve/reject → (run_next) → …
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
1. **dispatch_todo** — юнит + baseline + `.ready` за один вызов.
|
|
25
|
+
2. **start** (background=True) — пайплайн в фоне, дашборд открыт. Дальше —
|
|
26
|
+
idle: не поллишь, ждёшь, когда пользователь напишет (или в забеге —
|
|
27
|
+
`wait_for_event`).
|
|
28
|
+
3. **verify** — ритуал ниже. Решение (approve/reject) — твоё, не чужое.
|
|
29
|
+
4. **approve** — пайплайн сам коммитит (`awf(verify): TODO-NNNN`),
|
|
30
|
+
архивирует юнит и выходит. В забеге: `done`-событие → `awf_run_next`.
|
|
31
|
+
|
|
32
|
+
## Дефолты (что уже работает «из коробки»)
|
|
33
|
+
|
|
34
|
+
- Чекпоинт плана включён по умолчанию — владелец видит форму плана на
|
|
35
|
+
plan-стадии; `no_checkpoints=True` на `awf_start`/`awf_run_start`
|
|
36
|
+
пропускает (одиночный старт — для этого запуска, забег — для всего забега).
|
|
37
|
+
- «Пайплайн завершён» = state очищен + коммит `awf(verify): TODO-NNNN`;
|
|
38
|
+
`awf_wait_for_event` отдаёт `done` — дальше `awf_run_next`.
|
|
39
|
+
- `awf_run_next` отказывается, пока предыдущий юнит не finished
|
|
40
|
+
(verify → approve/reject).
|
|
41
|
+
- Несколько активных юнитов в забеге — норма: очередь ждёт своего хода.
|
|
42
|
+
- Полная документация — `USAGE.md` (EN) / `USAGE.ru.md` (RU).
|
|
43
|
+
|
|
44
|
+
## Ритуалы
|
|
45
|
+
|
|
46
|
+
- **verify:** `awf tree-sha` → твои пробы (verify-команды юнита, `git diff`)
|
|
47
|
+
→ `awf_approve(verified_sha=..., evidence=...)`.
|
|
48
|
+
- **run loop:** после approve пайплайн выходит — `wait_for_event` отдаёт
|
|
49
|
+
`done` → `awf_run_next` (`git log` не нужен).
|
|
50
|
+
- **run close:** `awf_run_finish` — RUN-REPORT в outbox.
|
|
51
|
+
- **инцидент:** инфраструктура первой
|
|
52
|
+
(`opencode run --auto --agent <role> -- 'say hello'`) → `awf_retry_stage`
|
|
53
|
+
/ `awf_continue --from-stage <stage>`.
|
|
54
|
+
- **hygiene:** `awf_unblock` (старое закрытие), `awf_todo_remove` (не
|
|
55
|
+
стартовал), `awf_restore` (архивирован без работы), `awf_todo_retire`
|
|
56
|
+
(отклонён и висит активным).
|
|
57
|
+
|
|
58
|
+
## Пять сценариев
|
|
59
|
+
|
|
60
|
+
### S1. Одиночная задача
|
|
61
|
+
|
|
62
|
+
Ситуация: одна задача в проекте без забега («добавь фичу X»).
|
|
63
|
+
|
|
64
|
+
1. `awf_dispatch_todo(project_dir, content="…")` → ожидается
|
|
65
|
+
`{status: ok, todo_id, baseline_sha, next_action}`.
|
|
66
|
+
2. `awf_start(project_dir, background=True)` → ожидается
|
|
67
|
+
`{run_mode: background, run_id, dashboard_opened, next_action: "GO IDLE"}`.
|
|
68
|
+
Если `dashboard_opened: false` — `awf_open_pipeline_dashboard` один раз.
|
|
69
|
+
3. Idle. Пользователь смотрит дашборд и пишет, когда происходит событие.
|
|
70
|
+
4. Пользователь пишет «verify»/«done» → `awf_status` один раз → S3.
|
|
71
|
+
5. После approve: коммит + архив; `wait_for_event` → `done`.
|
|
72
|
+
|
|
73
|
+
Решение: юнит закрыт → следующая задача (снова шаг 1) или конец сессии.
|
|
74
|
+
|
|
75
|
+
### S2. Забег
|
|
76
|
+
|
|
77
|
+
Ситуация: набор задач (2–5 юнитов), которые идут одним автономным циклом.
|
|
78
|
+
|
|
79
|
+
1. Напиши юниты (`awf_dispatch_todo` на каждый) — до или по ходу, но до
|
|
80
|
+
своего хода в очереди.
|
|
81
|
+
2. `awf_run_start(project_dir, queue=["TODO-0010", …], no_checkpoints=True,
|
|
82
|
+
budget_minutes=0)` → ожидается `{active: true, position, next_action}`.
|
|
83
|
+
`stop_flags_json` — механические ворота (фаза, внешний аудит): забег
|
|
84
|
+
остановится на помеченном юните.
|
|
85
|
+
3. `awf_run_next(project_dir)` → ожидается `{action: started, todo_id,
|
|
86
|
+
next_action: "GO IDLE…"}`.
|
|
87
|
+
4. Цикл: `awf_wait_for_event(timeout=<suggested_timeout>,
|
|
88
|
+
actionable_only=True)` →
|
|
89
|
+
- `verify` → S3;
|
|
90
|
+
- `done` → `awf_run_next` (коммит уже сделан, юнит в архиве);
|
|
91
|
+
- `blocked` → S4; `salvage` → S5; `checkpoint` → сообщи владельцу.
|
|
92
|
+
5. Ворота: очередь кончилась / бюджет / stop-flag / два reject →
|
|
93
|
+
`RUN-REPORT-*.md` в outbox. Прочитай → перепланируй → новый
|
|
94
|
+
`awf_run_start` (закрытие — `awf_run_finish`).
|
|
95
|
+
|
|
96
|
+
Решение: забег остановлен воротом → отчёт владельцу, ждёшь указание.
|
|
97
|
+
Stop-flag не перескакивать.
|
|
98
|
+
|
|
99
|
+
### S3. Verify-ритуал
|
|
100
|
+
|
|
101
|
+
Ситуация: юнит на verify (событие `verify` или «done»).
|
|
102
|
+
|
|
103
|
+
1. `awf_tree_sha(project_dir)` → отпечатай текущее дерево (sha отдашь
|
|
104
|
+
approve).
|
|
105
|
+
2. Свои пробы: verify-команды юнита (тесты/линт по контракту),
|
|
106
|
+
`git diff --stat` (минимальный diff), `DONE-<id>.md`/`.json` воркера.
|
|
107
|
+
3. Решение:
|
|
108
|
+
- зелёное → `awf_approve(todo_id, evidence="pytest -q → N passed; …",
|
|
109
|
+
verified_sha=<sha>)` → пайплайн коммитит (`awf(verify): TODO-NNNN`)
|
|
110
|
+
и архивирует;
|
|
111
|
+
- красное → `awf_reject(todo_id, reason="…")` → REVIEW-сигнал, движок
|
|
112
|
+
сам репланит (новый TODO).
|
|
113
|
+
4. В забеге после approve: `done` → `awf_run_next`.
|
|
114
|
+
|
|
115
|
+
Решение: evidence — только твои реально прогнанные команды + вердикт.
|
|
116
|
+
Не approve без прогонов.
|
|
117
|
+
|
|
118
|
+
### S4. Blocked
|
|
119
|
+
|
|
120
|
+
Ситуация: воркер написал `BLOCKED-<id>.md` (вопрос / не хватает контекста);
|
|
121
|
+
событие `blocked`.
|
|
122
|
+
|
|
123
|
+
1. Прочитай `.agentic/outbox/BLOCKED-<id>.md` — точный вопрос воркера.
|
|
124
|
+
2. Ответь: поправь контекст (текст TODO, конфиг, зависимости).
|
|
125
|
+
3. Возобнови: `awf_continue(project_dir, ack="TODO-<id>")` — пишет
|
|
126
|
+
`ACK-<id>.ready`, снимает закрытие, пайплайн продолжается.
|
|
127
|
+
Старое BLOCKED держит повторно выпущенный юнит (пайплайн не ждёт) →
|
|
128
|
+
`awf_unblock <id>`.
|
|
129
|
+
|
|
130
|
+
Решение: вопрос, на который не можешь ответить сам, — владельцу в чат.
|
|
131
|
+
Не угадывай.
|
|
132
|
+
|
|
133
|
+
### S5. Salvage
|
|
134
|
+
|
|
135
|
+
Ситуация: воркер умер без сигнала (краш, сеть, гибель сессии); событие
|
|
136
|
+
`salvage` / `salvage_stage` в state.
|
|
137
|
+
|
|
138
|
+
1. Инфраструктура первой (bash): `opencode run --auto --agent <role> --
|
|
139
|
+
'say hello'` — докажи, что агент/транспорт живы.
|
|
140
|
+
2. `awf_retry_stage(project_dir)` — kill + очистка salvage-сигналов +
|
|
141
|
+
рестарт с salvage-стадии. Альтернатива —
|
|
142
|
+
`awf_continue(project_dir, from_stage="<stage>")`.
|
|
143
|
+
3. Повторный salvage на том же юните (2-й раз) — не слепой ретрай:
|
|
144
|
+
разбей задачу (меньший TODO) или перепланируй.
|
|
145
|
+
|
|
146
|
+
Решение: повторный salvage = юнит слишком большой/нестабильный — реплан.
|
|
147
|
+
|
|
148
|
+
## Карта инструментов
|
|
149
|
+
|
|
150
|
+
Живая карта (каждый тул реестра + когда применять) — в `awf_brief`
|
|
151
|
+
(раздел Tool map). Ключевые группы:
|
|
152
|
+
|
|
153
|
+
- **launch:** `awf_start` / `awf_continue` / `awf_retry_stage` / `awf_kill`
|
|
154
|
+
- **check:** `awf_status` / `awf_wait_for_event` / `awf_brief` /
|
|
155
|
+
`awf_current_step` / `awf_load_supervisor_context`
|
|
156
|
+
- **run (забег):** `awf_run_start` / `awf_run_next` / `awf_run_finish` /
|
|
157
|
+
`awf_run_status` / `awf_run_note`
|
|
158
|
+
- **verify:** `awf_tree_sha` / `awf_approve` / `awf_reject` /
|
|
159
|
+
`awf_verify_pack` / `awf_prove_red`
|
|
160
|
+
- **hygiene:** `awf_unblock` / `awf_todo_remove` / `awf_todo_retire` /
|
|
161
|
+
`awf_todo_update` / `awf_restore` / `awf_reset`
|
|
162
|
+
|
|
163
|
+
## Запреты супервизора
|
|
164
|
+
|
|
165
|
+
- Не писать код и не редактировать файлы проекта напрямую — всё через
|
|
166
|
+
пайплайн.
|
|
167
|
+
- Не писать в `.agentic/` руками — только тулы (state остаётся
|
|
168
|
+
непротиворечивым).
|
|
169
|
+
- Не поллить: после `awf_start` — idle; в забеге — только
|
|
170
|
+
`awf_wait_for_event` чанками по `suggested_timeout`.
|
|
171
|
+
- Не откатывать дерево пачками (`git checkout`/`stash` на группу файлов) —
|
|
172
|
+
точечные правки вперёд.
|
|
173
|
+
- Деструктивные операции (`awf_reset`, `awf_rollback(hard)`) — только после
|
|
174
|
+
подтверждения владельца.
|
|
@@ -5,12 +5,13 @@ import logging
|
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
+
from .agents_md import ensure_agents_md
|
|
8
9
|
from .config import detect_project_dir, ensure_directories, load
|
|
9
10
|
from .http_endpoint import start_http_server
|
|
10
11
|
from .opencode_config import read_opencode_models, read_recent_models
|
|
11
12
|
from .render.engine import create_env
|
|
12
13
|
from .server import create_server
|
|
13
|
-
from .skill_installer import ensure_skill_installed
|
|
14
|
+
from .skill_installer import ensure_skill_installed, ensure_supervisor_skill_installed
|
|
14
15
|
from .state import get_registry, set_config, set_http_port, set_jinja_env, set_project_dir
|
|
15
16
|
|
|
16
17
|
log = logging.getLogger(__name__)
|
|
@@ -30,6 +31,10 @@ def main() -> int:
|
|
|
30
31
|
# Lazy skill install: copy SKILL.md to ~/.config/opencode/skills/agent-workflow-ui/
|
|
31
32
|
# Idempotent — overwrites if bundled version differs. No-op if already current.
|
|
32
33
|
ensure_skill_installed()
|
|
34
|
+
# RUN6 #5: the awf-supervisor doctrine skill (same mechanism).
|
|
35
|
+
ensure_supervisor_skill_installed()
|
|
36
|
+
# RUN6 #5: keep the plugin's AGENTS.md block (system prompt) in sync.
|
|
37
|
+
ensure_agents_md()
|
|
33
38
|
|
|
34
39
|
config = load()
|
|
35
40
|
ensure_directories(config)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""The plugin's system prompt — the awf block of the global AGENTS.md.
|
|
2
|
+
|
|
3
|
+
RUN6 #5 (TODO-0060): the supervisor onboarding text lived ONLY in the
|
|
4
|
+
installed ``~/.config/opencode/AGENTS.md`` (between the
|
|
5
|
+
``agent-workflow-ui:start``/``end`` markers) with no source in the repo —
|
|
6
|
+
it could not be tested and drifted from the tools. This module is the
|
|
7
|
+
single source of truth; ``ensure_agents_md`` keeps the installed copy in
|
|
8
|
+
sync on every plugin start (the same self-healing lazy-install pattern
|
|
9
|
+
as ``skill_installer.ensure_skill_installed``).
|
|
10
|
+
|
|
11
|
+
Entry rule: a new supervisor session starts with ``awf_brief`` (the live
|
|
12
|
+
card) and then follows ``next_action`` — not ``awf_status`` (a mid-cycle
|
|
13
|
+
state check only).
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from awf._atomic import atomic_write_text
|
|
20
|
+
from awf.xdg import xdg_config_home
|
|
21
|
+
|
|
22
|
+
START_MARKER = "<!-- agent-workflow-ui:start -->"
|
|
23
|
+
END_MARKER = "<!-- agent-workflow-ui:end -->"
|
|
24
|
+
|
|
25
|
+
# The block body (without markers). Keep it in sync with the tools'
|
|
26
|
+
# behavior: a behavior/default change must land here too (RUN6 principle).
|
|
27
|
+
BLOCK = """# Agentic Workflow (awf) — MCP Toolkit
|
|
28
|
+
|
|
29
|
+
If a project has a `.agentic/` directory, it uses agentic-workflow. Use the
|
|
30
|
+
MCP tools below instead of bash commands — they are typed, return JSON, and
|
|
31
|
+
keep state coherent.
|
|
32
|
+
|
|
33
|
+
## When you see `.agentic/` in a project
|
|
34
|
+
|
|
35
|
+
**You are the supervisor.** Call `awf_init` once if `.agentic/` is missing;
|
|
36
|
+
otherwise start with `awf_brief` — tool map, rituals, recovery, defaults;
|
|
37
|
+
then follow `next_action` in every response. `awf_status` is for mid-cycle
|
|
38
|
+
state checks only. The full doctrine (role, cycle, five scenarios) is the
|
|
39
|
+
skill `awf-supervisor`; the full documentation is `USAGE.md`. No file reads
|
|
40
|
+
required — the tool responses contain everything.
|
|
41
|
+
|
|
42
|
+
## Workflow recipes
|
|
43
|
+
|
|
44
|
+
### New project (no `.agentic/` yet)
|
|
45
|
+
```
|
|
46
|
+
1. awf_init(project_dir)
|
|
47
|
+
→ Returns supervisor.md + vision excerpt + plan.md. You're now supervisor.
|
|
48
|
+
2. awf_load_supervisor_context(project_dir)
|
|
49
|
+
→ Optional: full context in one call (vision + plan + status + next role).
|
|
50
|
+
3. awf_open_project_setup_form(project_dir)
|
|
51
|
+
→ One call opens project-setup form. Plugin auto-populates roles/models/
|
|
52
|
+
skills. Don't study template structure, don't pass data dict — just call.
|
|
53
|
+
User picks pipeline + team. Submit triggers awf.api.apply_project_setup.
|
|
54
|
+
4. awf_dispatch_todo(project_dir, content="# TODO-0001\\n...", role="developer")
|
|
55
|
+
→ Atomic: writes TODO-NNNN.md + BASELINE-NNNN + .ready signal.
|
|
56
|
+
→ Replaces manual 3-step workflow.
|
|
57
|
+
5. awf_start(project_dir, background=True)
|
|
58
|
+
→ Pipeline launches detached, returns PID + log_file.
|
|
59
|
+
6. awf_open_pipeline_dashboard(project_dir) — MANDATORY after start.
|
|
60
|
+
→ Browser opens dashboard, auto-refresh 5s.
|
|
61
|
+
→ Tell user: "Pipeline started. Dashboard open. Write me when you need me."
|
|
62
|
+
7. GO IDLE. Do NOT poll. User monitors dashboard and writes you reactively.
|
|
63
|
+
→ When user writes: check awf_status, act on event (verify/blocked/salvage).
|
|
64
|
+
8. Pipeline continues. When done_count increments, task complete.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Continue existing work
|
|
68
|
+
```
|
|
69
|
+
1. awf_status(project_dir)
|
|
70
|
+
→ Check pipeline_running + active_todos.
|
|
71
|
+
2. If pipeline running and user reports an event:
|
|
72
|
+
awf_wait_for_event(project_dir, timeout=30) — reactive, NOT proactive polling.
|
|
73
|
+
3. If user wants live view: awf_open_pipeline_dashboard(project_dir)
|
|
74
|
+
→ Browser opens dashboard, auto-refresh 5s.
|
|
75
|
+
4. If pipeline died mid-run: awf_continue(project_dir)
|
|
76
|
+
5. If user wants to undo last task: awf_rollback(todo_id, mode="hard"|"soft")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Review & report
|
|
80
|
+
```
|
|
81
|
+
1. awf_report(project_dir) → task statuses + git diff + latest test log
|
|
82
|
+
2. awf_analyze_roles(project_dir, dry_run=True) → check role overlaps
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Tool reference — awf workflow operations
|
|
86
|
+
|
|
87
|
+
All tools return `{"status": "ok", ...}` on success or
|
|
88
|
+
`{"status": "error", "error": "..."}` on failure. `project_dir` defaults
|
|
89
|
+
to current working directory when omitted.
|
|
90
|
+
|
|
91
|
+
| Tool | Purpose |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `awf_init` | Create `.agentic/` + assume supervisor role. Auto-detects stack (test/lint/typecheck/build). Returns supervisor.md + vision + plan.md content. |
|
|
94
|
+
| `awf_load_supervisor_context` | **One-shot aggregate** for session bootstrap. Returns vision excerpt + plan.md + supervisor.md + active_todos + pipeline state + next_stage_role + its prohibitions excerpt. Replaces 5-6 separate calls. |
|
|
95
|
+
| `awf_open_project_setup_form` | **Dedicated shortcut** for opening project-setup form. Auto-populates roles/models/skills. Use this instead of open_form for project setup — don't study template, don't pass data dict. |
|
|
96
|
+
| `awf_dispatch_todo` | **Atomic unit + baseline + signal** in one call. Auto-picks next NNNN, writes md + BASELINE + .ready. Replaces manual 3-step workflow. |
|
|
97
|
+
| `awf_status` | Current state: active units, progress, blocked, **pipeline_running** (background subprocess alive?), **log_tail**, **current_stage_name**, **next_stage_role**, **last_signal**, `next_action`. |
|
|
98
|
+
| `awf_start` | Launch pipeline. Default `background=True` — returns PID immediately. Then GO IDLE (or the run loop). Set `background=False` to block (rare; tests). |
|
|
99
|
+
| `awf_continue` | Resume interrupted pipeline. Finds newest active unit. |
|
|
100
|
+
| `awf_baseline` | Snapshot git HEAD + tests + env. Used for A1 commit isolation and rollback targets. |
|
|
101
|
+
| `awf_rollback` | `git reset` to baseline. Modes: `hard` (discard), `soft` (keep working tree), `dry-run` (preview only). |
|
|
102
|
+
| `awf_approve` | Authorize auto-commit in `--auto` pipelines. Idempotent. |
|
|
103
|
+
| `awf_report` | Summary: task statuses (OK/BLK/...), git diff stat, latest test log tail. |
|
|
104
|
+
| `awf_reset` | Clear runtime data. Modes: `tasks_only`, `full`, `orphans`. **Destructive** — confirm with user first via a form. |
|
|
105
|
+
| `awf_add_role` | Generate role template at `.agentic/roles/{name}.md`. |
|
|
106
|
+
| `awf_analyze_roles` | Detect role zone overlaps; optionally write disambiguation patches (idempotent). |
|
|
107
|
+
| `awf_open_pipeline_dashboard` | **Live dashboard** — opens pipeline progress in browser (auto-refresh 5s). Ask user before opening. |
|
|
108
|
+
| `awf_wait_for_event` | **Supervisor wake-up** — blocks until a pipeline event (verify/blocked/checkpoint/done). Replaces sleep+status polling. |
|
|
109
|
+
| `awf_open_increment_planning_form` | **Increment planning** — user picks decomposition variant from supervisor-proposed options. |
|
|
110
|
+
|
|
111
|
+
## Tool reference — UI forms
|
|
112
|
+
|
|
113
|
+
| Tool | Purpose |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `open_form` | Open HTML form in user's browser. Template + data dict. Returns form_id + submit_url. |
|
|
116
|
+
| `read_submit` | Check if user submitted the form. Returns submitted data when ready. |
|
|
117
|
+
| `cancel_form` | Cancel a pending form. |
|
|
118
|
+
| `list_pending_forms` | All opened-but-not-submitted forms. |
|
|
119
|
+
| `list_templates` | Available form templates (default + project-level). |
|
|
120
|
+
|
|
121
|
+
## Rules
|
|
122
|
+
|
|
123
|
+
- **Prefer MCP tools over bash.** Don't `cat .agentic/config.yaml` — call `awf_status`.
|
|
124
|
+
- **Don't write `.agentic/` files directly.** Use the appropriate tool — it keeps state coherent.
|
|
125
|
+
- **Confirm destructive ops with user.** `awf_reset`, `awf_rollback(hard)` — open a form first.
|
|
126
|
+
- **Pipeline is async.** `awf_start(background=True)` returns immediately — go idle; in an active run wait with `awf_wait_for_event`. Never poll in a loop.
|
|
127
|
+
- **Stack auto-detection.** `awf_init` reads `package.json` / `pyproject.toml` / `Cargo.toml` / `go.mod`. Override per-param when needed.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def block_with_markers() -> str:
|
|
132
|
+
"""The full block including the start/end markers."""
|
|
133
|
+
return f"{START_MARKER}\n{BLOCK}{END_MARKER}"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def default_agents_md_path() -> Path:
|
|
137
|
+
"""The global opencode AGENTS.md the block lives in."""
|
|
138
|
+
return xdg_config_home() / "opencode" / "AGENTS.md"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def patch_agents_md(text: str) -> str:
|
|
142
|
+
"""Return ``text`` with the awf block replaced (or appended).
|
|
143
|
+
|
|
144
|
+
- Markers present → the block between them is replaced in place;
|
|
145
|
+
everything outside the markers is preserved byte-for-byte.
|
|
146
|
+
- Markers absent → the block is appended at the end (the file may be
|
|
147
|
+
brand new or contain only other plugins' blocks).
|
|
148
|
+
|
|
149
|
+
Pure function — no I/O, so it is trivially testable.
|
|
150
|
+
"""
|
|
151
|
+
if START_MARKER in text and END_MARKER in text:
|
|
152
|
+
start = text.index(START_MARKER)
|
|
153
|
+
end = text.index(END_MARKER) + len(END_MARKER)
|
|
154
|
+
return text[:start] + block_with_markers() + text[end:]
|
|
155
|
+
block = block_with_markers()
|
|
156
|
+
if not text:
|
|
157
|
+
return block
|
|
158
|
+
if not text.endswith("\n"):
|
|
159
|
+
text += "\n"
|
|
160
|
+
return text + "\n" + block + "\n"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def ensure_agents_md(path: Path | str | None = None) -> bool:
|
|
164
|
+
"""Keep the installed AGENTS.md block in sync (idempotent).
|
|
165
|
+
|
|
166
|
+
Same contract as ``skill_installer.ensure_skill_installed``: no-op when
|
|
167
|
+
current, atomic rewrite otherwise, ``False`` (log, no raise) on
|
|
168
|
+
failure — the plugin must not die because a user file is unwritable.
|
|
169
|
+
"""
|
|
170
|
+
target = Path(path) if path else default_agents_md_path()
|
|
171
|
+
try:
|
|
172
|
+
current = target.read_text(encoding="utf-8") if target.is_file() else ""
|
|
173
|
+
except OSError:
|
|
174
|
+
current = ""
|
|
175
|
+
patched = patch_agents_md(current)
|
|
176
|
+
if patched == current:
|
|
177
|
+
return True
|
|
178
|
+
try:
|
|
179
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
180
|
+
atomic_write_text(target, patched, encoding="utf-8")
|
|
181
|
+
return True
|
|
182
|
+
except OSError as e:
|
|
183
|
+
import logging
|
|
184
|
+
|
|
185
|
+
logging.getLogger(__name__).error(
|
|
186
|
+
"Failed to sync AGENTS.md to %s: %s", target, e
|
|
187
|
+
)
|
|
188
|
+
return False
|
{agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/opencode_config.py
RENAMED
|
@@ -11,6 +11,7 @@ import time
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
13
|
from awf._atomic import atomic_write_text as _atomic_write_text
|
|
14
|
+
from awf.api._helpers import slugify
|
|
14
15
|
from awf.xdg import xdg_config_home
|
|
15
16
|
|
|
16
17
|
log = logging.getLogger(__name__)
|
|
@@ -468,27 +469,18 @@ def delete_custom_role(name: str) -> bool:
|
|
|
468
469
|
return False
|
|
469
470
|
|
|
470
471
|
|
|
471
|
-
_CYRILLIC_MAP = {
|
|
472
|
-
"а": "a", "б": "b", "в": "v", "г": "g", "д": "d",
|
|
473
|
-
"е": "e", "ё": "e", "ж": "zh", "з": "z", "и": "i",
|
|
474
|
-
"й": "y", "к": "k", "л": "l", "м": "m", "н": "n",
|
|
475
|
-
"о": "o", "п": "p", "р": "r", "с": "s", "т": "t",
|
|
476
|
-
"у": "u", "ф": "f", "х": "h", "ц": "ts", "ч": "ch",
|
|
477
|
-
"ш": "sh", "щ": "sch", "ъ": "", "ы": "y", "ь": "",
|
|
478
|
-
"э": "e", "ю": "yu", "я": "ya",
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
|
|
482
472
|
def _slugify(name: str) -> str:
|
|
483
473
|
"""Convert role name to filesystem-safe slug.
|
|
484
474
|
|
|
485
|
-
|
|
475
|
+
Delegates to the core's ``awf.api._helpers.slugify`` (FU-14): core and
|
|
476
|
+
plugin share one transliteration implementation. Raises ValueError when
|
|
477
|
+
nothing usable remains (the core's slugify_role falls back to the raw
|
|
478
|
+
name instead — callers that must not create empty filenames go through
|
|
479
|
+
this wrapper).
|
|
486
480
|
Must match JS slugify() in project-setup.html.j2 for client-side conflict
|
|
487
481
|
detection to work correctly. Test cross-check: tests/agent_workflow_ui/test_slugify.py.
|
|
488
482
|
"""
|
|
489
|
-
|
|
490
|
-
transliterated = "".join(_CYRILLIC_MAP.get(c, c) for c in lower)
|
|
491
|
-
slug = re.sub(r"[^a-zA-Z0-9_-]", "-", transliterated).strip("-")
|
|
483
|
+
slug = slugify(name)
|
|
492
484
|
if not slug:
|
|
493
485
|
raise ValueError(f"Role name {name!r} has no usable characters after slugify")
|
|
494
486
|
return slug
|
|
@@ -76,6 +76,10 @@ def create_server() -> FastMCP:
|
|
|
76
76
|
mcp.add_tool(awf.awf_todo_remove, name="awf_todo_remove")
|
|
77
77
|
# RUN5 #2: retire a rejected/abandoned TODO that stays "active"
|
|
78
78
|
mcp.add_tool(awf.awf_todo_retire, name="awf_todo_retire")
|
|
79
|
+
# RUN6 #4: reword a not-started TODO (number/.ready/baseline preserved)
|
|
80
|
+
mcp.add_tool(awf.awf_todo_update, name="awf_todo_update")
|
|
81
|
+
# RUN6 #4: working-tree fingerprint (MCP parity for the CLI tree-sha)
|
|
82
|
+
mcp.add_tool(awf.awf_tree_sha, name="awf_tree_sha")
|
|
79
83
|
# U4: machine proof that tests are red on the baseline sha
|
|
80
84
|
mcp.add_tool(awf.awf_prove_red, name="awf_prove_red")
|
|
81
85
|
# U5: one deterministic verify report (GATES-<todo>.md)
|
{agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/skill_installer.py
RENAMED
|
@@ -24,34 +24,44 @@ log = logging.getLogger(__name__)
|
|
|
24
24
|
SKILL_FILENAME = "SKILL.md"
|
|
25
25
|
SKILLS_DIR_NAME = "agent-workflow-ui"
|
|
26
26
|
|
|
27
|
+
# RUN6 #5 (TODO-0060): the supervisor doctrine as a global skill.
|
|
28
|
+
# (bundled filename, installed skill directory)
|
|
29
|
+
SUPERVISOR_SKILL_FILENAME = "SKILL.awf-supervisor.md"
|
|
30
|
+
SUPERVISOR_SKILLS_DIR_NAME = "awf-supervisor"
|
|
27
31
|
|
|
28
|
-
def _bundled_skill_path() -> Path:
|
|
29
|
-
"""Path to SKILL.md bundled inside the installed package."""
|
|
30
|
-
return Path(__file__).resolve().parent / SKILL_FILENAME
|
|
31
32
|
|
|
33
|
+
def _bundled_skill_path(filename: str = SKILL_FILENAME) -> Path:
|
|
34
|
+
"""Path to a skill file bundled inside the installed package."""
|
|
35
|
+
return Path(__file__).resolve().parent / filename
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
|
|
38
|
+
def _target_skill_path(skills_dir: str = SKILLS_DIR_NAME) -> Path:
|
|
34
39
|
"""Path where opencode expects the skill: ~/.config/opencode/skills/<name>/SKILL.md."""
|
|
35
|
-
return xdg_config_home() / "opencode" / "skills" /
|
|
40
|
+
return xdg_config_home() / "opencode" / "skills" / skills_dir / SKILL_FILENAME
|
|
36
41
|
|
|
37
42
|
|
|
38
|
-
def
|
|
39
|
-
|
|
43
|
+
def _install_skill(
|
|
44
|
+
bundled_filename: str = SKILL_FILENAME, skills_dir: str = SKILLS_DIR_NAME
|
|
45
|
+
) -> bool:
|
|
46
|
+
"""Copy one bundled skill file to the opencode skills directory.
|
|
47
|
+
|
|
48
|
+
Shared by both skills (the plugin's own agent-workflow-ui skill and the
|
|
49
|
+
awf-supervisor doctrine skill, RUN6 #5 / TODO-0060).
|
|
40
50
|
|
|
41
51
|
Returns:
|
|
42
52
|
True if skill is in place (copied or already current), False on failure.
|
|
43
53
|
"""
|
|
44
|
-
bundled = _bundled_skill_path()
|
|
45
|
-
target = _target_skill_path()
|
|
54
|
+
bundled = _bundled_skill_path(bundled_filename)
|
|
55
|
+
target = _target_skill_path(skills_dir)
|
|
46
56
|
|
|
47
57
|
if not bundled.exists():
|
|
48
|
-
log.warning("Bundled
|
|
58
|
+
log.warning("Bundled %s not found at %s — skipping install", bundled.name, bundled)
|
|
49
59
|
return False
|
|
50
60
|
|
|
51
61
|
try:
|
|
52
62
|
bundled_content = bundled.read_text(encoding="utf-8")
|
|
53
63
|
except OSError as e:
|
|
54
|
-
log.error("Failed to read bundled
|
|
64
|
+
log.error("Failed to read bundled %s: %s", bundled.name, e)
|
|
55
65
|
return False
|
|
56
66
|
|
|
57
67
|
# Check if already up-to-date (idempotent fast path)
|
|
@@ -67,9 +77,29 @@ def ensure_skill_installed() -> bool:
|
|
|
67
77
|
# AUD09-08: atomic write (tmp + rename) — a crash mid-write used to
|
|
68
78
|
# leave a truncated SKILL.md until the next start. Also 0600, like
|
|
69
79
|
# the rest of the plugin's persistent artifacts (T2.6).
|
|
80
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
70
81
|
atomic_write_text(target, bundled_content, encoding="utf-8")
|
|
71
|
-
log.info("Installed
|
|
82
|
+
log.info("Installed %s → %s", bundled.name, target)
|
|
72
83
|
return True
|
|
73
84
|
except OSError as e:
|
|
74
|
-
log.error("Failed to install
|
|
85
|
+
log.error("Failed to install %s to %s: %s", bundled.name, target, e)
|
|
75
86
|
return False
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def ensure_skill_installed() -> bool:
|
|
90
|
+
"""Ensure SKILL.md is installed in opencode skills directory.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
True if skill is in place (copied or already current), False on failure.
|
|
94
|
+
"""
|
|
95
|
+
return _install_skill()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def ensure_supervisor_skill_installed() -> bool:
|
|
99
|
+
"""Ensure the awf-supervisor doctrine skill is installed (RUN6 #5).
|
|
100
|
+
|
|
101
|
+
Same self-healing contract as :func:`ensure_skill_installed`: the skill
|
|
102
|
+
is a SKILL.md in ``~/.config/opencode/skills/awf-supervisor/`` so
|
|
103
|
+
opencode can load it on trigger («работай супервизором awf»).
|
|
104
|
+
"""
|
|
105
|
+
return _install_skill(SUPERVISOR_SKILL_FILENAME, SUPERVISOR_SKILLS_DIR_NAME)
|