lithermes-ai 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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +245 -0
  3. package/README_Ko-KR.md +245 -0
  4. package/assets/lithermes-plugin/NOTICE.md +37 -0
  5. package/assets/lithermes-plugin/README.md +40 -0
  6. package/assets/lithermes-plugin/__init__.py +179 -0
  7. package/assets/lithermes-plugin/core.py +853 -0
  8. package/assets/lithermes-plugin/litgoal/__init__.py +10 -0
  9. package/assets/lithermes-plugin/litgoal/cli.py +133 -0
  10. package/assets/lithermes-plugin/litgoal/hook.py +48 -0
  11. package/assets/lithermes-plugin/litgoal/model.py +171 -0
  12. package/assets/lithermes-plugin/litgoal/runtime.py +273 -0
  13. package/assets/lithermes-plugin/litgoal/store.py +93 -0
  14. package/assets/lithermes-plugin/litgoal/tools.py +228 -0
  15. package/assets/lithermes-plugin/payload-version.json +471 -0
  16. package/assets/lithermes-plugin/plugin.yaml +9 -0
  17. package/assets/lithermes-plugin/skills/ai-slop-remover/SKILL.md +142 -0
  18. package/assets/lithermes-plugin/skills/comment-checker/SKILL.md +50 -0
  19. package/assets/lithermes-plugin/skills/debugging/SKILL.md +116 -0
  20. package/assets/lithermes-plugin/skills/debugging/references/methodology/00-setup.md +108 -0
  21. package/assets/lithermes-plugin/skills/debugging/references/methodology/02-investigate.md +121 -0
  22. package/assets/lithermes-plugin/skills/debugging/references/methodology/04-oracle-triple.md +136 -0
  23. package/assets/lithermes-plugin/skills/debugging/references/methodology/05-escalate.md +69 -0
  24. package/assets/lithermes-plugin/skills/debugging/references/methodology/06-fix.md +116 -0
  25. package/assets/lithermes-plugin/skills/debugging/references/methodology/08-qa.md +94 -0
  26. package/assets/lithermes-plugin/skills/debugging/references/methodology/09-cleanup.md +164 -0
  27. package/assets/lithermes-plugin/skills/debugging/references/methodology/partial-runtime-evidence.md +229 -0
  28. package/assets/lithermes-plugin/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
  29. package/assets/lithermes-plugin/skills/debugging/references/runtimes/go.md +252 -0
  30. package/assets/lithermes-plugin/skills/debugging/references/runtimes/native-binary.md +484 -0
  31. package/assets/lithermes-plugin/skills/debugging/references/runtimes/node.md +260 -0
  32. package/assets/lithermes-plugin/skills/debugging/references/runtimes/python.md +248 -0
  33. package/assets/lithermes-plugin/skills/debugging/references/runtimes/rust.md +234 -0
  34. package/assets/lithermes-plugin/skills/debugging/references/tools/ghidra.md +212 -0
  35. package/assets/lithermes-plugin/skills/debugging/references/tools/playwright-cli.md +194 -0
  36. package/assets/lithermes-plugin/skills/debugging/references/tools/pwndbg.md +263 -0
  37. package/assets/lithermes-plugin/skills/debugging/references/tools/pwntools.md +265 -0
  38. package/assets/lithermes-plugin/skills/frontend-ui-ux/SKILL.md +77 -0
  39. package/assets/lithermes-plugin/skills/lit-plan/SKILL.md +374 -0
  40. package/assets/lithermes-plugin/skills/litgoal/.gitkeep +0 -0
  41. package/assets/lithermes-plugin/skills/litgoal/SKILL.md +207 -0
  42. package/assets/lithermes-plugin/skills/litwork/SKILL.md +262 -0
  43. package/assets/lithermes-plugin/skills/lsp/SKILL.md +53 -0
  44. package/assets/lithermes-plugin/skills/programming/SKILL.md +463 -0
  45. package/assets/lithermes-plugin/skills/programming/references/go/README.md +90 -0
  46. package/assets/lithermes-plugin/skills/programming/references/go/backend-stack.md +641 -0
  47. package/assets/lithermes-plugin/skills/programming/references/go/bootstrap.md +328 -0
  48. package/assets/lithermes-plugin/skills/programming/references/go/bubbletea-v2.md +360 -0
  49. package/assets/lithermes-plugin/skills/programming/references/go/cobra-stack.md +468 -0
  50. package/assets/lithermes-plugin/skills/programming/references/go/concurrency.md +362 -0
  51. package/assets/lithermes-plugin/skills/programming/references/go/data-modeling.md +329 -0
  52. package/assets/lithermes-plugin/skills/programming/references/go/error-handling.md +359 -0
  53. package/assets/lithermes-plugin/skills/programming/references/go/golangci-strict.md +236 -0
  54. package/assets/lithermes-plugin/skills/programming/references/go/grpc-connect.md +375 -0
  55. package/assets/lithermes-plugin/skills/programming/references/go/libraries.md +337 -0
  56. package/assets/lithermes-plugin/skills/programming/references/go/one-liners.md +202 -0
  57. package/assets/lithermes-plugin/skills/programming/references/go/sqlc-pgx.md +471 -0
  58. package/assets/lithermes-plugin/skills/programming/references/go/testing.md +467 -0
  59. package/assets/lithermes-plugin/skills/programming/references/go/type-patterns.md +298 -0
  60. package/assets/lithermes-plugin/skills/programming/references/python/README.md +314 -0
  61. package/assets/lithermes-plugin/skills/programming/references/python/async-anyio.md +442 -0
  62. package/assets/lithermes-plugin/skills/programming/references/python/data-modeling.md +233 -0
  63. package/assets/lithermes-plugin/skills/programming/references/python/data-processing.md +133 -0
  64. package/assets/lithermes-plugin/skills/programming/references/python/error-handling.md +218 -0
  65. package/assets/lithermes-plugin/skills/programming/references/python/fastapi-stack.md +316 -0
  66. package/assets/lithermes-plugin/skills/programming/references/python/httpx2-optimization.md +360 -0
  67. package/assets/lithermes-plugin/skills/programming/references/python/libraries.md +307 -0
  68. package/assets/lithermes-plugin/skills/programming/references/python/one-liners.md +268 -0
  69. package/assets/lithermes-plugin/skills/programming/references/python/orjson-stack.md +378 -0
  70. package/assets/lithermes-plugin/skills/programming/references/python/pydantic-ai.md +285 -0
  71. package/assets/lithermes-plugin/skills/programming/references/python/pyproject-strict.md +232 -0
  72. package/assets/lithermes-plugin/skills/programming/references/python/textual-tui.md +201 -0
  73. package/assets/lithermes-plugin/skills/programming/references/python/type-patterns.md +176 -0
  74. package/assets/lithermes-plugin/skills/programming/references/rust/README.md +317 -0
  75. package/assets/lithermes-plugin/skills/programming/references/rust/async-tokio.md +299 -0
  76. package/assets/lithermes-plugin/skills/programming/references/rust/axum-stack.md +467 -0
  77. package/assets/lithermes-plugin/skills/programming/references/rust/cargo-strict.md +317 -0
  78. package/assets/lithermes-plugin/skills/programming/references/rust/clap-stack.md +409 -0
  79. package/assets/lithermes-plugin/skills/programming/references/rust/concurrency.md +375 -0
  80. package/assets/lithermes-plugin/skills/programming/references/rust/libraries.md +439 -0
  81. package/assets/lithermes-plugin/skills/programming/references/rust/one-liners.md +291 -0
  82. package/assets/lithermes-plugin/skills/programming/references/rust/proptest-insta.md +429 -0
  83. package/assets/lithermes-plugin/skills/programming/references/rust/type-state.md +354 -0
  84. package/assets/lithermes-plugin/skills/programming/references/rust/unsafe-discipline.md +250 -0
  85. package/assets/lithermes-plugin/skills/programming/references/rust/zero-cost-safety.md +527 -0
  86. package/assets/lithermes-plugin/skills/programming/references/rust-ub/README.md +289 -0
  87. package/assets/lithermes-plugin/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
  88. package/assets/lithermes-plugin/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
  89. package/assets/lithermes-plugin/skills/programming/references/typescript/README.md +195 -0
  90. package/assets/lithermes-plugin/skills/programming/references/typescript/backend-hono.md +672 -0
  91. package/assets/lithermes-plugin/skills/programming/references/typescript/bootstrap.md +199 -0
  92. package/assets/lithermes-plugin/skills/programming/references/typescript/data-modeling.md +202 -0
  93. package/assets/lithermes-plugin/skills/programming/references/typescript/error-handling.md +169 -0
  94. package/assets/lithermes-plugin/skills/programming/references/typescript/tsconfig-strict.md +152 -0
  95. package/assets/lithermes-plugin/skills/programming/references/typescript/type-patterns.md +196 -0
  96. package/assets/lithermes-plugin/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
  97. package/assets/lithermes-plugin/skills/programming/scripts/go/new-project.py +138 -0
  98. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/.editorconfig +13 -0
  99. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/.golangci.yml +95 -0
  100. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
  101. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
  102. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
  103. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/ci.yml +37 -0
  104. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/config.go +24 -0
  105. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/gitignore +15 -0
  106. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
  107. package/assets/lithermes-plugin/skills/programming/scripts/go/templates/run.go +15 -0
  108. package/assets/lithermes-plugin/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
  109. package/assets/lithermes-plugin/skills/programming/scripts/python/new-project.py +172 -0
  110. package/assets/lithermes-plugin/skills/programming/scripts/python/new-script.py +116 -0
  111. package/assets/lithermes-plugin/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
  112. package/assets/lithermes-plugin/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
  113. package/assets/lithermes-plugin/skills/programming/scripts/rust/new-project.py +175 -0
  114. package/assets/lithermes-plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
  115. package/assets/lithermes-plugin/skills/programming/scripts/typescript/new-project.ts +177 -0
  116. package/assets/lithermes-plugin/skills/refactor/SKILL.md +770 -0
  117. package/assets/lithermes-plugin/skills/remove-ai-slops/SKILL.md +335 -0
  118. package/assets/lithermes-plugin/skills/review-work/SKILL.md +562 -0
  119. package/assets/lithermes-plugin/skills/rules/SKILL.md +41 -0
  120. package/assets/lithermes-plugin/skills/start-work/SKILL.md +332 -0
  121. package/bin/lithermes.js +8 -0
  122. package/cover.png +0 -0
  123. package/package.json +39 -0
  124. package/src/cli.js +129 -0
  125. package/src/lib/check.js +94 -0
  126. package/src/lib/config.js +170 -0
  127. package/src/lib/files.js +65 -0
  128. package/src/lib/hermesDiscovery.js +50 -0
  129. package/src/lib/hud.js +121 -0
  130. package/src/lib/install.js +159 -0
  131. package/src/lib/patch.js +153 -0
  132. package/src/lib/skins.js +113 -0
  133. package/src/lib/spinner.js +104 -0
@@ -0,0 +1,93 @@
1
+ """Path resolution + atomic persistence for the litgoal durable runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import tempfile
8
+ from datetime import datetime, timezone
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from . import model
13
+
14
+ # Anchor every litgoal path under the constant declared in core (dual-context).
15
+ try: # installed as lithermes.litgoal.store
16
+ from ..core import LITGOAL_STATE_DIRNAME
17
+ except (ImportError, ModuleNotFoundError): # pragma: no cover - standalone import fallback
18
+ try:
19
+ from core import LITGOAL_STATE_DIRNAME # type: ignore
20
+ except (ImportError, ModuleNotFoundError):
21
+ LITGOAL_STATE_DIRNAME = "litgoal"
22
+
23
+
24
+ def _utc_now() -> str:
25
+ return datetime.now(timezone.utc).isoformat()
26
+
27
+
28
+ def state_dir(workspace: Path) -> Path:
29
+ return Path(workspace) / ".hermes" / "lithermes" / LITGOAL_STATE_DIRNAME
30
+
31
+
32
+ def goals_path(workspace: Path) -> Path:
33
+ return state_dir(workspace) / "goals.json"
34
+
35
+
36
+ def ledger_path(workspace: Path) -> Path:
37
+ return state_dir(workspace) / "ledger.jsonl"
38
+
39
+
40
+ def evidence_dir(workspace: Path) -> Path:
41
+ return state_dir(workspace) / "evidence"
42
+
43
+
44
+ def brief_path(workspace: Path) -> Path:
45
+ return state_dir(workspace) / "brief.md"
46
+
47
+
48
+ def load_or_create(workspace: Path) -> model.LitgoalState:
49
+ path = goals_path(workspace)
50
+ if path.exists():
51
+ data = json.loads(path.read_text(encoding="utf-8"))
52
+ return model.LitgoalState.from_dict(data)
53
+ return model.LitgoalState(created_at=_utc_now(), updated_at=_utc_now())
54
+
55
+
56
+ def save(workspace: Path, state: model.LitgoalState) -> None:
57
+ state.updated_at = _utc_now()
58
+ if not state.created_at:
59
+ state.created_at = state.updated_at
60
+ target = goals_path(workspace)
61
+ target.parent.mkdir(parents=True, exist_ok=True)
62
+ payload = json.dumps(model.to_dict(state), indent=2, sort_keys=False, ensure_ascii=False)
63
+ # Atomic: write to a temp file in the same dir, fsync, then os.replace.
64
+ fd, tmp_name = tempfile.mkstemp(dir=str(target.parent), suffix=".tmp")
65
+ try:
66
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
67
+ handle.write(payload + "\n")
68
+ handle.flush()
69
+ os.fsync(handle.fileno())
70
+ os.replace(tmp_name, target)
71
+ # Best-effort: fsync the parent directory so the rename is durable.
72
+ try:
73
+ dir_fd = os.open(str(target.parent), os.O_RDONLY)
74
+ try:
75
+ os.fsync(dir_fd)
76
+ finally:
77
+ os.close(dir_fd)
78
+ except (OSError, PermissionError):
79
+ pass # platforms that disallow dir-fsync degrade silently
80
+ finally:
81
+ if os.path.exists(tmp_name):
82
+ os.remove(tmp_name)
83
+
84
+
85
+ def append_ledger(workspace: Path, event: dict[str, Any]) -> None:
86
+ path = ledger_path(workspace)
87
+ path.parent.mkdir(parents=True, exist_ok=True)
88
+ entry = {"at": _utc_now(), **event}
89
+ # NOTE: ledger appends are best-effort append-durable; no fsync here to keep
90
+ # high-frequency event writes cheap. Data loss on crash is limited to the
91
+ # last unflushed entry; goals.json (the source of truth) is fsync-durable.
92
+ with path.open("a", encoding="utf-8") as handle:
93
+ handle.write(json.dumps(entry, ensure_ascii=False) + "\n")
@@ -0,0 +1,228 @@
1
+ """Model-facing litgoal tools (registered via ctx.register_tool).
2
+
3
+ These layer durable criteria/evidence/checkpoint/steering/gate tracking on top of
4
+ Hermes' native goal system. They operate on the current workspace (cwd). Each
5
+ returns a JSON string for the model.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Any, Callable
13
+
14
+ from . import model, runtime, store
15
+
16
+ TOOLSET = "lithermes-goal"
17
+
18
+
19
+ def _workspace() -> Path:
20
+ return Path.cwd()
21
+
22
+
23
+ def _json(obj: Any) -> str:
24
+ return json.dumps(obj, ensure_ascii=False, indent=2)
25
+
26
+
27
+ def _snapshot(workspace: Path) -> dict[str, Any]:
28
+ goal = runtime.get_active(workspace)
29
+ if goal is None:
30
+ return {"active_goal": None}
31
+ gate = runtime.quality_gate(workspace)
32
+ return {
33
+ "active_goal": {
34
+ "id": goal.id,
35
+ "objective": goal.objective,
36
+ "title": goal.title,
37
+ "status": goal.status,
38
+ "criteria": [
39
+ {
40
+ "id": c.id,
41
+ "scenario": c.scenario,
42
+ "qa_channel": c.qa_channel,
43
+ "test_ref": c.test_ref,
44
+ "status": c.status,
45
+ "evidence_kinds": sorted({e.kind for e in c.evidence}),
46
+ }
47
+ for c in goal.criteria
48
+ ],
49
+ "unresolved_blockers": [b.id for b in goal.review_blockers if not b.resolved],
50
+ },
51
+ "quality_gate": gate,
52
+ }
53
+
54
+
55
+ # -- tool handlers (args: dict) -> str --------------------------------------
56
+
57
+ def tool_goal_status(args: dict | None = None, **kwargs) -> str:
58
+ return _json(_snapshot(_workspace()))
59
+
60
+
61
+ def tool_goal_set(args: dict, **kwargs) -> str:
62
+ ws = _workspace()
63
+ objective = str((args or {}).get("objective", "")).strip()
64
+ title = str((args or {}).get("title", "")).strip()
65
+ criteria = (args or {}).get("criteria") or []
66
+ runtime.create_goal(ws, objective, title=title, criteria=criteria)
67
+ return _json(_snapshot(ws))
68
+
69
+
70
+ def tool_goal_add_criterion(args: dict, **kwargs) -> str:
71
+ ws = _workspace()
72
+ crit = runtime.add_criterion(
73
+ ws,
74
+ str((args or {}).get("scenario", "")),
75
+ qa_channel=str((args or {}).get("qa_channel", "")),
76
+ test_ref=str((args or {}).get("test_ref", "")),
77
+ )
78
+ return _json({"criterion_id": crit.id, **_snapshot(ws)})
79
+
80
+
81
+ def tool_goal_evidence(args: dict, **kwargs) -> str:
82
+ ws = _workspace()
83
+ runtime.add_evidence(
84
+ ws,
85
+ str((args or {}).get("criterion_id", "")),
86
+ str((args or {}).get("kind", "note")),
87
+ str((args or {}).get("ref", "")),
88
+ str((args or {}).get("detail", "")),
89
+ )
90
+ return _json(_snapshot(ws))
91
+
92
+
93
+ def tool_goal_criterion_status(args: dict, **kwargs) -> str:
94
+ ws = _workspace()
95
+ runtime.set_criterion_status(
96
+ ws, str((args or {}).get("criterion_id", "")), str((args or {}).get("status", ""))
97
+ )
98
+ return _json(_snapshot(ws))
99
+
100
+
101
+ def tool_goal_steer(args: dict, **kwargs) -> str:
102
+ ws = _workspace()
103
+ try:
104
+ runtime.record_steering(
105
+ ws,
106
+ str((args or {}).get("directive", "")),
107
+ kind=str((args or {}).get("kind", "redirect")) or "redirect",
108
+ )
109
+ except ValueError as exc:
110
+ return _json({"rejected": True, "reason": str(exc), **_snapshot(ws)})
111
+ return _json(_snapshot(ws))
112
+
113
+
114
+ def tool_goal_checkpoint(args: dict, **kwargs) -> str:
115
+ ws = _workspace()
116
+ runtime.record_checkpoint(
117
+ ws,
118
+ str((args or {}).get("summary", "")),
119
+ active_criterion=str((args or {}).get("active_criterion", "")),
120
+ )
121
+ return _json(_snapshot(ws))
122
+
123
+
124
+ def tool_goal_complete(args: dict | None = None, **kwargs) -> str:
125
+ return _json(runtime.complete_goal(_workspace()))
126
+
127
+
128
+ def _spec(name: str, description: str, handler: Callable, properties: dict, required: list[str]) -> dict:
129
+ return {
130
+ "name": name,
131
+ "toolset": TOOLSET,
132
+ "description": description,
133
+ "handler": handler,
134
+ "schema": {
135
+ "type": "object",
136
+ "properties": properties,
137
+ "required": required,
138
+ "additionalProperties": False,
139
+ },
140
+ }
141
+
142
+
143
+ TOOL_SPECS: list[dict] = [
144
+ _spec("goal_status", "Show the active LitHermes litgoal, its criteria, evidence, and quality gate.", tool_goal_status, {}, []),
145
+ _spec(
146
+ "goal_set",
147
+ "Create the active litgoal with an objective and optional upfront success criteria.",
148
+ tool_goal_set,
149
+ {
150
+ "objective": {"type": "string", "description": "the concrete user-visible objective"},
151
+ "title": {"type": "string"},
152
+ "criteria": {
153
+ "type": "array",
154
+ "items": {
155
+ "type": "object",
156
+ "properties": {
157
+ "scenario": {"type": "string"},
158
+ "qa_channel": {"type": "string", "enum": ["http", "tmux", "browser", "computer", "cli"]},
159
+ "test_ref": {"type": "string"},
160
+ },
161
+ "required": ["scenario"],
162
+ },
163
+ },
164
+ },
165
+ ["objective"],
166
+ ),
167
+ _spec(
168
+ "goal_add_criterion",
169
+ "Add one success criterion (scenario + QA channel + test reference) to the active goal.",
170
+ tool_goal_add_criterion,
171
+ {
172
+ "scenario": {"type": "string"},
173
+ "qa_channel": {"type": "string", "enum": ["http", "tmux", "browser", "computer", "cli"]},
174
+ "test_ref": {"type": "string"},
175
+ },
176
+ ["scenario"],
177
+ ),
178
+ _spec(
179
+ "goal_evidence",
180
+ "Attach evidence (red|green|scenario|cleanup|note) to a criterion.",
181
+ tool_goal_evidence,
182
+ {
183
+ "criterion_id": {"type": "string"},
184
+ "kind": {"type": "string", "enum": ["red", "green", "scenario", "cleanup", "note"]},
185
+ "ref": {"type": "string", "description": "test id or artifact path"},
186
+ "detail": {"type": "string"},
187
+ },
188
+ ["criterion_id", "kind", "ref"],
189
+ ),
190
+ _spec(
191
+ "goal_criterion_status",
192
+ "Set a criterion status (pending|in_progress|blocked|pass|fail).",
193
+ tool_goal_criterion_status,
194
+ {
195
+ "criterion_id": {"type": "string"},
196
+ "status": {"type": "string", "enum": list(model.CRITERION_STATUSES)},
197
+ },
198
+ ["criterion_id", "status"],
199
+ ),
200
+ _spec(
201
+ "goal_steer",
202
+ "Record a steering directive that redirects or extends the active goal mid-flight. "
203
+ "Refused if it tries to weaken the completion gate (skip tests/QA/review or auto-complete).",
204
+ tool_goal_steer,
205
+ {
206
+ "directive": {"type": "string"},
207
+ "kind": {"type": "string", "enum": list(model.STEERING_KINDS)},
208
+ },
209
+ ["directive"],
210
+ ),
211
+ _spec("goal_checkpoint", "Record a durable checkpoint snapshot for resume.", tool_goal_checkpoint, {"summary": {"type": "string"}, "active_criterion": {"type": "string"}}, ["summary"]),
212
+ _spec("goal_complete", "Attempt to complete the goal; refused with reasons unless the quality gate passes.", tool_goal_complete, {}, []),
213
+ ]
214
+
215
+
216
+ def register_tools(ctx) -> list[str]:
217
+ """Register every litgoal tool on the plugin context. Returns the names."""
218
+ names: list[str] = []
219
+ for spec in TOOL_SPECS:
220
+ ctx.register_tool(
221
+ name=spec["name"],
222
+ toolset=spec["toolset"],
223
+ schema=spec["schema"],
224
+ handler=spec["handler"],
225
+ description=spec["description"],
226
+ )
227
+ names.append(spec["name"])
228
+ return names