@sakanano-moe/agenthelm 6.5.3 → 6.6.0
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/bin/agenthelm.js +1 -1
- package/package.json +2 -2
- package/runtime/index.json +80 -0
- package/runtime/plugins/agent-pack/.codex-plugin/plugin.json +19 -0
- package/runtime/plugins/agent-pack/.mcp.json +12 -0
- package/runtime/plugins/agent-pack/hooks/hooks.json +16 -0
- package/runtime/plugins/agent-pack/mcp.json +14 -0
- package/runtime/plugins/agent-pack/plugin.json +20 -0
- package/runtime/plugins/agent-pack/references/capability-config.md +124 -0
- package/runtime/{agenthelm.py → plugins/agent-pack/runtime/agenthelm.py} +42 -1
- package/runtime/plugins/agent-pack/runtime/agenthelm_runtime.py +388 -0
- package/runtime/plugins/agent-pack/skills/agent-pack/SKILL.md +46 -0
- package/runtime/plugins/agents-init/.codex-plugin/plugin.json +22 -0
- package/runtime/plugins/agents-init/content.json +31 -0
- package/runtime/plugins/agents-init/plugin.json +19 -0
- package/runtime/plugins/agents-init/skills/agents-init/AGENT.template.md +376 -0
- package/runtime/plugins/agents-init/skills/agents-init/SKILL.md +119 -0
- package/runtime/plugins/agents-init/skills/agents-init/index.html +357 -0
- package/runtime/plugins/agents-init/skills/agents-init/launcher.sh +93 -0
- package/runtime/plugins/agents-init/skills/agents-init/project.schema.json +171 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/init_project.py +1190 -0
- package/runtime/plugins/agents-init/skills/agents-init/scripts/test_init_project.py +678 -0
- package/runtime/plugins/agents-init/skills/agents-init/skill.json +122 -0
- package/runtime/plugins/opinion-manager/.codex-plugin/plugin.json +26 -0
- package/runtime/plugins/opinion-manager/catalog/README.md +15 -0
- package/runtime/plugins/opinion-manager/catalog/rules/.gitkeep +0 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.align-requirements/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.assumptions-authority/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.batch-questions/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.bilingual-terms/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.calculations/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.chinese-response/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.clean-deliverable/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.complete-options/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.conclusion-first/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.concrete-titles/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.consistent-items/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.device-readable/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.editable-diagrams/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.evidence-inference/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.maintenance-chinese/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.maintenance-project-language/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.markdown-structure/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.portable-files/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.report-context/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.report-formats/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.report-review/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.response-brief/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.response-dense/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.rules-tools/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.semantic-wrap/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.solutions-proportional/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.solutions-three-level/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.tables-compact/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.tables-list-first/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.test-evidence/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/rules/om.versioned-files/versions/1.0.0.json +11 -0
- package/runtime/plugins/opinion-manager/catalog/templates/.gitkeep +0 -0
- package/runtime/plugins/opinion-manager/catalog/templates/delivery-standard/compact-tables/versions/1.0.0.json +21 -0
- package/runtime/plugins/opinion-manager/catalog/templates/delivery-standard/list-first/versions/1.0.0.json +21 -0
- package/runtime/plugins/opinion-manager/catalog/templates/development-standard/chinese-maintenance/versions/1.0.0.json +15 -0
- package/runtime/plugins/opinion-manager/catalog/templates/development-standard/project-language/versions/1.0.0.json +15 -0
- package/runtime/plugins/opinion-manager/catalog/templates/response-style/brief-chinese/versions/1.0.0.json +19 -0
- package/runtime/plugins/opinion-manager/catalog/templates/response-style/dense-chinese/versions/1.0.0.json +19 -0
- package/runtime/plugins/opinion-manager/catalog/templates/scenario/concise-project/versions/1.0.0.json +39 -0
- package/runtime/plugins/opinion-manager/catalog/templates/scenario/reviewed-project/versions/1.0.0.json +39 -0
- package/runtime/plugins/opinion-manager/catalog/templates/work-method/proportional/versions/1.0.0.json +20 -0
- package/runtime/plugins/opinion-manager/catalog/templates/work-method/three-level/versions/1.0.0.json +20 -0
- package/runtime/plugins/opinion-manager/content.json +63 -0
- package/runtime/plugins/opinion-manager/plugin.json +18 -0
- package/runtime/plugins/opinion-manager/references/lock.schema.json +14 -0
- package/runtime/plugins/opinion-manager/references/onboarding.md +57 -0
- package/runtime/plugins/opinion-manager/references/profile.schema.json +56 -0
- package/runtime/plugins/opinion-manager/references/rule.schema.json +16 -0
- package/runtime/plugins/opinion-manager/references/template.schema.json +71 -0
- package/runtime/plugins/opinion-manager/references/versioned-template.schema.json +19 -0
- package/runtime/plugins/opinion-manager/references/versioning.md +263 -0
- package/runtime/plugins/opinion-manager/runtime/constants.py +7 -0
- package/runtime/plugins/opinion-manager/runtime/context.py +180 -0
- package/runtime/plugins/opinion-manager/runtime/lifecycle.py +395 -0
- package/runtime/plugins/opinion-manager/runtime/opinion_manager.py +419 -0
- package/runtime/plugins/opinion-manager/runtime/versioned.py +705 -0
- package/runtime/plugins/opinion-manager/skills/opinion-manager/SKILL.md +62 -0
- package/runtime/plugins/opinion-manager/skills/opinion-manager/agents/openai.yaml +4 -0
- package/runtime/plugins/opinion-manager/templates/.gitkeep +1 -0
- package/runtime/plugins/project-orchestrator/.codex-plugin/plugin.json +18 -0
- package/runtime/plugins/project-orchestrator/content.json +37 -0
- package/runtime/plugins/project-orchestrator/plugin.json +19 -0
- package/runtime/plugins/project-orchestrator/references/asset-synonyms.example.json +8 -0
- package/runtime/plugins/project-orchestrator/references/case-workspaces.md +159 -0
- package/runtime/plugins/project-orchestrator/references/case.schema.json +31 -0
- package/runtime/plugins/project-orchestrator/runtime/assets.py +1288 -0
- package/runtime/plugins/project-orchestrator/runtime/case.py +557 -0
- package/runtime/plugins/project-orchestrator/runtime/case_workspace.py +350 -0
- package/runtime/plugins/project-orchestrator/runtime/memory.py +363 -0
- package/runtime/plugins/project-orchestrator/runtime/project_base.py +107 -0
- package/runtime/plugins/project-orchestrator/runtime/project_cli.py +599 -0
- package/runtime/plugins/project-orchestrator/runtime/project_demo.py +504 -0
- package/runtime/plugins/project-orchestrator/runtime/project_maintain.py +470 -0
- package/runtime/plugins/project-orchestrator/runtime/project_menu.py +394 -0
- package/runtime/plugins/project-orchestrator/runtime/project_real.py +522 -0
- package/runtime/plugins/project-orchestrator/runtime/project_write.py +329 -0
- package/runtime/plugins/project-orchestrator/runtime/wf.py +439 -0
- package/runtime/plugins/project-orchestrator/skills/project-orchestrator/SKILL.md +230 -0
- package/runtime/plugins/project-orchestrator/skills/project-orchestrator/agents/openai.yaml +4 -0
- package/runtime/plugins/project-orchestrator/templates/CASE.md +39 -0
- /package/runtime/{agent_pack_config.py → plugins/agent-pack/runtime/agent_pack_config.py} +0 -0
- /package/runtime/{mcp_env_launcher.py → plugins/agent-pack/runtime/mcp_env_launcher.py} +0 -0
- /package/runtime/{mcp_server.py → plugins/agent-pack/runtime/mcp_server.py} +0 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"""项目入口的交互菜单:按想完成的任务组织,每一步都能继续操作或回车返回。
|
|
2
|
+
|
|
3
|
+
只在终端里使用;脚本与 CI 请用 project_cli 的子命令,它们从不等待输入。
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
import project_cli as cli
|
|
10
|
+
from project_cli import S, EntryError
|
|
11
|
+
|
|
12
|
+
MENU = (
|
|
13
|
+
("1", "找资料", "按关键词、日期、类型查找并打开", "find"),
|
|
14
|
+
("2", "看进展", "浏览事项、下一动作与等待对象", "progress"),
|
|
15
|
+
("3", "项目指南", "这个项目做什么、资料在哪、怎样开始", "guide"),
|
|
16
|
+
("4", "检查项目与环境", "依赖、插件、记录与索引", "health"),
|
|
17
|
+
None,
|
|
18
|
+
("5", "修复与更新", "重建索引、安装与更新", "fix"),
|
|
19
|
+
("6", "记录事项", "新建、追加记录、更新状态", "record"),
|
|
20
|
+
("7", "项目工具", "项目自行声明的操作", "tools"),
|
|
21
|
+
)
|
|
22
|
+
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"))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Back(Exception):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Quit(Exception):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def ask(prompt: str) -> str:
|
|
37
|
+
try:
|
|
38
|
+
line = input(S.bold(S.cyan(f"\n{prompt} › "))).strip()
|
|
39
|
+
except EOFError:
|
|
40
|
+
raise Quit from None
|
|
41
|
+
if line in ("q", "quit", "退出"):
|
|
42
|
+
raise Quit
|
|
43
|
+
if line in ("", "b", "返回"):
|
|
44
|
+
raise Back
|
|
45
|
+
return line
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def choose(prompt: str, default: str) -> str:
|
|
49
|
+
"""Ask with a default: an empty answer means the default instead of going back."""
|
|
50
|
+
try:
|
|
51
|
+
return ask(prompt)
|
|
52
|
+
except Back:
|
|
53
|
+
return default
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def show(lines: list[str]) -> None:
|
|
57
|
+
print("\n".join(lines))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def fail(error: Exception) -> None:
|
|
61
|
+
code = getattr(error, "code", "usage")
|
|
62
|
+
show(cli.render_error(EntryError(str(error), code, getattr(error, "next_steps", []))))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def sub(screen, *args) -> None:
|
|
66
|
+
"""Run a nested screen; its "回车返回" comes back here instead of to the home page."""
|
|
67
|
+
try:
|
|
68
|
+
screen(*args)
|
|
69
|
+
except Back:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def confirm(prompt: str = "输入 yes 执行,回车取消") -> bool:
|
|
74
|
+
try:
|
|
75
|
+
return ask(prompt).lower() in ("yes", "y", "是")
|
|
76
|
+
except Back:
|
|
77
|
+
print(S.dim("已取消,没有写入。"))
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------- screens
|
|
82
|
+
|
|
83
|
+
def home(provider) -> None:
|
|
84
|
+
print()
|
|
85
|
+
show(cli.render_overview(provider))
|
|
86
|
+
capabilities = {item["id"]: item for item in provider.capabilities()}
|
|
87
|
+
health = provider.overview()["health"]
|
|
88
|
+
print()
|
|
89
|
+
for entry in MENU:
|
|
90
|
+
if entry is None:
|
|
91
|
+
print(S.dim(" ─────────── 维护"))
|
|
92
|
+
continue
|
|
93
|
+
key, title, hint, capability = entry
|
|
94
|
+
state = capabilities[capability]
|
|
95
|
+
if capability == "health" and any(health.values()):
|
|
96
|
+
hint = " · ".join(f"{health[k]} 项{label}" for k, label in cli.LEVELS if health.get(k))
|
|
97
|
+
if state["state"] != "available":
|
|
98
|
+
hint = S.yellow(f"不可用:{state['reason']}")
|
|
99
|
+
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 退出"))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def find_screen(provider, first: str | None = None) -> None:
|
|
104
|
+
show(cli.header(provider, "找资料"))
|
|
105
|
+
print(S.dim("输入关键词,可带日期(2025-12、10月)与 type:pdf、in:片段;回车返回"))
|
|
106
|
+
result = None
|
|
107
|
+
pending = first
|
|
108
|
+
while True:
|
|
109
|
+
line, pending = (pending, None) if pending else (ask("查找"), None)
|
|
110
|
+
match = ACTION_RE.match(line) or re.match(r"^()(\d+)$", line)
|
|
111
|
+
if match and result is not None:
|
|
112
|
+
key, number = match.group(1) or "o", int(match.group(2))
|
|
113
|
+
try:
|
|
114
|
+
item = cli._act_target(result, number)
|
|
115
|
+
except EntryError as exc:
|
|
116
|
+
fail(exc)
|
|
117
|
+
continue
|
|
118
|
+
if key == "v":
|
|
119
|
+
show(cli.render_versions(item) if item["versions"] else [S.dim("这份资料没有其他版本")])
|
|
120
|
+
elif key == "r":
|
|
121
|
+
show(cli.render_refs(provider.refs(item["path"])))
|
|
122
|
+
else:
|
|
123
|
+
print(provider.act({"o": "open", "f": "reveal", "c": "copy"}[key], item["path"])["message"])
|
|
124
|
+
continue
|
|
125
|
+
terms, ext, container = [], None, None
|
|
126
|
+
for token in line.split():
|
|
127
|
+
if token.startswith("type:"):
|
|
128
|
+
ext = token[5:]
|
|
129
|
+
elif token.startswith("in:"):
|
|
130
|
+
container = token[3:]
|
|
131
|
+
else:
|
|
132
|
+
terms.append(token)
|
|
133
|
+
try:
|
|
134
|
+
result = provider.find(terms, ext=ext, container=container)
|
|
135
|
+
except EntryError as exc:
|
|
136
|
+
fail(exc)
|
|
137
|
+
print(S.dim("输入 5 可直接去「修复与更新」。"))
|
|
138
|
+
if ask("下一步") != "5":
|
|
139
|
+
return
|
|
140
|
+
sub(fix_screen, provider, ["rebuild-index"] if exc.code == "index-broken" else None)
|
|
141
|
+
if provider.index_state()["status"] == "broken":
|
|
142
|
+
return
|
|
143
|
+
pending = line # 修好了:用同一个词再查一次
|
|
144
|
+
continue
|
|
145
|
+
show(cli.render_find(result))
|
|
146
|
+
if result["results"]:
|
|
147
|
+
print(S.dim("o1 打开 · f1 显示所在目录 · c1 复制路径 · v1 看版本 · r1 看引用 · 继续输入词再查 · 回车返回"))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def item_screen(provider, item: dict) -> None:
|
|
151
|
+
show(cli.render_item(item))
|
|
152
|
+
while True:
|
|
153
|
+
print(S.dim("o 打开权威记录 · d1 打开交付物 · f 显示所在目录 · w 为它记录 · 回车返回"))
|
|
154
|
+
line = ask("事项")
|
|
155
|
+
if line == "o":
|
|
156
|
+
print(provider.act("open", item["path"])["message"])
|
|
157
|
+
elif line == "f":
|
|
158
|
+
print(provider.act("reveal", item["path"])["message"])
|
|
159
|
+
elif line == "w":
|
|
160
|
+
sub(record_screen, provider, item["id"])
|
|
161
|
+
elif re.fullmatch(r"d\d+", line) and 1 <= int(line[1:]) <= len(item["deliverables"]):
|
|
162
|
+
print(provider.act("open", item["deliverables"][int(line[1:]) - 1])["message"])
|
|
163
|
+
else:
|
|
164
|
+
print(S.yellow("没有这个操作。"))
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def progress_screen(provider) -> None:
|
|
168
|
+
term, expand = None, False
|
|
169
|
+
while True:
|
|
170
|
+
show(cli.header(provider, "看进展"))
|
|
171
|
+
lines, numbered = cli.render_items(provider.items(term=term), expand_closed=expand)
|
|
172
|
+
show(([S.dim(f"筛选:{term}(输入 * 清除)")] if term else []) + lines)
|
|
173
|
+
print(S.dim("序号看详情 · 输入词查找(标题、别名、旧编号)· all 展开已完成 · 回车返回"))
|
|
174
|
+
line = ask("进展")
|
|
175
|
+
if line.isdigit() and 1 <= int(line) <= len(numbered):
|
|
176
|
+
sub(item_screen, provider, provider.item(numbered[int(line) - 1]["id"]))
|
|
177
|
+
elif line == "all":
|
|
178
|
+
expand = True
|
|
179
|
+
elif line == "*":
|
|
180
|
+
term = None
|
|
181
|
+
else:
|
|
182
|
+
term = line
|
|
183
|
+
matches = provider.items(term=term)["items"]
|
|
184
|
+
if len(matches) == 1:
|
|
185
|
+
sub(item_screen, provider, provider.item(matches[0]["id"]))
|
|
186
|
+
term = None
|
|
187
|
+
elif not matches:
|
|
188
|
+
print(S.yellow(f"没有事项匹配“{line}”。"))
|
|
189
|
+
term = None
|
|
190
|
+
else:
|
|
191
|
+
expand = True
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def guide_screen(provider) -> None:
|
|
195
|
+
show(cli.header(provider, "项目指南") + cli.render_guide(provider.guide()))
|
|
196
|
+
ask("回车返回")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def health_screen(provider) -> None:
|
|
200
|
+
show(cli.header(provider, "检查项目与环境"))
|
|
201
|
+
lines, numbered = cli.render_health(provider.health())
|
|
202
|
+
show(lines)
|
|
203
|
+
while True:
|
|
204
|
+
print(S.dim("序号去处理 · 回车返回"))
|
|
205
|
+
line = ask("检查")
|
|
206
|
+
if not (line.isdigit() and 1 <= int(line) <= len(numbered)):
|
|
207
|
+
print(S.yellow("没有这一项。"))
|
|
208
|
+
continue
|
|
209
|
+
finding = numbered[int(line) - 1]
|
|
210
|
+
actions = [step["action"] for step in finding["next"] if step["action"]]
|
|
211
|
+
if not actions:
|
|
212
|
+
print(S.dim("这一项不能在菜单里直接处理:") + finding["next"][0]["label"])
|
|
213
|
+
continue
|
|
214
|
+
verb, _, target = actions[0].partition(":")
|
|
215
|
+
if verb == "fix":
|
|
216
|
+
sub(fix_screen, provider, [target])
|
|
217
|
+
elif verb == "open":
|
|
218
|
+
print(provider.act("open", target)["message"])
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def fix_screen(provider, preset: list[str] | None = None) -> None:
|
|
222
|
+
show(cli.header(provider, "修复与更新"))
|
|
223
|
+
print(S.dim("流程:检查 → 选择处理项 → 查看影响 → 确认执行 → 验证结果"))
|
|
224
|
+
available = provider.fix_plan([])["steps"]
|
|
225
|
+
if not available:
|
|
226
|
+
print(S.green("检查完成:没有需要处理的项。"))
|
|
227
|
+
return
|
|
228
|
+
if preset:
|
|
229
|
+
ids = [step["id"] for step in available if step["id"] in preset]
|
|
230
|
+
titles = [step["title"] for step in available if step["id"] in ids]
|
|
231
|
+
print(S.bold("1–2. 检查并选中:") + ("、".join(titles) or S.yellow("这一项已处理或当前不可处理")))
|
|
232
|
+
if not ids:
|
|
233
|
+
return
|
|
234
|
+
else:
|
|
235
|
+
print(S.bold("1. 检查结果"))
|
|
236
|
+
for number, step in enumerate(available, 1):
|
|
237
|
+
blocked = S.yellow(f"({step['blocked']})") if step["blocked"] else ""
|
|
238
|
+
print(f" {S.bold(S.cyan(str(number)))} {step['title']}{blocked}")
|
|
239
|
+
line = ask("2. 选择处理项(序号,空格分隔;all 全选)")
|
|
240
|
+
picks = range(1, len(available) + 1) if line == "all" else \
|
|
241
|
+
[int(token) for token in line.split() if token.isdigit() and 1 <= int(token) <= len(available)]
|
|
242
|
+
ids = [available[number - 1]["id"] for number in picks]
|
|
243
|
+
if not ids:
|
|
244
|
+
print(S.yellow("没有选中任何项。"))
|
|
245
|
+
return
|
|
246
|
+
choices: dict[str, str] = {}
|
|
247
|
+
if "add-entry" in ids and not provider.overview().get("launcher"):
|
|
248
|
+
name = choose("启动器文件名(回车用 menu)", "")
|
|
249
|
+
if name:
|
|
250
|
+
choices["entry-name"] = name
|
|
251
|
+
plan = provider.fix_plan(ids, dict(choices))
|
|
252
|
+
print(S.bold("3. 查看影响"))
|
|
253
|
+
show(cli.render_plan(plan))
|
|
254
|
+
asked = False
|
|
255
|
+
for step in plan["steps"]:
|
|
256
|
+
for item in step["custom"]:
|
|
257
|
+
if step["blocked"]:
|
|
258
|
+
continue
|
|
259
|
+
answer = choose(f"{item['path']} {item['state']}:k 不改(默认)/ a 采用", "k") \
|
|
260
|
+
if item["path"] == "README.md" else \
|
|
261
|
+
choose(f"{item['path']} {item['state']}:k 保留你的版本(默认)/ a 采用新模板", "k")
|
|
262
|
+
choices[item["path"]] = "accept" if answer == "a" else "keep"
|
|
263
|
+
asked = True
|
|
264
|
+
if asked:
|
|
265
|
+
plan = provider.fix_plan(ids, choices)
|
|
266
|
+
print(S.bold("按你的选择,最终将:"))
|
|
267
|
+
show(cli.render_plan(plan, choices))
|
|
268
|
+
if all(step["blocked"] for step in plan["steps"]):
|
|
269
|
+
print(S.yellow("所选项目前都不能执行,只能预览。"))
|
|
270
|
+
return
|
|
271
|
+
print(S.bold("4. 确认执行"))
|
|
272
|
+
if not confirm():
|
|
273
|
+
return
|
|
274
|
+
print(S.bold("5. 验证结果"))
|
|
275
|
+
show(cli.render_apply(provider.apply_fix(plan, choices)))
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def record_screen(provider, target: str | None = None) -> None:
|
|
279
|
+
show(cli.header(provider, "记录事项"))
|
|
280
|
+
capability = next(item for item in provider.capabilities() if item["id"] == "record")
|
|
281
|
+
if capability["state"] != "available":
|
|
282
|
+
try:
|
|
283
|
+
provider.record_preview(None, {})
|
|
284
|
+
except EntryError as exc:
|
|
285
|
+
fail(exc)
|
|
286
|
+
return
|
|
287
|
+
if target is None:
|
|
288
|
+
line = ask("n 新建 · 或输入事项(编号、名称、旧编号)")
|
|
289
|
+
target = None if line == "n" else line
|
|
290
|
+
if target is None:
|
|
291
|
+
change = {"title": ask("标题"), "goal": ask("目标")}
|
|
292
|
+
try:
|
|
293
|
+
change["next_action"] = ask("下一动作(回车跳过)")
|
|
294
|
+
except Back:
|
|
295
|
+
change["next_action"] = ""
|
|
296
|
+
else:
|
|
297
|
+
try:
|
|
298
|
+
item = provider.item(target)
|
|
299
|
+
except EntryError as exc:
|
|
300
|
+
fail(exc)
|
|
301
|
+
return
|
|
302
|
+
print(f"{S.bold(item['title'])} [{cli.GROUP_LABEL.get(item['group'], item['status'])}]")
|
|
303
|
+
for key, title, _ in RECORD_ACTIONS:
|
|
304
|
+
print(f" {S.bold(S.cyan(key))} {title}")
|
|
305
|
+
choice = dict((key, field) for key, _, field in RECORD_ACTIONS).get(ask("要做什么"))
|
|
306
|
+
if choice is None:
|
|
307
|
+
print(S.yellow("没有这个操作。"))
|
|
308
|
+
return
|
|
309
|
+
target = item["id"]
|
|
310
|
+
if choice == "close":
|
|
311
|
+
change = {"status": "done"}
|
|
312
|
+
elif choice == "reopen":
|
|
313
|
+
change = {"status": "active"}
|
|
314
|
+
if not item["next_action"]:
|
|
315
|
+
change["next_action"] = ask("恢复后的下一动作")
|
|
316
|
+
elif choice == "waiting":
|
|
317
|
+
change = {"status": "waiting", "waiting_for": ask("在等谁或等什么")}
|
|
318
|
+
elif choice == "deliverable":
|
|
319
|
+
change = {"add_deliverable": [ask("文件路径(相对项目根目录,可从「找资料」c1 复制)")]}
|
|
320
|
+
else:
|
|
321
|
+
change = {choice: ask("内容")}
|
|
322
|
+
try:
|
|
323
|
+
preview = provider.record_preview(target, change)
|
|
324
|
+
except EntryError as exc:
|
|
325
|
+
fail(exc)
|
|
326
|
+
return
|
|
327
|
+
show(cli.render_preview(preview))
|
|
328
|
+
if not confirm("输入 yes 写入,回车取消"):
|
|
329
|
+
return
|
|
330
|
+
try:
|
|
331
|
+
print(S.green(provider.record_apply(preview)["verify"]))
|
|
332
|
+
except EntryError as exc:
|
|
333
|
+
fail(exc)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def tools_screen(provider) -> None:
|
|
337
|
+
tools = provider.tools()
|
|
338
|
+
show(cli.header(provider, "项目工具") + cli.render_tools(tools))
|
|
339
|
+
while tools:
|
|
340
|
+
line = ask("序号查看并执行 · 回车返回")
|
|
341
|
+
if not (line.isdigit() and 1 <= int(line) <= len(tools)):
|
|
342
|
+
print(S.yellow("没有这一项。"))
|
|
343
|
+
continue
|
|
344
|
+
tool = tools[int(line) - 1]
|
|
345
|
+
if tool["state"] != "available":
|
|
346
|
+
print(S.yellow(f"“{tool['title']}”当前不可用:{tool['reason']}"))
|
|
347
|
+
continue
|
|
348
|
+
show(cli.render_tools([tool]))
|
|
349
|
+
if confirm():
|
|
350
|
+
print(provider.run_tool(tool["id"])["message"])
|
|
351
|
+
if not tools:
|
|
352
|
+
ask("回车返回")
|
|
353
|
+
|
|
354
|
+
|
|
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
|
+
|
|
363
|
+
SCREENS = {"1": find_screen, "2": progress_screen, "3": guide_screen, "4": health_screen,
|
|
364
|
+
"5": fix_screen, "6": record_screen, "7": tools_screen}
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def run(provider) -> int:
|
|
368
|
+
try:
|
|
369
|
+
import readline # noqa: F401 line editing and history
|
|
370
|
+
except ImportError:
|
|
371
|
+
pass
|
|
372
|
+
try:
|
|
373
|
+
while True:
|
|
374
|
+
home(provider)
|
|
375
|
+
try:
|
|
376
|
+
line = ask("选择")
|
|
377
|
+
except Back:
|
|
378
|
+
continue
|
|
379
|
+
try:
|
|
380
|
+
if line in SCREENS:
|
|
381
|
+
SCREENS[line](provider)
|
|
382
|
+
elif line in ("?", "help", "帮助"):
|
|
383
|
+
print(HELP)
|
|
384
|
+
else:
|
|
385
|
+
find_screen(provider, line)
|
|
386
|
+
except Back:
|
|
387
|
+
continue
|
|
388
|
+
except EntryError as exc:
|
|
389
|
+
fail(exc)
|
|
390
|
+
except Quit:
|
|
391
|
+
return cli.EXIT_OK
|
|
392
|
+
except KeyboardInterrupt:
|
|
393
|
+
print()
|
|
394
|
+
return cli.EXIT_INTERRUPTED
|