@chenmk/superflow 0.4.0 → 0.5.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.
Files changed (241) hide show
  1. package/INSTALL.en.md +54 -12
  2. package/INSTALL.md +59 -33
  3. package/README.md +178 -54
  4. package/README.zh-CN.md +156 -51
  5. package/assets/brand/managed-review-loop.png +0 -0
  6. package/assets/manifest.json +10 -5
  7. package/assets/scripts/superflow-coding-ready.mjs +135 -0
  8. package/assets/scripts/superflow-dependency-update-hook.sh +81 -8
  9. package/assets/scripts/superflow-document-audit.mjs +240 -0
  10. package/assets/scripts/superflow-environment-preflight.mjs +398 -0
  11. package/assets/scripts/superflow-hook-guard.sh +40 -1
  12. package/assets/scripts/superflow-managed-command-guard.py +309 -0
  13. package/assets/scripts/superflow-managed-executor-preflight.mjs +421 -0
  14. package/assets/scripts/superflow-managed-owner-verification.sh +117 -0
  15. package/assets/scripts/superflow-managed-work-check.mjs +287 -26
  16. package/assets/scripts/superflow-managed-work-guard.sh +6 -92
  17. package/assets/skills/superflow-clarify/SKILL.md +14 -0
  18. package/assets/skills/superflow-design/SKILL.md +38 -13
  19. package/assets/skills/superflow-docs/SKILL.md +93 -2
  20. package/assets/skills/superflow-pipeline/SKILL.md +167 -10
  21. package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +17 -9
  22. package/assets/skills/superflow-pipeline/references/document-readiness.md +157 -0
  23. package/assets/skills/superflow-pipeline/references/document-templates.md +9 -5
  24. package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +14 -0
  25. package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +20 -11
  26. package/assets/skills/superflow-pipeline/references/managed-work.md +123 -15
  27. package/assets/skills/superflow-pipeline/references/orchestration.md +2 -2
  28. package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +3 -2
  29. package/assets/skills/superflow-pipeline/references/quality-gate.md +7 -5
  30. package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +7 -0
  31. package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  32. package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  33. package/assets/skills/superflow-requirement-review/SKILL.md +178 -0
  34. package/assets/skills/superflow-requirement-review/agents/openai.yaml +4 -0
  35. package/assets/skills-en/superflow-clarify/SKILL.md +17 -0
  36. package/assets/skills-en/superflow-design/SKILL.md +31 -12
  37. package/assets/skills-en/superflow-docs/SKILL.md +99 -0
  38. package/assets/skills-en/superflow-pipeline/SKILL.md +163 -9
  39. package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +4 -0
  40. package/assets/skills-en/superflow-pipeline/references/document-readiness.md +125 -0
  41. package/assets/skills-en/superflow-pipeline/references/document-templates.md +10 -0
  42. package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +7 -0
  43. package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +7 -0
  44. package/assets/skills-en/superflow-pipeline/references/managed-work.md +160 -10
  45. package/assets/skills-en/superflow-pipeline/references/quality-gate.md +12 -0
  46. package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +7 -0
  47. package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  48. package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  49. package/assets/skills-en/superflow-requirement-review/SKILL.md +99 -0
  50. package/assets/skills-en/superflow-requirement-review/agents/openai.yaml +4 -0
  51. package/dist/app/cli.js +64 -9
  52. package/dist/app/cli.js.map +1 -1
  53. package/dist/app/commands/change-guard.js +19 -2
  54. package/dist/app/commands/change-guard.js.map +1 -1
  55. package/dist/app/commands/check.js +107 -45
  56. package/dist/app/commands/check.js.map +1 -1
  57. package/dist/app/commands/docs.js +7 -3
  58. package/dist/app/commands/docs.js.map +1 -1
  59. package/dist/app/commands/doctor.js +174 -198
  60. package/dist/app/commands/doctor.js.map +1 -1
  61. package/dist/app/commands/eval.js +64 -0
  62. package/dist/app/commands/eval.js.map +1 -0
  63. package/dist/app/commands/implement.js +7 -3
  64. package/dist/app/commands/implement.js.map +1 -1
  65. package/dist/app/commands/init.js +12 -24
  66. package/dist/app/commands/init.js.map +1 -1
  67. package/dist/app/commands/mcp.js +167 -0
  68. package/dist/app/commands/mcp.js.map +1 -0
  69. package/dist/app/commands/pipeline.js +652 -18
  70. package/dist/app/commands/pipeline.js.map +1 -1
  71. package/dist/app/commands/requirement-review.js +6 -0
  72. package/dist/app/commands/requirement-review.js.map +1 -0
  73. package/dist/app/commands/status.js +66 -3
  74. package/dist/app/commands/status.js.map +1 -1
  75. package/dist/app/commands/uninstall.js +3 -3
  76. package/dist/app/commands/uninstall.js.map +1 -1
  77. package/dist/app/commands/update.js +83 -9
  78. package/dist/app/commands/update.js.map +1 -1
  79. package/dist/domains/agent.js +3 -11
  80. package/dist/domains/agent.js.map +1 -1
  81. package/dist/domains/config/cli-help.js +54 -12
  82. package/dist/domains/config/cli-help.js.map +1 -1
  83. package/dist/domains/config/i18n.js +6 -8
  84. package/dist/domains/config/i18n.js.map +1 -1
  85. package/dist/domains/config/manifest.js +0 -3
  86. package/dist/domains/config/manifest.js.map +1 -1
  87. package/dist/domains/deps.js +68 -7
  88. package/dist/domains/deps.js.map +1 -1
  89. package/dist/domains/managed-work/completion-policy.js +174 -0
  90. package/dist/domains/managed-work/completion-policy.js.map +1 -0
  91. package/dist/domains/managed-work/context-manifest.js +208 -0
  92. package/dist/domains/managed-work/context-manifest.js.map +1 -0
  93. package/dist/domains/managed-work/contract.js +135 -4
  94. package/dist/domains/managed-work/contract.js.map +1 -1
  95. package/dist/domains/managed-work/control-signals.js +34 -0
  96. package/dist/domains/managed-work/control-signals.js.map +1 -0
  97. package/dist/domains/managed-work/control.js +628 -0
  98. package/dist/domains/managed-work/control.js.map +1 -0
  99. package/dist/domains/managed-work/evaluation.js +497 -0
  100. package/dist/domains/managed-work/evaluation.js.map +1 -0
  101. package/dist/domains/managed-work/evidence-path.js +14 -0
  102. package/dist/domains/managed-work/evidence-path.js.map +1 -0
  103. package/dist/domains/managed-work/execution-contract.js +167 -0
  104. package/dist/domains/managed-work/execution-contract.js.map +1 -0
  105. package/dist/domains/managed-work/executor-policy.js +65 -0
  106. package/dist/domains/managed-work/executor-policy.js.map +1 -0
  107. package/dist/domains/managed-work/failure.js +65 -0
  108. package/dist/domains/managed-work/failure.js.map +1 -0
  109. package/dist/domains/managed-work/host-review.js +77 -0
  110. package/dist/domains/managed-work/host-review.js.map +1 -0
  111. package/dist/domains/managed-work/human-messages.js +31 -0
  112. package/dist/domains/managed-work/human-messages.js.map +1 -0
  113. package/dist/domains/managed-work/input.js +29 -3
  114. package/dist/domains/managed-work/input.js.map +1 -1
  115. package/dist/domains/managed-work/journal.js +77 -3
  116. package/dist/domains/managed-work/journal.js.map +1 -1
  117. package/dist/domains/managed-work/lock.js +67 -1
  118. package/dist/domains/managed-work/lock.js.map +1 -1
  119. package/dist/domains/managed-work/notifications.js +0 -15
  120. package/dist/domains/managed-work/notifications.js.map +1 -1
  121. package/dist/domains/managed-work/pair-admission.js +108 -0
  122. package/dist/domains/managed-work/pair-admission.js.map +1 -0
  123. package/dist/domains/managed-work/prompts.js +257 -22
  124. package/dist/domains/managed-work/prompts.js.map +1 -1
  125. package/dist/domains/managed-work/redaction.js +90 -0
  126. package/dist/domains/managed-work/redaction.js.map +1 -0
  127. package/dist/domains/managed-work/review-convergence.js +41 -0
  128. package/dist/domains/managed-work/review-convergence.js.map +1 -0
  129. package/dist/domains/managed-work/review-facts.js +181 -0
  130. package/dist/domains/managed-work/review-facts.js.map +1 -0
  131. package/dist/domains/managed-work/rule-selection.js +84 -0
  132. package/dist/domains/managed-work/rule-selection.js.map +1 -0
  133. package/dist/domains/managed-work/runner.js +1967 -175
  134. package/dist/domains/managed-work/runner.js.map +1 -1
  135. package/dist/domains/managed-work/schemas.js +72 -2
  136. package/dist/domains/managed-work/schemas.js.map +1 -1
  137. package/dist/domains/managed-work/service.js +82 -13
  138. package/dist/domains/managed-work/service.js.map +1 -1
  139. package/dist/domains/managed-work/state.js +195 -5
  140. package/dist/domains/managed-work/state.js.map +1 -1
  141. package/dist/domains/managed-work/storage.js +66 -24
  142. package/dist/domains/managed-work/storage.js.map +1 -1
  143. package/dist/domains/managed-work/types.js +12 -9
  144. package/dist/domains/managed-work/types.js.map +1 -1
  145. package/dist/domains/managed-work/verification-categories.js +39 -0
  146. package/dist/domains/managed-work/verification-categories.js.map +1 -0
  147. package/dist/domains/managed-work/verification-policy.js +116 -0
  148. package/dist/domains/managed-work/verification-policy.js.map +1 -0
  149. package/dist/domains/managed-work/wait.js +21 -3
  150. package/dist/domains/managed-work/wait.js.map +1 -1
  151. package/dist/domains/managed-work/workspace-binding.js +133 -0
  152. package/dist/domains/managed-work/workspace-binding.js.map +1 -0
  153. package/dist/domains/sdd-readiness.js +44 -0
  154. package/dist/domains/sdd-readiness.js.map +1 -0
  155. package/dist/domains/skill/deploy.js +0 -6
  156. package/dist/domains/skill/deploy.js.map +1 -1
  157. package/dist/domains/skill/scripts.js +0 -8
  158. package/dist/domains/skill/scripts.js.map +1 -1
  159. package/dist/domains/state.js +1 -2
  160. package/dist/domains/state.js.map +1 -1
  161. package/dist/mcp/runtime-identity.js +21 -0
  162. package/dist/mcp/runtime-identity.js.map +1 -0
  163. package/dist/mcp/server.js +369 -0
  164. package/dist/mcp/server.js.map +1 -0
  165. package/dist/platform/agent-process.js +499 -38
  166. package/dist/platform/agent-process.js.map +1 -1
  167. package/dist/platform/executable.js +28 -0
  168. package/dist/platform/executable.js.map +1 -0
  169. package/dist/platform/paths.js +1 -17
  170. package/dist/platform/paths.js.map +1 -1
  171. package/dist/platform/process-tree.js +44 -0
  172. package/dist/platform/process-tree.js.map +1 -0
  173. package/dist/platform/process.js +1 -1
  174. package/dist/platform/process.js.map +1 -1
  175. package/package.json +16 -7
  176. package/scripts/check-design-principles.mjs +107 -0
  177. package/scripts/check-managed-work-design.mjs +507 -0
  178. package/scripts/clean-dist.js +9 -0
  179. package/scripts/ensure-bin-modes.js +11 -0
  180. package/dist/cli/index.js +0 -193
  181. package/dist/cli/index.js.map +0 -1
  182. package/dist/commands/archive.js +0 -6
  183. package/dist/commands/archive.js.map +0 -1
  184. package/dist/commands/clarify.js +0 -6
  185. package/dist/commands/clarify.js.map +0 -1
  186. package/dist/commands/design.js +0 -28
  187. package/dist/commands/design.js.map +0 -1
  188. package/dist/commands/docs.js +0 -6
  189. package/dist/commands/docs.js.map +0 -1
  190. package/dist/commands/doctor.js +0 -494
  191. package/dist/commands/doctor.js.map +0 -1
  192. package/dist/commands/implement.js +0 -6
  193. package/dist/commands/implement.js.map +0 -1
  194. package/dist/commands/init.js +0 -520
  195. package/dist/commands/init.js.map +0 -1
  196. package/dist/commands/pipeline.js +0 -6
  197. package/dist/commands/pipeline.js.map +0 -1
  198. package/dist/commands/scan.js +0 -63
  199. package/dist/commands/scan.js.map +0 -1
  200. package/dist/commands/status.js +0 -173
  201. package/dist/commands/status.js.map +0 -1
  202. package/dist/commands/uninstall.js +0 -214
  203. package/dist/commands/uninstall.js.map +0 -1
  204. package/dist/commands/update.js +0 -193
  205. package/dist/commands/update.js.map +0 -1
  206. package/dist/commands/verify.js +0 -6
  207. package/dist/commands/verify.js.map +0 -1
  208. package/dist/core/assets.js +0 -27
  209. package/dist/core/assets.js.map +0 -1
  210. package/dist/core/cli-help.js +0 -98
  211. package/dist/core/cli-help.js.map +0 -1
  212. package/dist/core/context.js +0 -123
  213. package/dist/core/context.js.map +0 -1
  214. package/dist/core/dependencies.js +0 -146
  215. package/dist/core/dependencies.js.map +0 -1
  216. package/dist/core/detect.js +0 -64
  217. package/dist/core/detect.js.map +0 -1
  218. package/dist/core/i18n.js +0 -105
  219. package/dist/core/i18n.js.map +0 -1
  220. package/dist/core/integrity.js +0 -46
  221. package/dist/core/integrity.js.map +0 -1
  222. package/dist/core/manifest.js +0 -21
  223. package/dist/core/manifest.js.map +0 -1
  224. package/dist/core/prompts.js +0 -20
  225. package/dist/core/prompts.js.map +0 -1
  226. package/dist/core/registry.js +0 -134
  227. package/dist/core/registry.js.map +0 -1
  228. package/dist/core/rules.js +0 -17
  229. package/dist/core/rules.js.map +0 -1
  230. package/dist/core/scripts.js +0 -48
  231. package/dist/core/scripts.js.map +0 -1
  232. package/dist/core/skill-check.js +0 -32
  233. package/dist/core/skill-check.js.map +0 -1
  234. package/dist/core/skills.js +0 -62
  235. package/dist/core/skills.js.map +0 -1
  236. package/dist/core/state.js +0 -54
  237. package/dist/core/state.js.map +0 -1
  238. package/dist/utils/path.js +0 -11
  239. package/dist/utils/path.js.map +0 -1
  240. package/dist/utils/shell.js +0 -29
  241. package/dist/utils/shell.js.map +0 -1
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env python3
2
+ """Pre-tool guard for Superflow managed Agent roles."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import os
8
+ import re
9
+ import shlex
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ def task_language() -> str:
15
+ root = os.environ.get("SUPERFLOW_MANAGED_PROJECT_ROOT", "")
16
+ task_id = os.environ.get("SUPERFLOW_MANAGED_TASK_ID", "")
17
+ if not root or not task_id:
18
+ return "zh"
19
+ task_file = Path(root) / ".superflow" / "tasks" / task_id / "task.json"
20
+ try:
21
+ return json.loads(task_file.read_text(encoding="utf-8")).get(
22
+ "language", "zh"
23
+ )
24
+ except (OSError, ValueError):
25
+ return "zh"
26
+
27
+
28
+ def block(zh: str, en: str | None = None) -> None:
29
+ message = en if task_language() == "en" and en else zh
30
+ print(f"[Superflow managed-work guard] {message}", file=sys.stderr)
31
+ raise SystemExit(2)
32
+
33
+
34
+ def tool_field(payload: dict, *names: str) -> str:
35
+ tool_input = payload.get("tool_input") or {}
36
+ for name in names:
37
+ value = tool_input.get(name)
38
+ if isinstance(value, str) and value.strip():
39
+ return value.strip()
40
+ return ""
41
+
42
+
43
+ def canonical(value: str, root: Path) -> Path:
44
+ candidate = Path(value).expanduser()
45
+ if not candidate.is_absolute():
46
+ candidate = root / candidate
47
+ return candidate.resolve(strict=False)
48
+
49
+
50
+ def overlaps_protected(candidate: Path, protected: list[Path]) -> bool:
51
+ for item in protected:
52
+ if candidate == item or candidate in item.parents:
53
+ return True
54
+ return False
55
+
56
+
57
+ def protected_entries() -> tuple[list[Path], list[Path]]:
58
+ manifest_path = os.environ.get("SUPERFLOW_MANAGED_CONTEXT_MANIFEST", "")
59
+ if not manifest_path:
60
+ return [], []
61
+ try:
62
+ manifest = json.loads(Path(manifest_path).read_text(encoding="utf-8"))
63
+ except (OSError, ValueError) as error:
64
+ block(
65
+ f"无法读取受保护输入清单,按失败关闭:{error}",
66
+ f"Cannot read the protected-input manifest; failing closed: {error}",
67
+ )
68
+ immutable: list[Path] = []
69
+ retained: list[Path] = []
70
+ for entry in manifest.get("entries", []):
71
+ entry_path = entry.get("path")
72
+ if not isinstance(entry_path, str) or not entry_path:
73
+ continue
74
+ target = Path(entry_path).resolve(strict=False)
75
+ if entry.get("protection", "immutable") == "retain":
76
+ retained.append(target)
77
+ else:
78
+ immutable.append(target)
79
+ return immutable, retained
80
+
81
+
82
+ def shell_tokens(command: str) -> list[str]:
83
+ try:
84
+ return shlex.split(command, posix=os.name != "nt")
85
+ except ValueError:
86
+ return command.split()
87
+
88
+
89
+ def path_tokens(command: str, root: Path) -> list[Path]:
90
+ tokens = shell_tokens(command)
91
+ values: list[Path] = []
92
+ for token in tokens:
93
+ cleaned = token.strip("'\"")
94
+ if (
95
+ not cleaned
96
+ or cleaned.startswith("-")
97
+ or "$" in cleaned
98
+ or cleaned in {"rm", "mv", "cp", "truncate", "unlink", "rmdir"}
99
+ ):
100
+ continue
101
+ values.append(canonical(cleaned, root))
102
+ return values
103
+
104
+
105
+ def mutates_files(command: str) -> bool:
106
+ return bool(
107
+ re.search(
108
+ r"(?:^|[;&|]\s*)(?:rm|mv|cp|truncate|unlink|rmdir)\s"
109
+ r"|\bsed\s+[^;&|]*-[A-Za-z]*i\b"
110
+ r"|\bperl\s+[^;&|]*-[A-Za-z]*i\b"
111
+ r"|(?:^|[^>])>{1,2}\s*[^&]",
112
+ command,
113
+ re.IGNORECASE,
114
+ )
115
+ )
116
+
117
+
118
+ def safe_workspace_rm(command: str, root: Path) -> bool:
119
+ allowed = {
120
+ "target",
121
+ "build",
122
+ "dist",
123
+ "coverage",
124
+ ".gradle",
125
+ ".cache",
126
+ ".tmp",
127
+ "tmp",
128
+ }
129
+ matched = False
130
+ for segment in re.split(r"[;&|]", command):
131
+ if not re.search(r"(?:^|\s)rm\s+-[^\s]*r[^\s]*f", segment):
132
+ continue
133
+ matched = True
134
+ tokens = shell_tokens(segment)
135
+ try:
136
+ start = tokens.index("rm") + 1
137
+ except ValueError:
138
+ return False
139
+ targets = [token for token in tokens[start:] if not token.startswith("-")]
140
+ if not targets:
141
+ return False
142
+ for value in targets:
143
+ if "$" in value:
144
+ return False
145
+ target = canonical(value, root)
146
+ try:
147
+ relative = target.relative_to(root)
148
+ except ValueError:
149
+ return False
150
+ if not relative.parts or relative.parts[0] not in allowed:
151
+ return False
152
+ return matched
153
+
154
+
155
+ def guard_supervisor(tool_name: str, command: str) -> None:
156
+ if tool_name in {"Edit", "Write", "MultiEdit", "NotebookEdit", "apply_patch"}:
157
+ block(
158
+ "监督角色只允许检查,禁止修改目标文件。",
159
+ "The supervisor is read-only and cannot modify target files.",
160
+ )
161
+ if command and re.search(
162
+ r"(?:^|\s)(?:rm|mv|cp|mkdir|touch|chmod|chown)(?:\s|$)"
163
+ r"|git\s+(?:add|commit|push|tag|reset|checkout|restore)"
164
+ r"|npm\s+publish|apply_patch|sed\s+-i|(?:^|[^>])>{1,2}\s|tee\s",
165
+ command,
166
+ re.IGNORECASE,
167
+ ):
168
+ block(
169
+ "监督角色的 Bash 仅允许只读检查,检测到可能写入的命令。",
170
+ "Supervisor shell access is read-only; a mutating command was detected.",
171
+ )
172
+
173
+
174
+ def guard_executor(
175
+ tool_name: str,
176
+ file_path: str,
177
+ command: str,
178
+ root: Path,
179
+ immutable: list[Path],
180
+ retained: list[Path],
181
+ ) -> None:
182
+ write_tools = {"Edit", "Write", "MultiEdit", "NotebookEdit", "apply_patch"}
183
+ if tool_name in write_tools:
184
+ if not file_path:
185
+ block(
186
+ "写入工具缺少目标路径,按失败关闭。",
187
+ "The write tool has no target path; failing closed.",
188
+ )
189
+ target = canonical(file_path, root)
190
+ if ".superflow/tasks" in target.as_posix():
191
+ block(
192
+ "执行角色禁止修改托管状态、事件、Prompt 和过程报告。",
193
+ "The executor cannot edit managed state, events, prompts, or run reports.",
194
+ )
195
+ if target in immutable:
196
+ block(
197
+ f"冻结输入只读,禁止修改:{target}",
198
+ f"Frozen input is read-only: {target}",
199
+ )
200
+
201
+ if command and ".superflow/tasks/" in command.replace("\\", "/"):
202
+ block(
203
+ "执行角色的 Bash 禁止直接访问托管运行目录,请使用只读工具。",
204
+ "Executor shell commands cannot access the managed run directory; use read-only tools.",
205
+ )
206
+ if command and re.search(
207
+ r"git\s+(?:add|commit|push|tag)|npm\s+publish"
208
+ r"|dangerously-(?:bypass|skip)|kubectl\s+(?:apply|delete)"
209
+ r"|helm\s+(?:install|upgrade)|DROP\s+(?:DATABASE|TABLE)"
210
+ r"|TRUNCATE\s+TABLE",
211
+ command,
212
+ re.IGNORECASE,
213
+ ):
214
+ block(
215
+ "执行角色禁止自动 Git、发布、部署、绕过沙箱或高破坏数据库操作。",
216
+ "The executor cannot perform Git delivery, publish, deploy, bypass the sandbox, or run destructive database operations.",
217
+ )
218
+
219
+ if command and mutates_files(command):
220
+ targets = path_tokens(command, root)
221
+ if any(overlaps_protected(target, immutable + retained) for target in targets):
222
+ block(
223
+ "命令试图删除、移动、截断或覆盖受保护合同/交付文档。",
224
+ "The command would delete, move, truncate, or overwrite a protected contract or retained delivery document.",
225
+ )
226
+
227
+ if not command:
228
+ return
229
+ if re.search(r"(?:^|[;&|]\s*)(?:pkill|killall)\b", command):
230
+ block(
231
+ "禁止按进程名清理;必须通过 owner helper 校验任务进程身份。",
232
+ "Process-name cleanup is forbidden; verify task ownership through the owner helper.",
233
+ )
234
+ if (
235
+ re.search(r"(?:^|[;&|]\s*)kill\s+(?!-0\b)", command)
236
+ and "superflow_signal_owner" not in command
237
+ ):
238
+ block(
239
+ "禁止使用裸 kill;必须调用 superflow_signal_owner。",
240
+ "Bare kill is forbidden; call superflow_signal_owner.",
241
+ )
242
+ if (
243
+ re.search(
244
+ r"\bdocker\s+(?:(?:container|network|volume)\s+)?rm\b",
245
+ command,
246
+ re.IGNORECASE,
247
+ )
248
+ and "superflow_docker_remove_verified" not in command
249
+ ):
250
+ block(
251
+ "禁止直接删除 Docker 资源;必须调用 owner helper 核验标签。",
252
+ "Direct Docker deletion is forbidden; verify labels through the owner helper.",
253
+ )
254
+ if re.search(r"(?:^|[;&|]\s*)rm\s+-[^;&|]*r[^;&|]*f", command):
255
+ if (
256
+ "superflow_runtime_remove_verified" not in command
257
+ and not safe_workspace_rm(command, root)
258
+ ):
259
+ block(
260
+ "rm -rf 仅允许清理仓库构建缓存;运行目录必须通过 owner helper 删除。",
261
+ "rm -rf may only clean repository build caches; runtime directories require the owner helper.",
262
+ )
263
+
264
+
265
+ def main() -> None:
266
+ role = os.environ.get("SUPERFLOW_MANAGED_ROLE", "")
267
+ if not role:
268
+ return
269
+ if role not in {"supervisor", "executor"}:
270
+ block(
271
+ "检测到非法托管角色,按失败关闭。",
272
+ "Invalid managed role; failing closed.",
273
+ )
274
+ try:
275
+ payload = json.load(sys.stdin)
276
+ except (ValueError, TypeError):
277
+ block(
278
+ "Hook 输入不是有效 JSON,按失败关闭。",
279
+ "Hook input is not valid JSON; failing closed.",
280
+ )
281
+ if not isinstance(payload, dict):
282
+ block(
283
+ "Hook 输入不是 JSON 对象,按失败关闭。",
284
+ "Hook input is not a JSON object; failing closed.",
285
+ )
286
+
287
+ tool_name = str(payload.get("tool_name") or payload.get("tool") or "")
288
+ file_path = tool_field(payload, "file_path", "path", "file")
289
+ command = tool_field(payload, "command", "cmd").replace("\n", " ")
290
+ root = Path(
291
+ os.environ.get("SUPERFLOW_MANAGED_PROJECT_ROOT", os.getcwd())
292
+ ).resolve(strict=False)
293
+ immutable, retained = protected_entries()
294
+
295
+ if role == "supervisor":
296
+ guard_supervisor(tool_name, command)
297
+ else:
298
+ guard_executor(
299
+ tool_name,
300
+ file_path,
301
+ command,
302
+ root,
303
+ immutable,
304
+ retained,
305
+ )
306
+
307
+
308
+ if __name__ == "__main__":
309
+ main()