@sakanano-moe/agenthelm 6.7.0 → 6.11.0-rc.1
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.
- package/package.json +1 -1
- package/runtime/index.json +6 -6
- package/runtime/plugins/agent-pack/.codex-plugin/plugin.json +1 -1
- package/runtime/plugins/agent-pack/hooks/hooks.json +32 -0
- package/runtime/plugins/agent-pack/plugin.json +1 -1
- package/runtime/plugins/agent-pack/references/local-only.md +126 -0
- package/runtime/plugins/agent-pack/references/nudge.en.md +58 -0
- package/runtime/plugins/agent-pack/references/nudge.md +58 -0
- package/runtime/plugins/agent-pack/runtime/agent_pack_nudge.py +573 -0
- package/runtime/plugins/agent-pack/runtime/agenthelm.py +419 -107
- package/runtime/plugins/agent-pack/runtime/agenthelm_runtime.py +77 -53
- package/runtime/plugins/agent-pack/runtime/i18n_agent_pack.py +78 -0
- package/runtime/plugins/agent-pack/runtime/local_only.py +1130 -0
- package/runtime/plugins/agent-pack/runtime/locale/en.json +385 -0
- package/runtime/plugins/agent-pack/runtime/mcp_server.py +25 -0
- package/runtime/plugins/agent-pack/runtime/nudge_text.py +107 -0
- package/runtime/plugins/agent-pack/skills/agent-pack/SKILL.md +9 -1
- package/runtime/plugins/agents-init/.codex-plugin/plugin.json +1 -1
- package/runtime/plugins/agents-init/content.json +17 -2
- package/runtime/plugins/agents-init/plugin.json +1 -1
- package/runtime/plugins/agents-init/skills/agents-init/AGENT.template.md +47 -0
- package/runtime/plugins/agents-init/skills/agents-init/SKILL.md +39 -1
- package/runtime/plugins/agents-init/skills/agents-init/project.schema.json +404 -70
- package/runtime/plugins/agents-init/skills/agents-init/scripts/adopt.py +596 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/i18n_init.py +78 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/init_project.py +245 -72
- package/runtime/plugins/agents-init/skills/agents-init/scripts/locale/en.json +210 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/regions.py +99 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/scaffold.py +1081 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/test_adopt.py +551 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/test_init_project.py +46 -2
- package/runtime/plugins/agents-init/skills/agents-init/scripts/test_scaffold.py +555 -0
- package/runtime/plugins/agents-init/skills/agents-init/skeleton.json +66 -0
- package/runtime/plugins/agents-init/skills/agents-init/skill.json +1 -1
- package/runtime/plugins/opinion-manager/.codex-plugin/plugin.json +1 -1
- package/runtime/plugins/opinion-manager/plugin.json +1 -1
- package/runtime/plugins/opinion-manager/references/inbox.en.md +48 -0
- package/runtime/plugins/opinion-manager/references/inbox.md +48 -0
- package/runtime/plugins/opinion-manager/runtime/context.py +19 -18
- package/runtime/plugins/opinion-manager/runtime/i18n_opinion.py +78 -0
- package/runtime/plugins/opinion-manager/runtime/inbox.py +397 -0
- package/runtime/plugins/opinion-manager/runtime/inbox_text.py +101 -0
- package/runtime/plugins/opinion-manager/runtime/lifecycle.py +45 -44
- package/runtime/plugins/opinion-manager/runtime/locale/en.json +184 -0
- package/runtime/plugins/opinion-manager/runtime/opinion_manager.py +55 -50
- package/runtime/plugins/opinion-manager/runtime/versioned.py +84 -83
- package/runtime/plugins/opinion-manager/skills/opinion-manager/SKILL.md +15 -2
- package/runtime/plugins/project-orchestrator/.codex-plugin/plugin.json +1 -1
- package/runtime/plugins/project-orchestrator/content.json +30 -3
- package/runtime/plugins/project-orchestrator/plugin.json +1 -1
- package/runtime/plugins/project-orchestrator/references/case-workspaces.md +9 -1
- package/runtime/plugins/project-orchestrator/references/case.schema.json +1 -0
- package/runtime/plugins/project-orchestrator/references/sop-interview.md +83 -0
- package/runtime/plugins/project-orchestrator/references/sop.md +156 -0
- package/runtime/plugins/project-orchestrator/runtime/assets.py +52 -57
- package/runtime/plugins/project-orchestrator/runtime/case.py +139 -80
- package/runtime/plugins/project-orchestrator/runtime/case_structure.py +71 -0
- package/runtime/plugins/project-orchestrator/runtime/case_workspace.py +47 -46
- package/runtime/plugins/project-orchestrator/runtime/i18n_orchestrator.py +78 -0
- package/runtime/plugins/project-orchestrator/runtime/locale/en.json +1409 -0
- package/runtime/plugins/project-orchestrator/runtime/memory.py +44 -43
- package/runtime/plugins/project-orchestrator/runtime/private_paths.py +119 -0
- package/runtime/plugins/project-orchestrator/runtime/project_base.py +37 -19
- package/runtime/plugins/project-orchestrator/runtime/project_cli.py +267 -174
- package/runtime/plugins/project-orchestrator/runtime/project_demo.py +130 -48
- package/runtime/plugins/project-orchestrator/runtime/project_maintain.py +524 -134
- package/runtime/plugins/project-orchestrator/runtime/project_menu.py +159 -85
- package/runtime/plugins/project-orchestrator/runtime/project_real.py +191 -106
- package/runtime/plugins/project-orchestrator/runtime/project_write.py +58 -55
- package/runtime/plugins/project-orchestrator/runtime/sop.py +1670 -0
- package/runtime/plugins/project-orchestrator/runtime/sop_structure.py +226 -0
- package/runtime/plugins/project-orchestrator/runtime/wf.py +74 -70
- package/runtime/plugins/project-orchestrator/skills/project-orchestrator/SKILL.md +22 -0
- package/runtime/plugins/project-orchestrator/sop-templates/blank/SOP.en.md +57 -0
- package/runtime/plugins/project-orchestrator/sop-templates/blank/SOP.md +57 -0
- package/runtime/plugins/project-orchestrator/sop-templates/knowledge-digest/SOP.en.md +77 -0
- package/runtime/plugins/project-orchestrator/sop-templates/knowledge-digest/SOP.md +77 -0
- package/runtime/plugins/project-orchestrator/sop-templates/knowledge-digest/templates/digest.en.md +11 -0
- package/runtime/plugins/project-orchestrator/sop-templates/knowledge-digest/templates/digest.md +11 -0
- package/runtime/plugins/project-orchestrator/sop-templates/video-to-transcript/SOP.en.md +87 -0
- package/runtime/plugins/project-orchestrator/sop-templates/video-to-transcript/SOP.md +87 -0
- package/runtime/plugins/project-orchestrator/sop-templates/weekly-review/SOP.en.md +75 -0
- package/runtime/plugins/project-orchestrator/sop-templates/weekly-review/SOP.md +75 -0
- package/runtime/plugins/project-orchestrator/sop-templates/weekly-review/templates/review.en.md +11 -0
- package/runtime/plugins/project-orchestrator/sop-templates/weekly-review/templates/review.md +12 -0
- package/runtime/plugins/project-orchestrator/templates/CASE.en.md +39 -0
|
@@ -8,21 +8,23 @@ import re
|
|
|
8
8
|
|
|
9
9
|
import project_cli as cli
|
|
10
10
|
from project_cli import S, EntryError
|
|
11
|
+
from i18n_orchestrator import _, N_ # noqa: E402
|
|
11
12
|
|
|
12
13
|
MENU = (
|
|
13
|
-
("1", "找资料", "按关键词、日期、类型查找并打开", "find"),
|
|
14
|
-
("2", "看进展", "浏览事项、下一动作与等待对象", "progress"),
|
|
15
|
-
("3", "项目指南", "这个项目做什么、资料在哪、怎样开始", "guide"),
|
|
16
|
-
("4", "检查项目与环境", "依赖、插件、记录与索引", "health"),
|
|
14
|
+
("1", N_("找资料"), N_("按关键词、日期、类型查找并打开"), "find"),
|
|
15
|
+
("2", N_("看进展"), N_("浏览事项、下一动作与等待对象"), "progress"),
|
|
16
|
+
("3", N_("项目指南"), N_("这个项目做什么、资料在哪、怎样开始"), "guide"),
|
|
17
|
+
("4", N_("检查项目与环境"), N_("依赖、插件、记录与索引"), "health"),
|
|
17
18
|
None,
|
|
18
|
-
("5", "修复与更新", "重建索引、安装与更新", "fix"),
|
|
19
|
-
("6", "记录事项", "新建、追加记录、更新状态", "record"),
|
|
20
|
-
("7", "项目工具", "项目自行声明的操作", "tools"),
|
|
19
|
+
("5", N_("修复与更新"), N_("重建索引、安装与更新"), "fix"),
|
|
20
|
+
("6", N_("记录事项"), N_("新建、追加记录、更新状态"), "record"),
|
|
21
|
+
("7", N_("项目工具"), N_("项目自行声明的操作"), "tools"),
|
|
22
|
+
("8", N_("流程(SOP)"), N_("自己定义的步骤序列:查看、检查所需工具、按它执行"), "sop"),
|
|
21
23
|
)
|
|
22
24
|
ACTION_RE = re.compile(r"^([ofcvr])\s*(\d+)$")
|
|
23
|
-
RECORD_ACTIONS = (("1", "追加一条记录", "log"), ("2", "更新下一动作", "next_action"),
|
|
24
|
-
("3", "改为等待(填写在等谁或等什么)", "waiting"), ("4", "关闭(标为已完成)", "close"),
|
|
25
|
-
("5", "恢复(标为进行中)", "reopen"), ("6", "登记资料或交付物", "deliverable"))
|
|
25
|
+
RECORD_ACTIONS = (("1", N_("追加一条记录"), "log"), ("2", N_("更新下一动作"), "next_action"),
|
|
26
|
+
("3", N_("改为等待(填写在等谁或等什么)"), "waiting"), ("4", N_("关闭(标为已完成)"), "close"),
|
|
27
|
+
("5", N_("恢复(标为进行中)"), "reopen"), ("6", N_("登记资料或交付物"), "deliverable"))
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
class Back(Exception):
|
|
@@ -70,11 +72,11 @@ def sub(screen, *args) -> None:
|
|
|
70
72
|
pass
|
|
71
73
|
|
|
72
74
|
|
|
73
|
-
def confirm(prompt: str = "输入 yes 执行,回车取消") -> bool:
|
|
75
|
+
def confirm(prompt: str = _("输入 yes 执行,回车取消")) -> bool:
|
|
74
76
|
try:
|
|
75
77
|
return ask(prompt).lower() in ("yes", "y", "是")
|
|
76
78
|
except Back:
|
|
77
|
-
print(S.dim("已取消,没有写入。"))
|
|
79
|
+
print(S.dim(_("已取消,没有写入。")))
|
|
78
80
|
return False
|
|
79
81
|
|
|
80
82
|
|
|
@@ -88,25 +90,26 @@ def home(provider) -> None:
|
|
|
88
90
|
print()
|
|
89
91
|
for entry in MENU:
|
|
90
92
|
if entry is None:
|
|
91
|
-
print(S.dim(" ─────────── 维护"))
|
|
93
|
+
print(S.dim(_(" ─────────── 维护")))
|
|
92
94
|
continue
|
|
93
95
|
key, title, hint, capability = entry
|
|
94
|
-
|
|
96
|
+
title, hint = _(title), _(hint)
|
|
97
|
+
state = capabilities.get(capability) or {"state": "unavailable", "reason": _("演示项目没有这一项")}
|
|
95
98
|
if capability == "health" and any(health.values()):
|
|
96
|
-
hint = " · ".join(
|
|
99
|
+
hint = " · ".join(_("{0} 项{1}").format(health[k], _(label)) for k, label in cli.LEVELS if health.get(k))
|
|
97
100
|
if state["state"] != "available":
|
|
98
|
-
hint = S.yellow(
|
|
101
|
+
hint = S.yellow(_("不可用:{0}").format(state['reason']))
|
|
99
102
|
print(f" {S.bold(S.cyan(key))} {cli.pad(title, 16)}{S.dim(hint) if state['state'] == 'available' else hint}")
|
|
100
|
-
print(S.dim("\n 输入序号;也可以直接输入关键词查资料。 ? 帮助 · q 退出"))
|
|
103
|
+
print(S.dim(_("\n 输入序号;也可以直接输入关键词查资料。 ? 帮助 · q 退出")))
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
def find_screen(provider, first: str | None = None) -> None:
|
|
104
|
-
show(cli.header(provider, "找资料"))
|
|
105
|
-
print(S.dim("输入关键词,可带日期(2025-12、10月)与 type:pdf、in:片段;回车返回"))
|
|
107
|
+
show(cli.header(provider, _("找资料")))
|
|
108
|
+
print(S.dim(_("输入关键词,可带日期(2025-12、10月)与 type:pdf、in:片段;回车返回")))
|
|
106
109
|
result = None
|
|
107
110
|
pending = first
|
|
108
111
|
while True:
|
|
109
|
-
line, pending = (pending, None) if pending else (ask("查找"), None)
|
|
112
|
+
line, pending = (pending, None) if pending else (ask(_("查找")), None)
|
|
110
113
|
match = ACTION_RE.match(line) or re.match(r"^()(\d+)$", line)
|
|
111
114
|
if match and result is not None:
|
|
112
115
|
key, number = match.group(1) or "o", int(match.group(2))
|
|
@@ -116,7 +119,7 @@ def find_screen(provider, first: str | None = None) -> None:
|
|
|
116
119
|
fail(exc)
|
|
117
120
|
continue
|
|
118
121
|
if key == "v":
|
|
119
|
-
show(cli.render_versions(item) if item["versions"] else [S.dim("这份资料没有其他版本")])
|
|
122
|
+
show(cli.render_versions(item) if item["versions"] else [S.dim(_("这份资料没有其他版本"))])
|
|
120
123
|
elif key == "r":
|
|
121
124
|
show(cli.render_refs(provider.refs(item["path"])))
|
|
122
125
|
else:
|
|
@@ -134,8 +137,8 @@ def find_screen(provider, first: str | None = None) -> None:
|
|
|
134
137
|
result = provider.find(terms, ext=ext, container=container)
|
|
135
138
|
except EntryError as exc:
|
|
136
139
|
fail(exc)
|
|
137
|
-
print(S.dim("输入 5 可直接去「修复与更新」。"))
|
|
138
|
-
if ask("下一步") != "5":
|
|
140
|
+
print(S.dim(_("输入 5 可直接去「修复与更新」。")))
|
|
141
|
+
if ask(_("下一步")) != "5":
|
|
139
142
|
return
|
|
140
143
|
sub(fix_screen, provider, ["rebuild-index"] if exc.code == "index-broken" else None)
|
|
141
144
|
if provider.index_state()["status"] == "broken":
|
|
@@ -144,14 +147,14 @@ def find_screen(provider, first: str | None = None) -> None:
|
|
|
144
147
|
continue
|
|
145
148
|
show(cli.render_find(result))
|
|
146
149
|
if result["results"]:
|
|
147
|
-
print(S.dim("o1 打开 · f1 显示所在目录 · c1 复制路径 · v1 看版本 · r1 看引用 · 继续输入词再查 · 回车返回"))
|
|
150
|
+
print(S.dim(_("o1 打开 · f1 显示所在目录 · c1 复制路径 · v1 看版本 · r1 看引用 · 继续输入词再查 · 回车返回")))
|
|
148
151
|
|
|
149
152
|
|
|
150
153
|
def item_screen(provider, item: dict) -> None:
|
|
151
154
|
show(cli.render_item(item))
|
|
152
155
|
while True:
|
|
153
|
-
print(S.dim("o 打开权威记录 · d1 打开交付物 · f 显示所在目录 · w 为它记录 · 回车返回"))
|
|
154
|
-
line = ask("事项")
|
|
156
|
+
print(S.dim(_("o 打开权威记录 · d1 打开交付物 · f 显示所在目录 · w 为它记录 · 回车返回")))
|
|
157
|
+
line = ask(_("事项"))
|
|
155
158
|
if line == "o":
|
|
156
159
|
print(provider.act("open", item["path"])["message"])
|
|
157
160
|
elif line == "f":
|
|
@@ -161,17 +164,17 @@ def item_screen(provider, item: dict) -> None:
|
|
|
161
164
|
elif re.fullmatch(r"d\d+", line) and 1 <= int(line[1:]) <= len(item["deliverables"]):
|
|
162
165
|
print(provider.act("open", item["deliverables"][int(line[1:]) - 1])["message"])
|
|
163
166
|
else:
|
|
164
|
-
print(S.yellow("没有这个操作。"))
|
|
167
|
+
print(S.yellow(_("没有这个操作。")))
|
|
165
168
|
|
|
166
169
|
|
|
167
170
|
def progress_screen(provider) -> None:
|
|
168
171
|
term, expand = None, False
|
|
169
172
|
while True:
|
|
170
|
-
show(cli.header(provider, "看进展"))
|
|
173
|
+
show(cli.header(provider, _("看进展")))
|
|
171
174
|
lines, numbered = cli.render_items(provider.items(term=term), expand_closed=expand)
|
|
172
|
-
show(([S.dim(
|
|
173
|
-
print(S.dim("序号看详情 · 输入词查找(标题、别名、旧编号)· all 展开已完成 · 回车返回"))
|
|
174
|
-
line = ask("进展")
|
|
175
|
+
show(([S.dim(_("筛选:{0}(输入 * 清除)").format(term))] if term else []) + lines)
|
|
176
|
+
print(S.dim(_("序号看详情 · 输入词查找(标题、别名、旧编号)· all 展开已完成 · 回车返回")))
|
|
177
|
+
line = ask(_("进展"))
|
|
175
178
|
if line.isdigit() and 1 <= int(line) <= len(numbered):
|
|
176
179
|
sub(item_screen, provider, provider.item(numbered[int(line) - 1]["id"]))
|
|
177
180
|
elif line == "all":
|
|
@@ -185,33 +188,33 @@ def progress_screen(provider) -> None:
|
|
|
185
188
|
sub(item_screen, provider, provider.item(matches[0]["id"]))
|
|
186
189
|
term = None
|
|
187
190
|
elif not matches:
|
|
188
|
-
print(S.yellow(
|
|
191
|
+
print(S.yellow(_("没有事项匹配“{0}”。").format(line)))
|
|
189
192
|
term = None
|
|
190
193
|
else:
|
|
191
194
|
expand = True
|
|
192
195
|
|
|
193
196
|
|
|
194
197
|
def guide_screen(provider) -> None:
|
|
195
|
-
show(cli.header(provider, "项目指南") + cli.render_guide(provider.guide()))
|
|
196
|
-
ask("回车返回")
|
|
198
|
+
show(cli.header(provider, _("项目指南")) + cli.render_guide(provider.guide()))
|
|
199
|
+
ask(_("回车返回"))
|
|
197
200
|
|
|
198
201
|
|
|
199
202
|
def health_screen(provider) -> None:
|
|
200
|
-
show(cli.header(provider, "检查项目与环境"))
|
|
203
|
+
show(cli.header(provider, _("检查项目与环境")))
|
|
201
204
|
lines, numbered = cli.render_health(provider.health())
|
|
202
205
|
show(lines)
|
|
203
206
|
while True:
|
|
204
|
-
print(S.dim("序号去处理 · 回车返回"))
|
|
205
|
-
line = ask("检查")
|
|
207
|
+
print(S.dim(_("序号去处理 · 回车返回")))
|
|
208
|
+
line = ask(_("检查"))
|
|
206
209
|
if not (line.isdigit() and 1 <= int(line) <= len(numbered)):
|
|
207
|
-
print(S.yellow("没有这一项。"))
|
|
210
|
+
print(S.yellow(_("没有这一项。")))
|
|
208
211
|
continue
|
|
209
212
|
finding = numbered[int(line) - 1]
|
|
210
213
|
actions = [step["action"] for step in finding["next"] if step["action"]]
|
|
211
214
|
if not actions:
|
|
212
|
-
print(S.dim("这一项不能在菜单里直接处理:") + finding["next"][0]["label"])
|
|
215
|
+
print(S.dim(_("这一项不能在菜单里直接处理:")) + finding["next"][0]["label"])
|
|
213
216
|
continue
|
|
214
|
-
verb,
|
|
217
|
+
verb, _sep, target = actions[0].partition(":")
|
|
215
218
|
if verb == "fix":
|
|
216
219
|
sub(fix_screen, provider, [target])
|
|
217
220
|
elif verb == "open":
|
|
@@ -219,64 +222,82 @@ def health_screen(provider) -> None:
|
|
|
219
222
|
|
|
220
223
|
|
|
221
224
|
def fix_screen(provider, preset: list[str] | None = None) -> None:
|
|
222
|
-
show(cli.header(provider, "修复与更新"))
|
|
223
|
-
print(S.dim("流程:检查 → 选择处理项 → 查看影响 → 确认执行 → 验证结果"))
|
|
225
|
+
show(cli.header(provider, _("修复与更新")))
|
|
226
|
+
print(S.dim(_("流程:检查 → 选择处理项 → 查看影响 → 确认执行 → 验证结果")))
|
|
224
227
|
available = provider.fix_plan([])["steps"]
|
|
225
228
|
if not available:
|
|
226
|
-
print(S.green("检查完成:没有需要处理的项。"))
|
|
229
|
+
print(S.green(_("检查完成:没有需要处理的项。")))
|
|
227
230
|
return
|
|
228
231
|
if preset:
|
|
229
232
|
ids = [step["id"] for step in available if step["id"] in preset]
|
|
230
233
|
titles = [step["title"] for step in available if step["id"] in ids]
|
|
231
|
-
print(S.bold("1–2. 检查并选中:") + ("、".join(titles) or S.yellow("这一项已处理或当前不可处理")))
|
|
234
|
+
print(S.bold(_("1–2. 检查并选中:")) + (_("、").join(titles) or S.yellow(_("这一项已处理或当前不可处理"))))
|
|
232
235
|
if not ids:
|
|
233
236
|
return
|
|
234
237
|
else:
|
|
235
|
-
print(S.bold("1. 检查结果"))
|
|
238
|
+
print(S.bold(_("1. 检查结果")))
|
|
236
239
|
for number, step in enumerate(available, 1):
|
|
237
|
-
blocked = S.yellow(
|
|
240
|
+
blocked = S.yellow(_("({0})").format(step['blocked'])) if step["blocked"] else ""
|
|
238
241
|
print(f" {S.bold(S.cyan(str(number)))} {step['title']}{blocked}")
|
|
239
|
-
line = ask("2. 选择处理项(序号,空格分隔;all 全选)")
|
|
242
|
+
line = ask(_("2. 选择处理项(序号,空格分隔;all 全选)"))
|
|
240
243
|
picks = range(1, len(available) + 1) if line == "all" else \
|
|
241
244
|
[int(token) for token in line.split() if token.isdigit() and 1 <= int(token) <= len(available)]
|
|
242
245
|
ids = [available[number - 1]["id"] for number in picks]
|
|
243
246
|
if not ids:
|
|
244
|
-
print(S.yellow("没有选中任何项。"))
|
|
247
|
+
print(S.yellow(_("没有选中任何项。")))
|
|
245
248
|
return
|
|
246
249
|
choices: dict[str, str] = {}
|
|
247
250
|
if "add-entry" in ids and not provider.overview().get("launcher"):
|
|
248
|
-
name = choose("启动器文件名(回车用 menu)", "")
|
|
251
|
+
name = choose(_("启动器文件名(回车用 menu)"), "")
|
|
249
252
|
if name:
|
|
250
253
|
choices["entry-name"] = name
|
|
251
254
|
plan = provider.fix_plan(ids, dict(choices))
|
|
252
|
-
print(S.bold("3. 查看影响"))
|
|
255
|
+
print(S.bold(_("3. 查看影响")))
|
|
253
256
|
show(cli.render_plan(plan))
|
|
254
257
|
asked = False
|
|
255
258
|
for step in plan["steps"]:
|
|
256
259
|
for item in step["custom"]:
|
|
257
260
|
if step["blocked"]:
|
|
258
261
|
continue
|
|
259
|
-
|
|
262
|
+
if item.get("options"):
|
|
263
|
+
choices[item["path"]] = _decide_item(item, step["payload"].get("diffs", {}).get(item["path"], ""))
|
|
264
|
+
if choices[item["path"]] == "skip" and "_reason" not in choices:
|
|
265
|
+
choices["_reason"] = choose(_("永久跳过的理由(回车略过;会随项目同步,换一台机器也看得到)"), "")
|
|
266
|
+
asked = True
|
|
267
|
+
continue
|
|
268
|
+
answer = choose(_("{0} {1}:k 不改(默认)/ a 采用").format(item['path'], item['state']), "k") \
|
|
260
269
|
if item["path"] == "README.md" else \
|
|
261
|
-
choose(
|
|
270
|
+
choose(_("{0} {1}:k 保留你的版本(默认)/ a 采用新模板").format(item['path'], item['state']), "k")
|
|
262
271
|
choices[item["path"]] = "accept" if answer == "a" else "keep"
|
|
263
272
|
asked = True
|
|
264
273
|
if asked:
|
|
265
274
|
plan = provider.fix_plan(ids, choices)
|
|
266
|
-
print(S.bold("按你的选择,最终将:"))
|
|
275
|
+
print(S.bold(_("按你的选择,最终将:")))
|
|
267
276
|
show(cli.render_plan(plan, choices))
|
|
268
277
|
if all(step["blocked"] for step in plan["steps"]):
|
|
269
|
-
print(S.yellow("所选项目前都不能执行,只能预览。"))
|
|
278
|
+
print(S.yellow(_("所选项目前都不能执行,只能预览。")))
|
|
270
279
|
return
|
|
271
|
-
print(S.bold("4. 确认执行"))
|
|
280
|
+
print(S.bold(_("4. 确认执行")))
|
|
272
281
|
if not confirm():
|
|
273
282
|
return
|
|
274
|
-
print(S.bold("5. 验证结果"))
|
|
283
|
+
print(S.bold(_("5. 验证结果")))
|
|
275
284
|
show(cli.render_apply(provider.apply_fix(plan, choices)))
|
|
276
285
|
|
|
277
286
|
|
|
287
|
+
def _decide_item(item: dict, diff: str) -> str:
|
|
288
|
+
"""每项四选一:采用 / 永久跳过 / 稍后 / 查看差异(看完再选)。"""
|
|
289
|
+
while True:
|
|
290
|
+
answer = choose(_("{0}({1}):a 采用(默认)/ s 永久跳过 / l 稍后 / d 查看差异").format(item['path'], item['state']), "a")
|
|
291
|
+
if answer == "d":
|
|
292
|
+
print(S.dim(diff or _("(没有差异内容可显示)")))
|
|
293
|
+
continue
|
|
294
|
+
if answer in ("a", "s", "l"):
|
|
295
|
+
return {"a": "accept", "s": "skip", "l": "later"}[answer]
|
|
296
|
+
print(S.yellow(_("请输入 a、s、l 或 d。")))
|
|
297
|
+
|
|
298
|
+
|
|
278
299
|
def record_screen(provider, target: str | None = None) -> None:
|
|
279
|
-
show(cli.header(provider, "记录事项"))
|
|
300
|
+
show(cli.header(provider, _("记录事项")))
|
|
280
301
|
capability = next(item for item in provider.capabilities() if item["id"] == "record")
|
|
281
302
|
if capability["state"] != "available":
|
|
282
303
|
try:
|
|
@@ -285,12 +306,12 @@ def record_screen(provider, target: str | None = None) -> None:
|
|
|
285
306
|
fail(exc)
|
|
286
307
|
return
|
|
287
308
|
if target is None:
|
|
288
|
-
line = ask("n 新建 · 或输入事项(编号、名称、旧编号)")
|
|
309
|
+
line = ask(_("n 新建 · 或输入事项(编号、名称、旧编号)"))
|
|
289
310
|
target = None if line == "n" else line
|
|
290
311
|
if target is None:
|
|
291
|
-
change = {"title": ask("标题"), "goal": ask("目标")}
|
|
312
|
+
change = {"title": ask(_("标题")), "goal": ask(_("目标"))}
|
|
292
313
|
try:
|
|
293
|
-
change["next_action"] = ask("下一动作(回车跳过)")
|
|
314
|
+
change["next_action"] = ask(_("下一动作(回车跳过)"))
|
|
294
315
|
except Back:
|
|
295
316
|
change["next_action"] = ""
|
|
296
317
|
else:
|
|
@@ -299,12 +320,12 @@ def record_screen(provider, target: str | None = None) -> None:
|
|
|
299
320
|
except EntryError as exc:
|
|
300
321
|
fail(exc)
|
|
301
322
|
return
|
|
302
|
-
print(f"{S.bold(item['title'])} [{cli.GROUP_LABEL.get(item['group'], item['status'])}]")
|
|
303
|
-
for key, title,
|
|
304
|
-
print(f" {S.bold(S.cyan(key))} {title}")
|
|
305
|
-
choice = dict((key, field) for key,
|
|
323
|
+
print(f"{S.bold(item['title'])} [{_(cli.GROUP_LABEL.get(item['group'], item['status']))}]")
|
|
324
|
+
for key, title, _field in RECORD_ACTIONS:
|
|
325
|
+
print(f" {S.bold(S.cyan(key))} {_(title)}")
|
|
326
|
+
choice = dict((key, field) for key, _title, field in RECORD_ACTIONS).get(ask(_("要做什么")))
|
|
306
327
|
if choice is None:
|
|
307
|
-
print(S.yellow("没有这个操作。"))
|
|
328
|
+
print(S.yellow(_("没有这个操作。")))
|
|
308
329
|
return
|
|
309
330
|
target = item["id"]
|
|
310
331
|
if choice == "close":
|
|
@@ -312,20 +333,20 @@ def record_screen(provider, target: str | None = None) -> None:
|
|
|
312
333
|
elif choice == "reopen":
|
|
313
334
|
change = {"status": "active"}
|
|
314
335
|
if not item["next_action"]:
|
|
315
|
-
change["next_action"] = ask("恢复后的下一动作")
|
|
336
|
+
change["next_action"] = ask(_("恢复后的下一动作"))
|
|
316
337
|
elif choice == "waiting":
|
|
317
|
-
change = {"status": "waiting", "waiting_for": ask("在等谁或等什么")}
|
|
338
|
+
change = {"status": "waiting", "waiting_for": ask(_("在等谁或等什么"))}
|
|
318
339
|
elif choice == "deliverable":
|
|
319
|
-
change = {"add_deliverable": [ask("文件路径(相对项目根目录,可从「找资料」c1 复制)")]}
|
|
340
|
+
change = {"add_deliverable": [ask(_("文件路径(相对项目根目录,可从「找资料」c1 复制)"))]}
|
|
320
341
|
else:
|
|
321
|
-
change = {choice: ask("内容")}
|
|
342
|
+
change = {choice: ask(_("内容"))}
|
|
322
343
|
try:
|
|
323
344
|
preview = provider.record_preview(target, change)
|
|
324
345
|
except EntryError as exc:
|
|
325
346
|
fail(exc)
|
|
326
347
|
return
|
|
327
348
|
show(cli.render_preview(preview))
|
|
328
|
-
if not confirm("输入 yes 写入,回车取消"):
|
|
349
|
+
if not confirm(_("输入 yes 写入,回车取消")):
|
|
329
350
|
return
|
|
330
351
|
try:
|
|
331
352
|
print(S.green(provider.record_apply(preview)["verify"]))
|
|
@@ -335,33 +356,86 @@ def record_screen(provider, target: str | None = None) -> None:
|
|
|
335
356
|
|
|
336
357
|
def tools_screen(provider) -> None:
|
|
337
358
|
tools = provider.tools()
|
|
338
|
-
show(cli.header(provider, "项目工具") + cli.render_tools(tools))
|
|
359
|
+
show(cli.header(provider, _("项目工具")) + cli.render_tools(tools))
|
|
339
360
|
while tools:
|
|
340
|
-
line = ask("序号查看并执行 · 回车返回")
|
|
361
|
+
line = ask(_("序号查看并执行 · 回车返回"))
|
|
341
362
|
if not (line.isdigit() and 1 <= int(line) <= len(tools)):
|
|
342
|
-
print(S.yellow("没有这一项。"))
|
|
363
|
+
print(S.yellow(_("没有这一项。")))
|
|
343
364
|
continue
|
|
344
365
|
tool = tools[int(line) - 1]
|
|
345
366
|
if tool["state"] != "available":
|
|
346
|
-
print(S.yellow(
|
|
367
|
+
print(S.yellow(_("“{0}”当前不可用:{1}").format(tool['title'], tool['reason'])))
|
|
347
368
|
continue
|
|
348
369
|
show(cli.render_tools([tool]))
|
|
349
370
|
if confirm():
|
|
350
371
|
print(provider.run_tool(tool["id"])["message"])
|
|
351
372
|
if not tools:
|
|
352
|
-
ask("回车返回")
|
|
373
|
+
ask(_("回车返回"))
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def sop_screen(provider) -> None:
|
|
377
|
+
while True:
|
|
378
|
+
code, data, lines = provider.sop(["list", "--all"])
|
|
379
|
+
show(cli.header(provider, _("流程(SOP)")) + lines)
|
|
380
|
+
rows = data["sops"]
|
|
381
|
+
print(S.dim(_("序号看详情 · d 哪些到期了 · n 新建草稿 · 回车返回")))
|
|
382
|
+
line = ask(_("流程"))
|
|
383
|
+
if line == "d":
|
|
384
|
+
show(provider.sop(["due"])[2])
|
|
385
|
+
elif line == "n":
|
|
386
|
+
sub(sop_new_screen, provider)
|
|
387
|
+
elif line.isdigit() and 1 <= int(line) <= len(rows):
|
|
388
|
+
sub(sop_detail_screen, provider, rows[int(line) - 1]["id"])
|
|
389
|
+
else:
|
|
390
|
+
print(S.yellow(_("没有这一项。")))
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def sop_new_screen(provider) -> None:
|
|
394
|
+
slug = ask(_("英文短名(小写字母、数字、连字符)"))
|
|
395
|
+
template = choose(_("样例:blank 空白 / video-to-transcript 视频转文字 / weekly-review 周复盘(回车用 blank)"), "blank")
|
|
396
|
+
argv = ["new", "--slug", slug, "--template", template]
|
|
397
|
+
show(provider.sop(argv)[2])
|
|
398
|
+
if confirm():
|
|
399
|
+
show(provider.sop([*argv, "--apply"])[2])
|
|
400
|
+
print(S.dim(_("下一步:让 Agent 带你谈清触发、工具、出错和中间产物的约定,或运行 sop interview ") + slug))
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def sop_detail_screen(provider, sop_id: str) -> None:
|
|
404
|
+
show(provider.sop(["show", sop_id])[2])
|
|
405
|
+
actions = {"c": _("执行前检查"), "q": _("还要谈清的事"), "v": _("校验"), "r": _("按它执行(先预览)"), "a": _("启用草稿(先预览)")}
|
|
406
|
+
while True:
|
|
407
|
+
print(S.dim(" · ".join(f"{key} {label}" for key, label in actions.items()) + _(" · 回车返回")))
|
|
408
|
+
line = ask(_("流程"))
|
|
409
|
+
if line == "c":
|
|
410
|
+
show(provider.sop(["preflight", sop_id])[2])
|
|
411
|
+
elif line == "q":
|
|
412
|
+
show(provider.sop(["interview", sop_id])[2])
|
|
413
|
+
elif line == "v":
|
|
414
|
+
show(provider.sop(["validate", sop_id])[2])
|
|
415
|
+
elif line in ("r", "a"):
|
|
416
|
+
argv = ["run", sop_id] if line == "r" else ["activate", sop_id]
|
|
417
|
+
code, _unused, lines = provider.sop(argv)
|
|
418
|
+
show(lines)
|
|
419
|
+
if code == 0 and confirm():
|
|
420
|
+
show(provider.sop([*argv, "--apply"])[2])
|
|
421
|
+
else:
|
|
422
|
+
print(S.yellow(_("没有这个操作。")))
|
|
423
|
+
|
|
353
424
|
|
|
425
|
+
def help_text() -> str:
|
|
426
|
+
bold, cyan = S.bold, S.cyan
|
|
427
|
+
return (bold(_("怎么用")) + "\n"
|
|
428
|
+
+ _(" 首页输入序号进入;直接输入词就是找资料。各页回车返回上一级,q 随时退出。") + "\n"
|
|
429
|
+
+ _(" 找资料结果:{0} 打开 · {1} 显示所在目录 · {2} 复制路径 · {3} 看版本 · {4} 看引用").format(
|
|
430
|
+
cyan("o1"), cyan("f1"), cyan("c1"), cyan("v1"), cyan("r1")) + "\n"
|
|
431
|
+
+ _(" 写入类操作(修复、记录、工具)都先显示影响,输入 yes 才执行。") + "\n"
|
|
432
|
+
+ bold(_("不进菜单也可以")) + "\n"
|
|
433
|
+
+ _(" ./<启动器> find 词 · progress · show 事项 · guide · health · fix · record · tools · sop 子命令") + "\n"
|
|
434
|
+
+ _(" 加 --output json 得到结构化结果;脚本里从不等待输入。完整说明:--help"))
|
|
354
435
|
|
|
355
|
-
HELP = f"""{S.bold('怎么用')}
|
|
356
|
-
首页输入序号进入;直接输入词就是找资料。各页回车返回上一级,q 随时退出。
|
|
357
|
-
找资料结果:{S.cyan('o1')} 打开 · {S.cyan('f1')} 显示所在目录 · {S.cyan('c1')} 复制路径 · {S.cyan('v1')} 看版本 · {S.cyan('r1')} 看引用
|
|
358
|
-
写入类操作(修复、记录、工具)都先显示影响,输入 yes 才执行。
|
|
359
|
-
{S.bold('不进菜单也可以')}
|
|
360
|
-
./<启动器> find 词 · progress · show 事项 · guide · health · fix · record · tools
|
|
361
|
-
加 --output json 得到结构化结果;脚本里从不等待输入。完整说明:--help"""
|
|
362
436
|
|
|
363
437
|
SCREENS = {"1": find_screen, "2": progress_screen, "3": guide_screen, "4": health_screen,
|
|
364
|
-
"5": fix_screen, "6": record_screen, "7": tools_screen}
|
|
438
|
+
"5": fix_screen, "6": record_screen, "7": tools_screen, "8": sop_screen}
|
|
365
439
|
|
|
366
440
|
|
|
367
441
|
def run(provider) -> int:
|
|
@@ -373,14 +447,14 @@ def run(provider) -> int:
|
|
|
373
447
|
while True:
|
|
374
448
|
home(provider)
|
|
375
449
|
try:
|
|
376
|
-
line = ask("选择")
|
|
450
|
+
line = ask(_("选择"))
|
|
377
451
|
except Back:
|
|
378
452
|
continue
|
|
379
453
|
try:
|
|
380
454
|
if line in SCREENS:
|
|
381
455
|
SCREENS[line](provider)
|
|
382
456
|
elif line in ("?", "help", "帮助"):
|
|
383
|
-
print(
|
|
457
|
+
print(help_text())
|
|
384
458
|
else:
|
|
385
459
|
find_screen(provider, line)
|
|
386
460
|
except Back:
|