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.
Files changed (33) hide show
  1. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/PKG-INFO +2 -2
  2. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/pyproject.toml +8 -3
  3. agent_workflow_ui-1.3.0/src/agent_workflow_ui/SKILL.awf-supervisor.md +174 -0
  4. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/__init__.py +1 -1
  5. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/__main__.py +6 -1
  6. agent_workflow_ui-1.3.0/src/agent_workflow_ui/agents_md.py +188 -0
  7. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/opencode_config.py +7 -15
  8. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/server.py +4 -0
  9. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/skill_installer.py +43 -13
  10. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/awf.py +298 -61
  11. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/PKG-INFO +2 -2
  12. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/SOURCES.txt +2 -0
  13. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/requires.txt +1 -1
  14. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/README.md +0 -0
  15. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/setup.cfg +0 -0
  16. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/SKILL.md +0 -0
  17. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/browser.py +0 -0
  18. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/config.py +0 -0
  19. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/http_endpoint.py +0 -0
  20. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/__init__.py +0 -0
  21. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/default_templates/ack.html.j2 +0 -0
  22. {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
  23. {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
  24. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/engine.py +0 -0
  25. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/render/frontmatter.py +0 -0
  26. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/roles_processor.py +0 -0
  27. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/state.py +0 -0
  28. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/__init__.py +0 -0
  29. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/forms.py +0 -0
  30. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui/tools/templates.py +0 -0
  31. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/dependency_links.txt +0 -0
  32. {agent_workflow_ui-1.2.0 → agent_workflow_ui-1.3.0}/src/agent_workflow_ui.egg-info/entry_points.txt +0 -0
  33. {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.2.0
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.2.0
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.2.0"
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.2.0",
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 = ["render/default_templates/*.j2", "SKILL.md"]
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
+ подтверждения владельца.
@@ -1,4 +1,4 @@
1
1
  """agent-workflow-ui — MCP plugin for visual interaction in opencode workflows."""
2
2
  # AUD16-07: must match [project] version in agent_workflow_ui/pyproject.toml —
3
3
  # tests/unit/test_version_sync.py pins both.
4
- __version__ = "1.2.0"
4
+ __version__ = "1.3.0"
@@ -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
@@ -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
- Transliterates Cyrillic to Latin before stripping non-ASCII.
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
- lower = name.lower()
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)
@@ -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
- def _target_skill_path() -> Path:
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" / SKILLS_DIR_NAME / SKILL_FILENAME
40
+ return xdg_config_home() / "opencode" / "skills" / skills_dir / SKILL_FILENAME
36
41
 
37
42
 
38
- def ensure_skill_installed() -> bool:
39
- """Ensure SKILL.md is installed in opencode skills directory.
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 SKILL.md not found at %s — skipping install", 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 SKILL.md: %s", e)
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 SKILL.md → %s", target)
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 SKILL.md to %s: %s", target, e)
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)