agent-sin 0.1.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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/assets/logo.png +0 -0
- package/builtin-skills/_shared/_models_lib.py +227 -0
- package/builtin-skills/_shared/_profile_lib.py +98 -0
- package/builtin-skills/_shared/_schedules_lib.py +313 -0
- package/builtin-skills/_shared/_skill_settings_lib.py +153 -0
- package/builtin-skills/_shared/i18n.py +26 -0
- package/builtin-skills/memo-delete/main.py +155 -0
- package/builtin-skills/memo-delete/skill.yaml +57 -0
- package/builtin-skills/memo-index/main.py +178 -0
- package/builtin-skills/memo-index/skill.yaml +53 -0
- package/builtin-skills/memo-save/README.md +5 -0
- package/builtin-skills/memo-save/main.py +74 -0
- package/builtin-skills/memo-save/skill.yaml +52 -0
- package/builtin-skills/memo-search/README.md +10 -0
- package/builtin-skills/memo-search/main.py +97 -0
- package/builtin-skills/memo-search/skill.yaml +51 -0
- package/builtin-skills/memo-vector-search/main.py +121 -0
- package/builtin-skills/memo-vector-search/skill.yaml +53 -0
- package/builtin-skills/model-add/main.py +180 -0
- package/builtin-skills/model-add/skill.yaml +112 -0
- package/builtin-skills/model-list/main.py +93 -0
- package/builtin-skills/model-list/skill.yaml +48 -0
- package/builtin-skills/model-set/main.py +123 -0
- package/builtin-skills/model-set/skill.yaml +69 -0
- package/builtin-skills/profile-delete/_profile_lib.py +98 -0
- package/builtin-skills/profile-delete/main.py +98 -0
- package/builtin-skills/profile-delete/skill.yaml +64 -0
- package/builtin-skills/profile-edit/_profile_lib.py +98 -0
- package/builtin-skills/profile-edit/main.py +97 -0
- package/builtin-skills/profile-edit/skill.yaml +72 -0
- package/builtin-skills/profile-save/main.py +52 -0
- package/builtin-skills/profile-save/skill.yaml +69 -0
- package/builtin-skills/schedule-add/_schedules_lib.py +303 -0
- package/builtin-skills/schedule-add/main.py +137 -0
- package/builtin-skills/schedule-add/skill.yaml +94 -0
- package/builtin-skills/schedule-list/_schedules_lib.py +303 -0
- package/builtin-skills/schedule-list/main.py +86 -0
- package/builtin-skills/schedule-list/skill.yaml +45 -0
- package/builtin-skills/schedule-remove/_schedules_lib.py +303 -0
- package/builtin-skills/schedule-remove/main.py +69 -0
- package/builtin-skills/schedule-remove/skill.yaml +49 -0
- package/builtin-skills/schedule-toggle/_schedules_lib.py +303 -0
- package/builtin-skills/schedule-toggle/main.py +78 -0
- package/builtin-skills/schedule-toggle/skill.yaml +61 -0
- package/builtin-skills/skills-disable/main.py +63 -0
- package/builtin-skills/skills-disable/skill.yaml +52 -0
- package/builtin-skills/skills-enable/main.py +62 -0
- package/builtin-skills/skills-enable/skill.yaml +51 -0
- package/builtin-skills/todo-add/main.py +68 -0
- package/builtin-skills/todo-add/skill.yaml +53 -0
- package/builtin-skills/todo-delete/main.py +65 -0
- package/builtin-skills/todo-delete/skill.yaml +47 -0
- package/builtin-skills/todo-done/main.py +75 -0
- package/builtin-skills/todo-done/skill.yaml +47 -0
- package/builtin-skills/todo-list/main.py +91 -0
- package/builtin-skills/todo-list/skill.yaml +48 -0
- package/builtin-skills/todo-tick/main.py +125 -0
- package/builtin-skills/todo-tick/skill.yaml +48 -0
- package/dist/builder/build-action-classifier.d.ts +18 -0
- package/dist/builder/build-action-classifier.js +142 -0
- package/dist/builder/build-commands.d.ts +19 -0
- package/dist/builder/build-commands.js +133 -0
- package/dist/builder/build-flow.d.ts +72 -0
- package/dist/builder/build-flow.js +416 -0
- package/dist/builder/builder-session.d.ts +117 -0
- package/dist/builder/builder-session.js +1129 -0
- package/dist/builder/conversation-router.d.ts +22 -0
- package/dist/builder/conversation-router.js +69 -0
- package/dist/builder/intent-runtime-store.d.ts +7 -0
- package/dist/builder/intent-runtime-store.js +60 -0
- package/dist/builder/progress-format.d.ts +7 -0
- package/dist/builder/progress-format.js +46 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +2835 -0
- package/dist/cli/spinner.d.ts +30 -0
- package/dist/cli/spinner.js +164 -0
- package/dist/core/ai-provider.d.ts +75 -0
- package/dist/core/ai-provider.js +678 -0
- package/dist/core/builtin-skills.d.ts +27 -0
- package/dist/core/builtin-skills.js +120 -0
- package/dist/core/chat-engine.d.ts +70 -0
- package/dist/core/chat-engine.js +812 -0
- package/dist/core/config.d.ts +127 -0
- package/dist/core/config.js +1379 -0
- package/dist/core/daily-memory-promotion.d.ts +21 -0
- package/dist/core/daily-memory-promotion.js +422 -0
- package/dist/core/i18n.d.ts +23 -0
- package/dist/core/i18n.js +167 -0
- package/dist/core/info-lines.d.ts +5 -0
- package/dist/core/info-lines.js +39 -0
- package/dist/core/input-schema.d.ts +2 -0
- package/dist/core/input-schema.js +156 -0
- package/dist/core/intent-router.d.ts +27 -0
- package/dist/core/intent-router.js +160 -0
- package/dist/core/logger.d.ts +60 -0
- package/dist/core/logger.js +240 -0
- package/dist/core/memory.d.ts +10 -0
- package/dist/core/memory.js +72 -0
- package/dist/core/message-utils.d.ts +13 -0
- package/dist/core/message-utils.js +104 -0
- package/dist/core/notifier.d.ts +17 -0
- package/dist/core/notifier.js +424 -0
- package/dist/core/output-writer.d.ts +13 -0
- package/dist/core/output-writer.js +100 -0
- package/dist/core/plan-decision.d.ts +16 -0
- package/dist/core/plan-decision.js +88 -0
- package/dist/core/profile-memory.d.ts +17 -0
- package/dist/core/profile-memory.js +142 -0
- package/dist/core/runtime.d.ts +50 -0
- package/dist/core/runtime.js +187 -0
- package/dist/core/scheduler.d.ts +28 -0
- package/dist/core/scheduler.js +155 -0
- package/dist/core/secrets.d.ts +31 -0
- package/dist/core/secrets.js +214 -0
- package/dist/core/service.d.ts +35 -0
- package/dist/core/service.js +479 -0
- package/dist/core/skill-planner.d.ts +24 -0
- package/dist/core/skill-planner.js +100 -0
- package/dist/core/skill-registry.d.ts +98 -0
- package/dist/core/skill-registry.js +319 -0
- package/dist/core/skill-scaffold.d.ts +33 -0
- package/dist/core/skill-scaffold.js +256 -0
- package/dist/core/skill-settings.d.ts +11 -0
- package/dist/core/skill-settings.js +63 -0
- package/dist/core/transfer.d.ts +31 -0
- package/dist/core/transfer.js +270 -0
- package/dist/core/update-notifier.d.ts +2 -0
- package/dist/core/update-notifier.js +140 -0
- package/dist/discord/bot.d.ts +96 -0
- package/dist/discord/bot.js +2424 -0
- package/dist/runtimes/codex-app-server.d.ts +53 -0
- package/dist/runtimes/codex-app-server.js +305 -0
- package/dist/runtimes/python-runner.d.ts +7 -0
- package/dist/runtimes/python-runner.js +302 -0
- package/dist/runtimes/typescript-runner.d.ts +5 -0
- package/dist/runtimes/typescript-runner.js +172 -0
- package/dist/skills-sdk/types.d.ts +38 -0
- package/dist/skills-sdk/types.js +1 -0
- package/dist/telegram/bot.d.ts +94 -0
- package/dist/telegram/bot.js +1219 -0
- package/install.ps1 +132 -0
- package/install.sh +130 -0
- package/package.json +60 -0
- package/templates/skill-python/main.py +74 -0
- package/templates/skill-python/skill.yaml +48 -0
- package/templates/skill-typescript/main.ts +87 -0
- package/templates/skill-typescript/skill.yaml +42 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"""Common helpers for schedule-* builtin skills.
|
|
2
|
+
|
|
3
|
+
Provides load / dump / cron-validation helpers for the
|
|
4
|
+
~/.agent-sin/schedules.yaml file. PyYAML is used if available;
|
|
5
|
+
otherwise a minimal hand-written parser/serializer (sufficient for the
|
|
6
|
+
restricted schedules schema) is used.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import tempfile
|
|
13
|
+
from typing import Any, Dict, List
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
import yaml as _yaml # type: ignore
|
|
17
|
+
HAS_PYYAML = True
|
|
18
|
+
except Exception:
|
|
19
|
+
HAS_PYYAML = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
CANONICAL_KEY_ORDER = [
|
|
23
|
+
"id",
|
|
24
|
+
"description",
|
|
25
|
+
"cron",
|
|
26
|
+
"skill",
|
|
27
|
+
"args",
|
|
28
|
+
"enabled",
|
|
29
|
+
"approve",
|
|
30
|
+
"timezone",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def schedules_path(workspace: str) -> str:
|
|
35
|
+
return os.path.join(workspace, "schedules.yaml")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def legacy_schedules_path(workspace: str) -> str:
|
|
39
|
+
return os.path.join(workspace, "schedules", "schedules.yaml")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def migrate_legacy_schedules(workspace: str) -> None:
|
|
43
|
+
target = schedules_path(workspace)
|
|
44
|
+
legacy = legacy_schedules_path(workspace)
|
|
45
|
+
if os.path.exists(target) or not os.path.exists(legacy):
|
|
46
|
+
return
|
|
47
|
+
with open(legacy, "r", encoding="utf-8") as src:
|
|
48
|
+
content = src.read()
|
|
49
|
+
with open(target, "w", encoding="utf-8") as dst:
|
|
50
|
+
dst.write(content)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def load_schedules(workspace: str) -> List[Dict[str, Any]]:
|
|
54
|
+
migrate_legacy_schedules(workspace)
|
|
55
|
+
path = schedules_path(workspace)
|
|
56
|
+
if not os.path.exists(path):
|
|
57
|
+
return []
|
|
58
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
59
|
+
raw = f.read()
|
|
60
|
+
return _parse_schedules(raw)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _parse_schedules(raw: str) -> List[Dict[str, Any]]:
|
|
64
|
+
if not raw.strip():
|
|
65
|
+
return []
|
|
66
|
+
if HAS_PYYAML:
|
|
67
|
+
data = _yaml.safe_load(raw) or {}
|
|
68
|
+
else:
|
|
69
|
+
data = _minimal_parse(raw)
|
|
70
|
+
items = data.get("schedules") if isinstance(data, dict) else None
|
|
71
|
+
return list(items) if isinstance(items, list) else []
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _minimal_parse(raw: str) -> Dict[str, Any]:
|
|
75
|
+
schedules: List[Dict[str, Any]] = []
|
|
76
|
+
current: Dict[str, Any] | None = None
|
|
77
|
+
in_args = False
|
|
78
|
+
in_schedules = False
|
|
79
|
+
for line in raw.splitlines():
|
|
80
|
+
if not line.strip() or line.lstrip().startswith("#"):
|
|
81
|
+
continue
|
|
82
|
+
if line.startswith("schedules:"):
|
|
83
|
+
in_schedules = True
|
|
84
|
+
rest = line[len("schedules:"):].strip()
|
|
85
|
+
if rest == "[]":
|
|
86
|
+
return {"schedules": []}
|
|
87
|
+
continue
|
|
88
|
+
if not in_schedules:
|
|
89
|
+
continue
|
|
90
|
+
indent = len(line) - len(line.lstrip(" "))
|
|
91
|
+
body = line.strip()
|
|
92
|
+
if body.startswith("- "):
|
|
93
|
+
if current is not None:
|
|
94
|
+
schedules.append(current)
|
|
95
|
+
current = {}
|
|
96
|
+
in_args = False
|
|
97
|
+
body = body[2:].strip()
|
|
98
|
+
if ":" in body:
|
|
99
|
+
k, _, v = body.partition(":")
|
|
100
|
+
current[k.strip()] = _parse_scalar(v.strip())
|
|
101
|
+
continue
|
|
102
|
+
if current is None:
|
|
103
|
+
continue
|
|
104
|
+
if in_args and indent >= 6:
|
|
105
|
+
if ":" in body:
|
|
106
|
+
k, _, v = body.partition(":")
|
|
107
|
+
current.setdefault("args", {})
|
|
108
|
+
current["args"][k.strip()] = _parse_scalar(v.strip())
|
|
109
|
+
continue
|
|
110
|
+
if ":" in body:
|
|
111
|
+
k, _, v = body.partition(":")
|
|
112
|
+
key = k.strip()
|
|
113
|
+
value = v.strip()
|
|
114
|
+
if key == "args" and value in ("", "{}"):
|
|
115
|
+
in_args = value == ""
|
|
116
|
+
current["args"] = {}
|
|
117
|
+
else:
|
|
118
|
+
in_args = False
|
|
119
|
+
current[key] = _parse_scalar(value)
|
|
120
|
+
if current is not None:
|
|
121
|
+
schedules.append(current)
|
|
122
|
+
return {"schedules": schedules}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _parse_scalar(value: str) -> Any:
|
|
126
|
+
if value == "":
|
|
127
|
+
return ""
|
|
128
|
+
if value.startswith('"') and value.endswith('"'):
|
|
129
|
+
try:
|
|
130
|
+
return bytes(value[1:-1], "utf-8").decode("unicode_escape")
|
|
131
|
+
except Exception:
|
|
132
|
+
return value[1:-1]
|
|
133
|
+
if value.startswith("'") and value.endswith("'"):
|
|
134
|
+
return value[1:-1]
|
|
135
|
+
low = value.lower()
|
|
136
|
+
if low == "true":
|
|
137
|
+
return True
|
|
138
|
+
if low == "false":
|
|
139
|
+
return False
|
|
140
|
+
if low in ("null", "~"):
|
|
141
|
+
return None
|
|
142
|
+
try:
|
|
143
|
+
if "." in value:
|
|
144
|
+
return float(value)
|
|
145
|
+
return int(value)
|
|
146
|
+
except ValueError:
|
|
147
|
+
return value
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def dump_schedules(items: List[Dict[str, Any]]) -> str:
|
|
151
|
+
if not items:
|
|
152
|
+
return "schedules: []\n"
|
|
153
|
+
if HAS_PYYAML:
|
|
154
|
+
cleaned = [_clean_item(item) for item in items]
|
|
155
|
+
return _yaml.safe_dump(
|
|
156
|
+
{"schedules": cleaned},
|
|
157
|
+
allow_unicode=True,
|
|
158
|
+
sort_keys=False,
|
|
159
|
+
default_flow_style=False,
|
|
160
|
+
)
|
|
161
|
+
lines = ["schedules:"]
|
|
162
|
+
for item in items:
|
|
163
|
+
cleaned = _clean_item(item)
|
|
164
|
+
keys = [k for k in CANONICAL_KEY_ORDER if k in cleaned]
|
|
165
|
+
keys.extend(k for k in cleaned.keys() if k not in keys)
|
|
166
|
+
first = True
|
|
167
|
+
for k in keys:
|
|
168
|
+
v = cleaned[k]
|
|
169
|
+
prefix = " - " if first else " "
|
|
170
|
+
first = False
|
|
171
|
+
if k == "args":
|
|
172
|
+
if not v:
|
|
173
|
+
lines.append(f"{prefix}args: {{}}")
|
|
174
|
+
continue
|
|
175
|
+
lines.append(f"{prefix}args:")
|
|
176
|
+
for ak, av in v.items():
|
|
177
|
+
lines.append(f" {ak}: {_yaml_scalar(av)}")
|
|
178
|
+
else:
|
|
179
|
+
lines.append(f"{prefix}{k}: {_yaml_scalar(v)}")
|
|
180
|
+
lines.append("")
|
|
181
|
+
return "\n".join(lines)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _clean_item(item: Dict[str, Any]) -> Dict[str, Any]:
|
|
185
|
+
return {k: v for k, v in item.items() if v is not None}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _yaml_scalar(v: Any) -> str:
|
|
189
|
+
if v is None:
|
|
190
|
+
return "null"
|
|
191
|
+
if isinstance(v, bool):
|
|
192
|
+
return "true" if v else "false"
|
|
193
|
+
if isinstance(v, (int, float)):
|
|
194
|
+
return str(v)
|
|
195
|
+
s = str(v)
|
|
196
|
+
if _needs_quote(s):
|
|
197
|
+
escaped = s.replace("\\", "\\\\").replace("\"", "\\\"")
|
|
198
|
+
return f'"{escaped}"'
|
|
199
|
+
return s
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _needs_quote(s: str) -> bool:
|
|
203
|
+
if s == "":
|
|
204
|
+
return True
|
|
205
|
+
if s.strip() != s:
|
|
206
|
+
return True
|
|
207
|
+
if s.lower() in ("null", "true", "false", "yes", "no", "on", "off", "~"):
|
|
208
|
+
return True
|
|
209
|
+
bad_start = set("-?:[]{},#&*!|>'\"%@`")
|
|
210
|
+
if s[0] in bad_start:
|
|
211
|
+
return True
|
|
212
|
+
for ch in s:
|
|
213
|
+
if ch in (":", "#", "\n"):
|
|
214
|
+
return True
|
|
215
|
+
return False
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def write_schedules_atomic(workspace: str, items: List[Dict[str, Any]]) -> str:
|
|
219
|
+
migrate_legacy_schedules(workspace)
|
|
220
|
+
path = schedules_path(workspace)
|
|
221
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
222
|
+
text = dump_schedules(items)
|
|
223
|
+
# round-trip check before writing
|
|
224
|
+
roundtrip = _parse_schedules(text)
|
|
225
|
+
if len(roundtrip) != len(items):
|
|
226
|
+
raise RuntimeError(
|
|
227
|
+
"Round-trip check failed: parsed entry count "
|
|
228
|
+
f"{len(roundtrip)} != original {len(items)}",
|
|
229
|
+
)
|
|
230
|
+
fd, tmp_path = tempfile.mkstemp(
|
|
231
|
+
prefix=".schedules.", suffix=".yaml.tmp", dir=os.path.dirname(path)
|
|
232
|
+
)
|
|
233
|
+
try:
|
|
234
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
235
|
+
f.write(text)
|
|
236
|
+
os.replace(tmp_path, path)
|
|
237
|
+
except Exception:
|
|
238
|
+
try:
|
|
239
|
+
os.unlink(tmp_path)
|
|
240
|
+
except Exception:
|
|
241
|
+
pass
|
|
242
|
+
raise
|
|
243
|
+
return path
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def validate_cron(raw: str) -> None:
|
|
247
|
+
fields = raw.strip().split()
|
|
248
|
+
if len(fields) != 5:
|
|
249
|
+
raise ValueError(
|
|
250
|
+
f'Cron must have 5 fields ("min hour dom month dow"): "{raw}"',
|
|
251
|
+
)
|
|
252
|
+
spec = [
|
|
253
|
+
(0, 59, "minute"),
|
|
254
|
+
(0, 23, "hour"),
|
|
255
|
+
(1, 31, "day-of-month"),
|
|
256
|
+
(1, 12, "month"),
|
|
257
|
+
(0, 6, "day-of-week"),
|
|
258
|
+
]
|
|
259
|
+
for field, (mn, mx, label) in zip(fields, spec):
|
|
260
|
+
_validate_cron_field(field, mn, mx, label)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _validate_cron_field(field: str, mn: int, mx: int, label: str) -> None:
|
|
264
|
+
for part in field.split(","):
|
|
265
|
+
seg = part.strip()
|
|
266
|
+
if not seg:
|
|
267
|
+
raise ValueError(f'Empty segment in {label} field: "{field}"')
|
|
268
|
+
body = seg
|
|
269
|
+
step = 1
|
|
270
|
+
if "/" in body:
|
|
271
|
+
body, _, step_raw = body.partition("/")
|
|
272
|
+
try:
|
|
273
|
+
step = int(step_raw)
|
|
274
|
+
except ValueError as exc:
|
|
275
|
+
raise ValueError(
|
|
276
|
+
f'Invalid step "{step_raw}" in {label} field: "{field}"',
|
|
277
|
+
) from exc
|
|
278
|
+
if step <= 0:
|
|
279
|
+
raise ValueError(
|
|
280
|
+
f'Invalid step "{step_raw}" in {label} field: "{field}"',
|
|
281
|
+
)
|
|
282
|
+
if body == "" or body == "*":
|
|
283
|
+
continue
|
|
284
|
+
if "-" in body:
|
|
285
|
+
a, _, b = body.partition("-")
|
|
286
|
+
try:
|
|
287
|
+
fr = int(a)
|
|
288
|
+
to = int(b)
|
|
289
|
+
except ValueError as exc:
|
|
290
|
+
raise ValueError(
|
|
291
|
+
f'Invalid value "{body}" in {label} field: "{field}"',
|
|
292
|
+
) from exc
|
|
293
|
+
else:
|
|
294
|
+
try:
|
|
295
|
+
fr = to = int(body)
|
|
296
|
+
except ValueError as exc:
|
|
297
|
+
raise ValueError(
|
|
298
|
+
f'Invalid value "{body}" in {label} field: "{field}"',
|
|
299
|
+
) from exc
|
|
300
|
+
if fr < mn or to > mx or fr > to:
|
|
301
|
+
raise ValueError(
|
|
302
|
+
f'{label} value out of range ({fr}-{to}); allowed {mn}-{mx} for "{field}"',
|
|
303
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Builtin: schedule-toggle
|
|
2
|
+
|
|
3
|
+
スケジュールの enabled フラグを切り替える(または指定値に設定する)。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
12
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
13
|
+
from i18n import localizer # noqa: E402
|
|
14
|
+
from _schedules_lib import load_schedules, write_schedules_atomic # noqa: E402
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def run(ctx, input):
|
|
18
|
+
loc = localizer(input)
|
|
19
|
+
args = input.get("args", {}) or {}
|
|
20
|
+
workspace = input.get("sources", {}).get("workspace", "")
|
|
21
|
+
if not workspace:
|
|
22
|
+
return _err(loc.t("Workspace unavailable", "ワークスペース不明"), loc.t("The workspace path is unavailable.", "workspace パスが取得できません"))
|
|
23
|
+
|
|
24
|
+
target_id = str(args.get("id", "")).strip()
|
|
25
|
+
if not target_id:
|
|
26
|
+
return _err(loc.t("ID is missing", "ID未指定"), loc.t("Specify the target schedule ID.", "対象スケジュールのIDを指定してください"))
|
|
27
|
+
|
|
28
|
+
explicit = "enabled" in args
|
|
29
|
+
target_enabled = bool(args["enabled"]) if explicit else None
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
entries = load_schedules(workspace)
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return _err(loc.t("Load failed", "読込失敗"), loc.t(f"Could not read schedules.yaml: {e}", f"schedules.yaml を読めませんでした: {e}"))
|
|
35
|
+
|
|
36
|
+
target = None
|
|
37
|
+
for item in entries:
|
|
38
|
+
if isinstance(item, dict) and item.get("id") == target_id:
|
|
39
|
+
target = item
|
|
40
|
+
break
|
|
41
|
+
if target is None:
|
|
42
|
+
return _err(loc.t("Not found", "見つかりません"), loc.t(f'Schedule "{target_id}" is not registered.', f'スケジュール "{target_id}" は登録されていません'))
|
|
43
|
+
|
|
44
|
+
current = target.get("enabled")
|
|
45
|
+
if current is None:
|
|
46
|
+
current = True
|
|
47
|
+
new_value = target_enabled if explicit else (not bool(current))
|
|
48
|
+
target["enabled"] = new_value
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
path = write_schedules_atomic(workspace, entries)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
return _err(loc.t("Save failed", "保存失敗"), loc.t(f"Failed to write schedules.yaml: {e}", f"schedules.yaml への書き込みに失敗しました: {e}"))
|
|
54
|
+
|
|
55
|
+
ctx.log.info(
|
|
56
|
+
f"schedule-toggle: id={target_id} {current} -> {new_value}",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
state = loc.t("enabled", "有効") if new_value else loc.t("disabled", "無効")
|
|
60
|
+
return {
|
|
61
|
+
"status": "ok",
|
|
62
|
+
"title": loc.t("Updated", "切替"),
|
|
63
|
+
"summary": loc.t(f"{target_id} is now {state}.", f"{target_id} を{state}にしました"),
|
|
64
|
+
"outputs": {},
|
|
65
|
+
"data": {"entry": target, "previous_enabled": bool(current), "enabled": new_value, "path": path},
|
|
66
|
+
"suggestions": [],
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _err(title, summary):
|
|
71
|
+
return {
|
|
72
|
+
"status": "error",
|
|
73
|
+
"title": title,
|
|
74
|
+
"summary": summary,
|
|
75
|
+
"outputs": {},
|
|
76
|
+
"data": {},
|
|
77
|
+
"suggestions": [],
|
|
78
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Builtin: schedule-toggle
|
|
2
|
+
# ~/.agent-sin/schedules.yaml の指定IDの enabled をトグルする。
|
|
3
|
+
|
|
4
|
+
id: schedule-toggle
|
|
5
|
+
name: Schedule Toggle
|
|
6
|
+
name_i18n:
|
|
7
|
+
en: Schedule Toggle
|
|
8
|
+
ja: スケジュール切替
|
|
9
|
+
description: スケジュールの有効/無効を切り替える
|
|
10
|
+
description_i18n:
|
|
11
|
+
en: Enable or disable a schedule
|
|
12
|
+
ja: スケジュールの有効/無効を切り替える
|
|
13
|
+
runtime: python
|
|
14
|
+
output_mode: raw
|
|
15
|
+
side_effect: true
|
|
16
|
+
|
|
17
|
+
invocation:
|
|
18
|
+
command: schedule.toggle
|
|
19
|
+
phrases:
|
|
20
|
+
- スケジュール一時停止
|
|
21
|
+
- スケジュール再開
|
|
22
|
+
- スケジュール有効化
|
|
23
|
+
- スケジュール無効化
|
|
24
|
+
- 定期実行を止めて
|
|
25
|
+
- 定期実行を有効に
|
|
26
|
+
phrases_i18n:
|
|
27
|
+
en:
|
|
28
|
+
- pause schedule
|
|
29
|
+
- resume schedule
|
|
30
|
+
- enable schedule
|
|
31
|
+
- disable schedule
|
|
32
|
+
- stop recurring task
|
|
33
|
+
- turn schedule on
|
|
34
|
+
ja:
|
|
35
|
+
- スケジュール一時停止
|
|
36
|
+
- スケジュール再開
|
|
37
|
+
- スケジュール有効化
|
|
38
|
+
- スケジュール無効化
|
|
39
|
+
- 定期実行を止めて
|
|
40
|
+
- 定期実行を有効に
|
|
41
|
+
|
|
42
|
+
input:
|
|
43
|
+
schema:
|
|
44
|
+
type: object
|
|
45
|
+
additionalProperties: false
|
|
46
|
+
properties:
|
|
47
|
+
id:
|
|
48
|
+
type: string
|
|
49
|
+
minLength: 1
|
|
50
|
+
description: 対象スケジュールのID
|
|
51
|
+
description_i18n:
|
|
52
|
+
en: Target schedule ID
|
|
53
|
+
ja: 対象スケジュールのID
|
|
54
|
+
enabled:
|
|
55
|
+
type: boolean
|
|
56
|
+
description: 明示的に有効/無効を指定する。省略時はトグル(反転)
|
|
57
|
+
description_i18n:
|
|
58
|
+
en: Explicitly set enabled or disabled. If omitted, toggle the current value
|
|
59
|
+
ja: 明示的に有効/無効を指定する。省略時はトグル(反転)
|
|
60
|
+
required:
|
|
61
|
+
- id
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Builtin: skills-disable
|
|
2
|
+
|
|
3
|
+
指定したスキルIDを skill-settings.yaml の disabled リストに追加する。
|
|
4
|
+
スキル本体ファイルには触れない。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
13
|
+
from i18n import localizer # noqa: E402
|
|
14
|
+
from _skill_settings_lib import set_skill_enabled # noqa: E402
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def run(ctx, input):
|
|
18
|
+
loc = localizer(input)
|
|
19
|
+
args = input.get("args", {}) or {}
|
|
20
|
+
workspace = input.get("sources", {}).get("workspace", "")
|
|
21
|
+
if not workspace:
|
|
22
|
+
return _err(loc.t("Workspace unavailable", "ワークスペース不明"), loc.t("The workspace path is unavailable.", "workspace パスが取得できません"))
|
|
23
|
+
|
|
24
|
+
target_id = str(args.get("id", "")).strip()
|
|
25
|
+
if not target_id:
|
|
26
|
+
return _err(loc.t("ID is missing", "ID未指定"), loc.t("Specify the skill ID to disable.", "無効化したいスキルのIDを指定してください"))
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
changed, settings = set_skill_enabled(workspace, target_id, False)
|
|
30
|
+
except Exception as e:
|
|
31
|
+
return _err(loc.t("Save failed", "保存失敗"), loc.t(f"Failed to update skill-settings.yaml: {e}", f"skill-settings.yaml の更新に失敗しました: {e}"))
|
|
32
|
+
|
|
33
|
+
if not changed:
|
|
34
|
+
return {
|
|
35
|
+
"status": "ok",
|
|
36
|
+
"title": loc.t("Already disabled", "既に無効"),
|
|
37
|
+
"summary": loc.t(f"{target_id} is already disabled.", f"{target_id} は既に無効です"),
|
|
38
|
+
"outputs": {},
|
|
39
|
+
"data": {"id": target_id, "enabled": False, "changed": False, "disabled": list(settings.get("disabled") or [])},
|
|
40
|
+
"suggestions": [],
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
ctx.log.info(f"skills-disable: id={target_id}")
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
"status": "ok",
|
|
47
|
+
"title": loc.t("Disabled", "無効化"),
|
|
48
|
+
"summary": loc.t(f"Disabled {target_id}.", f"{target_id} を無効化しました"),
|
|
49
|
+
"outputs": {},
|
|
50
|
+
"data": {"id": target_id, "enabled": False, "changed": True, "disabled": list(settings.get("disabled") or [])},
|
|
51
|
+
"suggestions": [],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _err(title, summary):
|
|
56
|
+
return {
|
|
57
|
+
"status": "error",
|
|
58
|
+
"title": title,
|
|
59
|
+
"summary": summary,
|
|
60
|
+
"outputs": {},
|
|
61
|
+
"data": {},
|
|
62
|
+
"suggestions": [],
|
|
63
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Builtin: skills-disable
|
|
2
|
+
# 任意のスキル(builtin/user 問わず)を無効化する。
|
|
3
|
+
# ~/.agent-sin/skill-settings.yaml の disabled リストに id を追記するだけで、
|
|
4
|
+
# スキル本体ファイルには触れない。
|
|
5
|
+
|
|
6
|
+
id: skills-disable
|
|
7
|
+
name: Skill Disable
|
|
8
|
+
name_i18n:
|
|
9
|
+
en: Skill Disable
|
|
10
|
+
ja: スキル無効化
|
|
11
|
+
description: 指定したスキルを無効化する
|
|
12
|
+
description_i18n:
|
|
13
|
+
en: Disable the specified skill
|
|
14
|
+
ja: 指定したスキルを無効化する
|
|
15
|
+
runtime: python
|
|
16
|
+
entry: main.py
|
|
17
|
+
handler: run
|
|
18
|
+
output_mode: raw
|
|
19
|
+
side_effect: true
|
|
20
|
+
|
|
21
|
+
invocation:
|
|
22
|
+
command: skills.disable
|
|
23
|
+
phrases:
|
|
24
|
+
- スキルを無効化
|
|
25
|
+
- スキルをオフ
|
|
26
|
+
- スキル無効化
|
|
27
|
+
- 無効化して
|
|
28
|
+
phrases_i18n:
|
|
29
|
+
en:
|
|
30
|
+
- disable skill
|
|
31
|
+
- turn off skill
|
|
32
|
+
- skill disable
|
|
33
|
+
ja:
|
|
34
|
+
- スキルを無効化
|
|
35
|
+
- スキルをオフ
|
|
36
|
+
- スキル無効化
|
|
37
|
+
- 無効化して
|
|
38
|
+
|
|
39
|
+
input:
|
|
40
|
+
schema:
|
|
41
|
+
type: object
|
|
42
|
+
additionalProperties: false
|
|
43
|
+
properties:
|
|
44
|
+
id:
|
|
45
|
+
type: string
|
|
46
|
+
minLength: 1
|
|
47
|
+
description: 無効化したいスキルのID
|
|
48
|
+
description_i18n:
|
|
49
|
+
en: ID of the skill to disable
|
|
50
|
+
ja: 無効化したいスキルのID
|
|
51
|
+
required:
|
|
52
|
+
- id
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Builtin: skills-enable
|
|
2
|
+
|
|
3
|
+
指定したスキルIDを skill-settings.yaml の disabled リストから取り除く。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "_shared"))
|
|
12
|
+
from i18n import localizer # noqa: E402
|
|
13
|
+
from _skill_settings_lib import set_skill_enabled # noqa: E402
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def run(ctx, input):
|
|
17
|
+
loc = localizer(input)
|
|
18
|
+
args = input.get("args", {}) or {}
|
|
19
|
+
workspace = input.get("sources", {}).get("workspace", "")
|
|
20
|
+
if not workspace:
|
|
21
|
+
return _err(loc.t("Workspace unavailable", "ワークスペース不明"), loc.t("The workspace path is unavailable.", "workspace パスが取得できません"))
|
|
22
|
+
|
|
23
|
+
target_id = str(args.get("id", "")).strip()
|
|
24
|
+
if not target_id:
|
|
25
|
+
return _err(loc.t("ID is missing", "ID未指定"), loc.t("Specify the skill ID to enable.", "有効化したいスキルのIDを指定してください"))
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
changed, settings = set_skill_enabled(workspace, target_id, True)
|
|
29
|
+
except Exception as e:
|
|
30
|
+
return _err(loc.t("Save failed", "保存失敗"), loc.t(f"Failed to update skill-settings.yaml: {e}", f"skill-settings.yaml の更新に失敗しました: {e}"))
|
|
31
|
+
|
|
32
|
+
if not changed:
|
|
33
|
+
return {
|
|
34
|
+
"status": "ok",
|
|
35
|
+
"title": loc.t("Already enabled", "既に有効"),
|
|
36
|
+
"summary": loc.t(f"{target_id} is already enabled.", f"{target_id} は既に有効です"),
|
|
37
|
+
"outputs": {},
|
|
38
|
+
"data": {"id": target_id, "enabled": True, "changed": False, "disabled": list(settings.get("disabled") or [])},
|
|
39
|
+
"suggestions": [],
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
ctx.log.info(f"skills-enable: id={target_id}")
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"status": "ok",
|
|
46
|
+
"title": loc.t("Enabled", "有効化"),
|
|
47
|
+
"summary": loc.t(f"Enabled {target_id}.", f"{target_id} を有効化しました"),
|
|
48
|
+
"outputs": {},
|
|
49
|
+
"data": {"id": target_id, "enabled": True, "changed": True, "disabled": list(settings.get("disabled") or [])},
|
|
50
|
+
"suggestions": [],
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _err(title, summary):
|
|
55
|
+
return {
|
|
56
|
+
"status": "error",
|
|
57
|
+
"title": title,
|
|
58
|
+
"summary": summary,
|
|
59
|
+
"outputs": {},
|
|
60
|
+
"data": {},
|
|
61
|
+
"suggestions": [],
|
|
62
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Builtin: skills-enable
|
|
2
|
+
# 無効化されているスキルを再び有効化する。
|
|
3
|
+
# ~/.agent-sin/skill-settings.yaml の disabled リストから id を取り除くだけ。
|
|
4
|
+
|
|
5
|
+
id: skills-enable
|
|
6
|
+
name: Skill Enable
|
|
7
|
+
name_i18n:
|
|
8
|
+
en: Skill Enable
|
|
9
|
+
ja: スキル有効化
|
|
10
|
+
description: 指定したスキルを有効化する
|
|
11
|
+
description_i18n:
|
|
12
|
+
en: Enable the specified skill
|
|
13
|
+
ja: 指定したスキルを有効化する
|
|
14
|
+
runtime: python
|
|
15
|
+
entry: main.py
|
|
16
|
+
handler: run
|
|
17
|
+
output_mode: raw
|
|
18
|
+
side_effect: true
|
|
19
|
+
|
|
20
|
+
invocation:
|
|
21
|
+
command: skills.enable
|
|
22
|
+
phrases:
|
|
23
|
+
- スキルを有効化
|
|
24
|
+
- スキルをオン
|
|
25
|
+
- スキル有効化
|
|
26
|
+
- 有効化して
|
|
27
|
+
phrases_i18n:
|
|
28
|
+
en:
|
|
29
|
+
- enable skill
|
|
30
|
+
- turn on skill
|
|
31
|
+
- skill enable
|
|
32
|
+
ja:
|
|
33
|
+
- スキルを有効化
|
|
34
|
+
- スキルをオン
|
|
35
|
+
- スキル有効化
|
|
36
|
+
- 有効化して
|
|
37
|
+
|
|
38
|
+
input:
|
|
39
|
+
schema:
|
|
40
|
+
type: object
|
|
41
|
+
additionalProperties: false
|
|
42
|
+
properties:
|
|
43
|
+
id:
|
|
44
|
+
type: string
|
|
45
|
+
minLength: 1
|
|
46
|
+
description: 有効化したいスキルのID
|
|
47
|
+
description_i18n:
|
|
48
|
+
en: ID of the skill to enable
|
|
49
|
+
ja: 有効化したいスキルのID
|
|
50
|
+
required:
|
|
51
|
+
- id
|