tau-coding-agent 0.1.0__py3-none-any.whl

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 (283) hide show
  1. tau/__init__.py +0 -0
  2. tau/agent/__init__.py +11 -0
  3. tau/agent/prompt/__init__.py +10 -0
  4. tau/agent/prompt/builder.py +302 -0
  5. tau/agent/prompt/types.py +33 -0
  6. tau/agent/service.py +369 -0
  7. tau/agent/types.py +61 -0
  8. tau/auth/manager.py +247 -0
  9. tau/auth/storage.py +82 -0
  10. tau/auth/types.py +41 -0
  11. tau/builtins/__init__.py +4 -0
  12. tau/builtins/__pycache__/__init__.cpython-313.pyc +0 -0
  13. tau/builtins/__pycache__/__init__.cpython-314.pyc +0 -0
  14. tau/builtins/commands/__init__.py +41 -0
  15. tau/builtins/commands/__pycache__/__init__.cpython-313.pyc +0 -0
  16. tau/builtins/commands/__pycache__/__init__.cpython-314.pyc +0 -0
  17. tau/builtins/commands/__pycache__/clear.cpython-313.pyc +0 -0
  18. tau/builtins/commands/__pycache__/clear.cpython-314.pyc +0 -0
  19. tau/builtins/commands/__pycache__/compact.cpython-313.pyc +0 -0
  20. tau/builtins/commands/__pycache__/compact.cpython-314.pyc +0 -0
  21. tau/builtins/commands/__pycache__/reload.cpython-313.pyc +0 -0
  22. tau/builtins/commands/__pycache__/reload.cpython-314.pyc +0 -0
  23. tau/builtins/commands/__pycache__/session.cpython-313.pyc +0 -0
  24. tau/builtins/commands/__pycache__/session.cpython-314.pyc +0 -0
  25. tau/builtins/commands/clear.py +16 -0
  26. tau/builtins/commands/compact.py +28 -0
  27. tau/builtins/commands/reload.py +27 -0
  28. tau/builtins/commands/session.py +19 -0
  29. tau/builtins/extensions/footer/__init__.py +76 -0
  30. tau/builtins/extensions/footer/__pycache__/__init__.cpython-313.pyc +0 -0
  31. tau/builtins/extensions/footer/__pycache__/git.cpython-313.pyc +0 -0
  32. tau/builtins/extensions/footer/__pycache__/model.cpython-313.pyc +0 -0
  33. tau/builtins/extensions/footer/__pycache__/utils.cpython-313.pyc +0 -0
  34. tau/builtins/extensions/footer/git.py +26 -0
  35. tau/builtins/extensions/footer/model.py +69 -0
  36. tau/builtins/extensions/footer/utils.py +44 -0
  37. tau/builtins/extensions/header/__init__.py +18 -0
  38. tau/builtins/extensions/header/__pycache__/__init__.cpython-313.pyc +0 -0
  39. tau/builtins/models/__init__.py +0 -0
  40. tau/builtins/models/__pycache__/__init__.cpython-313.pyc +0 -0
  41. tau/builtins/models/__pycache__/text.cpython-313.pyc +0 -0
  42. tau/builtins/models/audio.py +43 -0
  43. tau/builtins/models/image.py +43 -0
  44. tau/builtins/models/text.py +482 -0
  45. tau/builtins/models/video.py +40 -0
  46. tau/builtins/prompts/commit.md +7 -0
  47. tau/builtins/prompts/docs.md +7 -0
  48. tau/builtins/prompts/explain.md +7 -0
  49. tau/builtins/prompts/fix.md +7 -0
  50. tau/builtins/prompts/refactor.md +7 -0
  51. tau/builtins/prompts/review.md +7 -0
  52. tau/builtins/prompts/test.md +7 -0
  53. tau/builtins/providers/__init__.py +0 -0
  54. tau/builtins/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  55. tau/builtins/providers/__pycache__/text.cpython-313.pyc +0 -0
  56. tau/builtins/providers/audio.py +10 -0
  57. tau/builtins/providers/image.py +9 -0
  58. tau/builtins/providers/text.py +33 -0
  59. tau/builtins/providers/video.py +6 -0
  60. tau/builtins/skills/code-review/SKILL.md +4 -0
  61. tau/builtins/skills/debug/SKILL.md +4 -0
  62. tau/builtins/skills/git-commit/SKILL.md +4 -0
  63. tau/builtins/themes/dark.yaml +1 -0
  64. tau/builtins/themes/light.yaml +46 -0
  65. tau/builtins/tools/__init__.py +73 -0
  66. tau/builtins/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  67. tau/builtins/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  68. tau/builtins/tools/__pycache__/bash.cpython-313.pyc +0 -0
  69. tau/builtins/tools/__pycache__/bash.cpython-314.pyc +0 -0
  70. tau/builtins/tools/__pycache__/edit.cpython-313.pyc +0 -0
  71. tau/builtins/tools/__pycache__/edit.cpython-314.pyc +0 -0
  72. tau/builtins/tools/__pycache__/glob.cpython-313.pyc +0 -0
  73. tau/builtins/tools/__pycache__/glob.cpython-314.pyc +0 -0
  74. tau/builtins/tools/__pycache__/grep.cpython-313.pyc +0 -0
  75. tau/builtins/tools/__pycache__/grep.cpython-314.pyc +0 -0
  76. tau/builtins/tools/__pycache__/ls.cpython-313.pyc +0 -0
  77. tau/builtins/tools/__pycache__/ls.cpython-314.pyc +0 -0
  78. tau/builtins/tools/__pycache__/read.cpython-313.pyc +0 -0
  79. tau/builtins/tools/__pycache__/read.cpython-314.pyc +0 -0
  80. tau/builtins/tools/__pycache__/terminal.cpython-313.pyc +0 -0
  81. tau/builtins/tools/__pycache__/terminal.cpython-314.pyc +0 -0
  82. tau/builtins/tools/__pycache__/write.cpython-313.pyc +0 -0
  83. tau/builtins/tools/__pycache__/write.cpython-314.pyc +0 -0
  84. tau/builtins/tools/edit.py +215 -0
  85. tau/builtins/tools/glob.py +112 -0
  86. tau/builtins/tools/grep.py +146 -0
  87. tau/builtins/tools/ls.py +135 -0
  88. tau/builtins/tools/read.py +122 -0
  89. tau/builtins/tools/terminal.py +150 -0
  90. tau/builtins/tools/write.py +105 -0
  91. tau/commands/__init__.py +10 -0
  92. tau/commands/registry.py +71 -0
  93. tau/commands/types.py +33 -0
  94. tau/console/__init__.py +0 -0
  95. tau/console/cli.py +266 -0
  96. tau/console/commands/__init__.py +0 -0
  97. tau/console/commands/auth.py +193 -0
  98. tau/console/commands/packages.py +104 -0
  99. tau/console/commands/update.py +76 -0
  100. tau/core/__init__.py +0 -0
  101. tau/core/registry.py +102 -0
  102. tau/engine/__init__.py +47 -0
  103. tau/engine/service.py +768 -0
  104. tau/engine/types.py +163 -0
  105. tau/extensions/__init__.py +28 -0
  106. tau/extensions/api.py +928 -0
  107. tau/extensions/context.py +462 -0
  108. tau/extensions/events.py +70 -0
  109. tau/extensions/loader.py +386 -0
  110. tau/extensions/runtime.py +184 -0
  111. tau/extensions/settings.py +137 -0
  112. tau/hooks/__init__.py +112 -0
  113. tau/hooks/engine.py +237 -0
  114. tau/hooks/inference.py +21 -0
  115. tau/hooks/runtime.py +126 -0
  116. tau/hooks/service.py +121 -0
  117. tau/hooks/session.py +117 -0
  118. tau/hooks/tui.py +61 -0
  119. tau/hooks/types.py +72 -0
  120. tau/inference/__init__.py +180 -0
  121. tau/inference/api/__init__.py +0 -0
  122. tau/inference/api/audio/__init__.py +0 -0
  123. tau/inference/api/audio/base.py +29 -0
  124. tau/inference/api/audio/builtins.py +15 -0
  125. tau/inference/api/audio/elevenlabs_audio.py +183 -0
  126. tau/inference/api/audio/gemini_audio.py +95 -0
  127. tau/inference/api/audio/openai_audio.py +159 -0
  128. tau/inference/api/audio/registry.py +15 -0
  129. tau/inference/api/audio/sarvam_audio.py +163 -0
  130. tau/inference/api/audio/service.py +103 -0
  131. tau/inference/api/audio/utils.py +47 -0
  132. tau/inference/api/image/__init__.py +0 -0
  133. tau/inference/api/image/base.py +17 -0
  134. tau/inference/api/image/builtins.py +8 -0
  135. tau/inference/api/image/gemini_image.py +77 -0
  136. tau/inference/api/image/openai_image.py +103 -0
  137. tau/inference/api/image/openrouter.py +144 -0
  138. tau/inference/api/image/registry.py +15 -0
  139. tau/inference/api/image/service.py +71 -0
  140. tau/inference/api/registry.py +82 -0
  141. tau/inference/api/text/__init__.py +0 -0
  142. tau/inference/api/text/anthropic_claude_code.py +222 -0
  143. tau/inference/api/text/anthropic_messages.py +196 -0
  144. tau/inference/api/text/base.py +40 -0
  145. tau/inference/api/text/builtins.py +19 -0
  146. tau/inference/api/text/gemini_generate.py +234 -0
  147. tau/inference/api/text/github_copilot_chat.py +172 -0
  148. tau/inference/api/text/google_antigravity.py +522 -0
  149. tau/inference/api/text/mistral_chat.py +284 -0
  150. tau/inference/api/text/ollama_chat.py +200 -0
  151. tau/inference/api/text/openai_codex_responses.py +497 -0
  152. tau/inference/api/text/openai_completions.py +227 -0
  153. tau/inference/api/text/openai_responses.py +235 -0
  154. tau/inference/api/text/registry.py +50 -0
  155. tau/inference/api/text/service.py +297 -0
  156. tau/inference/api/text/types.py +7 -0
  157. tau/inference/api/text/utils.py +228 -0
  158. tau/inference/api/video/__init__.py +0 -0
  159. tau/inference/api/video/base.py +26 -0
  160. tau/inference/api/video/builtins.py +7 -0
  161. tau/inference/api/video/fal_video.py +119 -0
  162. tau/inference/api/video/openrouter_video.py +142 -0
  163. tau/inference/api/video/registry.py +15 -0
  164. tau/inference/api/video/service.py +72 -0
  165. tau/inference/model/__init__.py +0 -0
  166. tau/inference/model/registry.py +102 -0
  167. tau/inference/model/types.py +65 -0
  168. tau/inference/provider/__init__.py +0 -0
  169. tau/inference/provider/oauth/__init__.py +35 -0
  170. tau/inference/provider/oauth/anthropic_claude_code.py +286 -0
  171. tau/inference/provider/oauth/github_copilot.py +333 -0
  172. tau/inference/provider/oauth/google_antigravity.py +258 -0
  173. tau/inference/provider/oauth/openai_codex.py +309 -0
  174. tau/inference/provider/oauth/pkce.py +14 -0
  175. tau/inference/provider/oauth/types.py +46 -0
  176. tau/inference/provider/oauth/utils.py +154 -0
  177. tau/inference/provider/registry.py +141 -0
  178. tau/inference/provider/types.py +114 -0
  179. tau/inference/types.py +549 -0
  180. tau/inference/utils.py +219 -0
  181. tau/message/__init__.py +0 -0
  182. tau/message/types.py +482 -0
  183. tau/message/utils.py +178 -0
  184. tau/packages/__init__.py +11 -0
  185. tau/packages/manager.py +190 -0
  186. tau/packages/types.py +20 -0
  187. tau/packages/utils.py +67 -0
  188. tau/prompts/expand.py +58 -0
  189. tau/prompts/loader.py +69 -0
  190. tau/prompts/registry.py +45 -0
  191. tau/prompts/types.py +24 -0
  192. tau/rpc/__init__.py +8 -0
  193. tau/rpc/mode.py +783 -0
  194. tau/rpc/types.py +252 -0
  195. tau/runtime/service.py +759 -0
  196. tau/runtime/types.py +303 -0
  197. tau/session/branch_summarization.py +312 -0
  198. tau/session/compaction.py +646 -0
  199. tau/session/manager.py +652 -0
  200. tau/session/types.py +188 -0
  201. tau/session/utils.py +233 -0
  202. tau/settings/manager.py +1077 -0
  203. tau/settings/paths.py +150 -0
  204. tau/settings/storage.py +63 -0
  205. tau/settings/types.py +173 -0
  206. tau/settings/utils.py +25 -0
  207. tau/skills/loader.py +91 -0
  208. tau/skills/registry.py +70 -0
  209. tau/skills/types.py +25 -0
  210. tau/themes/loader.py +238 -0
  211. tau/themes/registry.py +108 -0
  212. tau/themes/types.py +19 -0
  213. tau/tool/__init__.py +3 -0
  214. tau/tool/registry.py +117 -0
  215. tau/tool/render.py +21 -0
  216. tau/tool/types.py +244 -0
  217. tau/trust/__init__.py +13 -0
  218. tau/trust/manager.py +80 -0
  219. tau/trust/types.py +14 -0
  220. tau/trust/utils.py +72 -0
  221. tau/tui/__init__.py +54 -0
  222. tau/tui/agent_hooks.py +346 -0
  223. tau/tui/ansi.py +330 -0
  224. tau/tui/app.py +540 -0
  225. tau/tui/autocomplete.py +33 -0
  226. tau/tui/capabilities.py +119 -0
  227. tau/tui/commands/__init__.py +3 -0
  228. tau/tui/commands/appearance.py +498 -0
  229. tau/tui/commands/auth.py +232 -0
  230. tau/tui/commands/context.py +38 -0
  231. tau/tui/commands/misc.py +82 -0
  232. tau/tui/commands/model.py +118 -0
  233. tau/tui/commands/session.py +464 -0
  234. tau/tui/component.py +268 -0
  235. tau/tui/components/__init__.py +0 -0
  236. tau/tui/components/autocomplete_manager.py +267 -0
  237. tau/tui/components/autocomplete_picker.py +143 -0
  238. tau/tui/components/box.py +90 -0
  239. tau/tui/components/command_palette.py +144 -0
  240. tau/tui/components/dynamic_border.py +19 -0
  241. tau/tui/components/file_picker.py +233 -0
  242. tau/tui/components/image.py +181 -0
  243. tau/tui/components/inline_selector.py +71 -0
  244. tau/tui/components/layout.py +1194 -0
  245. tau/tui/components/message_list.py +692 -0
  246. tau/tui/components/modal.py +97 -0
  247. tau/tui/components/model_palette.py +204 -0
  248. tau/tui/components/picker_overlay.py +174 -0
  249. tau/tui/components/prompt_overlay.py +236 -0
  250. tau/tui/components/resume_modal.py +372 -0
  251. tau/tui/components/select_list.py +222 -0
  252. tau/tui/components/settings_modal.py +274 -0
  253. tau/tui/components/settings_schema.py +203 -0
  254. tau/tui/components/spinner.py +119 -0
  255. tau/tui/components/text_input.py +396 -0
  256. tau/tui/components/text_prompt.py +82 -0
  257. tau/tui/components/tree_select_list.py +580 -0
  258. tau/tui/components/trust_screen.py +97 -0
  259. tau/tui/diff.py +114 -0
  260. tau/tui/fuzzy.py +99 -0
  261. tau/tui/input.py +496 -0
  262. tau/tui/input_handler.py +716 -0
  263. tau/tui/keybindings.py +87 -0
  264. tau/tui/markdown.py +286 -0
  265. tau/tui/message_renderers.py +31 -0
  266. tau/tui/overlay.py +326 -0
  267. tau/tui/renderer.py +378 -0
  268. tau/tui/terminal.py +499 -0
  269. tau/tui/theme.py +148 -0
  270. tau/tui/tui.py +544 -0
  271. tau/tui/ui_context.py +768 -0
  272. tau/tui/utils.py +20 -0
  273. tau/utils/__init__.py +0 -0
  274. tau/utils/http_proxy.py +221 -0
  275. tau/utils/image_processing.py +172 -0
  276. tau/utils/secrets.py +59 -0
  277. tau/utils/version_check.py +60 -0
  278. tau_coding_agent-0.1.0.dist-info/METADATA +177 -0
  279. tau_coding_agent-0.1.0.dist-info/RECORD +283 -0
  280. tau_coding_agent-0.1.0.dist-info/WHEEL +5 -0
  281. tau_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
  282. tau_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
  283. tau_coding_agent-0.1.0.dist-info/top_level.txt +1 -0
tau/tui/diff.py ADDED
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+
3
+ import difflib
4
+ import re
5
+ from typing import Callable
6
+
7
+ # Matches a standard unified diff line: prefix (+/-/ ) followed by optional
8
+ # line number then content.
9
+ _UNIFIED_LINE = re.compile(r"^([+\- ])(\s*\d*)\s?(.*)$")
10
+
11
+
12
+ def _is_diff(text: str) -> bool:
13
+ """Heuristic: return True if text looks like a unified diff."""
14
+ lines = text.splitlines()
15
+ has_marker = any(l.startswith(("---", "+++", "@@")) for l in lines[:20])
16
+ has_change = any(l.startswith(("+", "-")) and len(l) > 1 for l in lines[:20])
17
+ return has_marker and has_change
18
+
19
+
20
+ def _word_diff(old: str, new: str, inverse: Callable[[str], str]) -> tuple[str, str]:
21
+ """Highlight changed words with inverse video."""
22
+ old_words = re.split(r"(\s+)", old)
23
+ new_words = re.split(r"(\s+)", new)
24
+ sm = difflib.SequenceMatcher(None, old_words, new_words, autojunk=False)
25
+
26
+ old_out, new_out = [], []
27
+ for tag, i1, i2, j1, j2 in sm.get_opcodes():
28
+ old_chunk = "".join(old_words[i1:i2])
29
+ new_chunk = "".join(new_words[j1:j2])
30
+ if tag == "equal":
31
+ old_out.append(old_chunk)
32
+ new_out.append(new_chunk)
33
+ elif tag == "replace":
34
+ old_out.append(inverse(old_chunk) if old_chunk.strip() else old_chunk)
35
+ new_out.append(inverse(new_chunk) if new_chunk.strip() else new_chunk)
36
+ elif tag == "delete":
37
+ old_out.append(inverse(old_chunk) if old_chunk.strip() else old_chunk)
38
+ elif tag == "insert":
39
+ new_out.append(inverse(new_chunk) if new_chunk.strip() else new_chunk)
40
+
41
+ return "".join(old_out), "".join(new_out)
42
+
43
+
44
+ def render_diff(
45
+ diff_text: str,
46
+ added: Callable[[str], str],
47
+ removed: Callable[[str], str],
48
+ context: Callable[[str], str],
49
+ hunk: Callable[[str], str],
50
+ inverse: Callable[[str], str],
51
+ ) -> list[str]:
52
+ """
53
+ Render a unified diff string with ANSI colors.
54
+
55
+ - Header lines (---, +++) and hunk markers (@@): styled with ``hunk``
56
+ - Added lines (+): styled with ``added``
57
+ - Removed lines (-): styled with ``removed``
58
+ - Context lines ( ): styled with ``context``
59
+ - Adjacent single-line add/remove pairs get intra-line word highlighting
60
+ via ``inverse``.
61
+ """
62
+ raw_lines = diff_text.splitlines()
63
+ result: list[str] = []
64
+ i = 0
65
+
66
+ while i < len(raw_lines):
67
+ line = raw_lines[i]
68
+
69
+ # Header / hunk marker
70
+ if line.startswith(("---", "+++", "@@", "diff ", "index ", "new file", "deleted file")):
71
+ result.append(hunk(line))
72
+ i += 1
73
+ continue
74
+
75
+ if line.startswith("-"):
76
+ # Collect consecutive removed lines
77
+ removed_lines: list[str] = []
78
+ while i < len(raw_lines) and raw_lines[i].startswith("-"):
79
+ removed_lines.append(raw_lines[i][1:])
80
+ i += 1
81
+
82
+ # Collect consecutive added lines that immediately follow
83
+ added_lines: list[str] = []
84
+ while i < len(raw_lines) and raw_lines[i].startswith("+"):
85
+ added_lines.append(raw_lines[i][1:])
86
+ i += 1
87
+
88
+ # Intra-line diff only when it's a 1:1 change
89
+ if len(removed_lines) == 1 and len(added_lines) == 1:
90
+ old_hi, new_hi = _word_diff(removed_lines[0], added_lines[0], inverse)
91
+ result.append(removed("-" + old_hi))
92
+ result.append(added("+" + new_hi))
93
+ else:
94
+ for l in removed_lines:
95
+ result.append(removed("-" + l))
96
+ for l in added_lines:
97
+ result.append(added("+" + l))
98
+ continue
99
+
100
+ if line.startswith("+"):
101
+ result.append(added(line))
102
+ i += 1
103
+ continue
104
+
105
+ if line.startswith(" "):
106
+ result.append(context(line))
107
+ i += 1
108
+ continue
109
+
110
+ # Unrecognised line — pass through dimmed
111
+ result.append(context(line))
112
+ i += 1
113
+
114
+ return result
tau/tui/fuzzy.py ADDED
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass
8
+ class FuzzyMatch:
9
+ matches: bool
10
+ score: float
11
+
12
+
13
+ def fuzzy_match(query: str, text: str) -> FuzzyMatch:
14
+ """
15
+ Match if all query characters appear in order (not necessarily consecutive).
16
+ Lower score = better match. Rewards consecutive runs and word-boundary hits.
17
+ """
18
+ q = query.lower()
19
+ t = text.lower()
20
+
21
+ def _match(q: str) -> FuzzyMatch:
22
+ if not q:
23
+ return FuzzyMatch(True, 0)
24
+ if len(q) > len(t):
25
+ return FuzzyMatch(False, 0)
26
+
27
+ qi = 0
28
+ score = 0.0
29
+ last = -1
30
+ consecutive = 0
31
+
32
+ for i, ch in enumerate(t):
33
+ if qi < len(q) and ch == q[qi]:
34
+ is_boundary = i == 0 or bool(re.match(r"[\s\-_./:]", t[i - 1]))
35
+ if last == i - 1:
36
+ consecutive += 1
37
+ score -= consecutive * 5
38
+ else:
39
+ consecutive = 0
40
+ if last >= 0:
41
+ score += (i - last - 1) * 2
42
+ if is_boundary:
43
+ score -= 10
44
+ score += i * 0.1
45
+ last = i
46
+ qi += 1
47
+
48
+ if qi < len(q):
49
+ return FuzzyMatch(False, 0)
50
+ if q == t:
51
+ score -= 100
52
+ return FuzzyMatch(True, score)
53
+
54
+ result = _match(q)
55
+ if result.matches:
56
+ return result
57
+
58
+ # Try swapped alphanumeric order (e.g. "v3" matches "3v")
59
+ m = re.match(r"^(?P<a>[a-z]+)(?P<d>[0-9]+)$", q) or re.match(r"^(?P<d>[0-9]+)(?P<a>[a-z]+)$", q)
60
+ if m:
61
+ swapped = (m.group("d") + m.group("a")) if "a" in m.groupdict() else ""
62
+ if swapped:
63
+ alt = _match(swapped)
64
+ if alt.matches:
65
+ return FuzzyMatch(True, alt.score + 5)
66
+
67
+ return result
68
+
69
+
70
+ def fuzzy_filter(items: list, query: str, get_text) -> list:
71
+ """
72
+ Filter and sort items by fuzzy match quality (best matches first).
73
+ Supports space-separated tokens — all tokens must match.
74
+ `get_text` is a callable that extracts the searchable string from an item.
75
+ """
76
+ if not query.strip():
77
+ return items
78
+
79
+ tokens = [t for t in query.strip().split() if t]
80
+ if not tokens:
81
+ return items
82
+
83
+ scored: list[tuple[object, float]] = []
84
+ for item in items:
85
+ text = get_text(item)
86
+ total = 0.0
87
+ ok = True
88
+ for token in tokens:
89
+ m = fuzzy_match(token, text)
90
+ if m.matches:
91
+ total += m.score
92
+ else:
93
+ ok = False
94
+ break
95
+ if ok:
96
+ scored.append((item, total))
97
+
98
+ scored.sort(key=lambda x: x[1])
99
+ return [item for item, _ in scored]