pantheon-opencode 1.1.1-beta.8 → 1.1.1
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/opencode.json +49 -2
- package/package.json +1 -1
- package/pantheon.schema.json +1 -1
- package/scripts/install-mcp.mjs +54 -18
- package/scripts/mcp_persistence_server.py +3 -3
- package/src/agents/mnemosyne.md +9 -1
- package/src/agents/nyx.md +7 -1
- package/src/agents/themis.md +8 -2
- package/src/agents/zeus.md +10 -0
- package/src/mcp/install-mcp.mjs +54 -18
- package/src/mcp/mcp_persistence_server.py +3 -3
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +208 -94
- package/src/plugins/tui/dist/tui.tsx +162 -31
- package/src/plugins/tui/src/index.tsx +162 -31
- package/src/routing.yml +11 -30
- package/commands/pantheon-audit.md +0 -25
- package/commands/pantheon-deepwork.md +0 -79
- package/commands/pantheon-focus.md +0 -9
- package/commands/pantheon-forget.md +0 -58
- package/commands/pantheon-optimize.md +0 -79
- package/commands/pantheon-remember.md +0 -44
- package/commands/pantheon-search.md +0 -48
- package/commands/pantheon-status.md +0 -71
- package/commands/pantheon.md +0 -49
- package/scripts/check-version-consistency.sh +0 -23
- package/scripts/generate-routing-docs.mjs +0 -104
- package/scripts/manifest.mjs +0 -597
- package/scripts/memory_cache.py +0 -78
- package/scripts/paths.py +0 -64
- package/scripts/prune_context.py +0 -72
- package/scripts/release-bundle.mjs +0 -109
- package/scripts/todo_enforcer.py +0 -242
- package/scripts/validate_agent_frontmatter.py +0 -226
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Validate all OpenCode agent .md files for correct frontmatter."""
|
|
3
|
-
|
|
4
|
-
import sys
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
import yaml
|
|
8
|
-
|
|
9
|
-
VALID_FIELDS = {
|
|
10
|
-
"description",
|
|
11
|
-
"mode",
|
|
12
|
-
"reasoning_effort",
|
|
13
|
-
"permission",
|
|
14
|
-
"temperature",
|
|
15
|
-
"steps",
|
|
16
|
-
"color",
|
|
17
|
-
"hidden",
|
|
18
|
-
"task_budget",
|
|
19
|
-
"source",
|
|
20
|
-
}
|
|
21
|
-
VALID_MODES = {"primary", "subagent"}
|
|
22
|
-
VALID_PERMISSION_KEYS = {
|
|
23
|
-
"edit",
|
|
24
|
-
"bash",
|
|
25
|
-
"task",
|
|
26
|
-
"skill",
|
|
27
|
-
"read",
|
|
28
|
-
"glob",
|
|
29
|
-
"grep",
|
|
30
|
-
"webfetch",
|
|
31
|
-
"websearch",
|
|
32
|
-
"question",
|
|
33
|
-
"lsp",
|
|
34
|
-
}
|
|
35
|
-
VALID_PERMISSION_VALUES = {"allow", "deny", "ask"}
|
|
36
|
-
|
|
37
|
-
AGENT_DIRS = [
|
|
38
|
-
Path("/home/ils15/pantheon/platform/opencode/agents"),
|
|
39
|
-
Path("/home/ils15/.config/opencode/agents"),
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
errors = []
|
|
43
|
-
warnings = []
|
|
44
|
-
valid_count = 0
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def err(filename, field, msg):
|
|
48
|
-
errors.append(f"❌ [{filename}] {field}: {msg}")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def warn(filename, field, msg):
|
|
52
|
-
warnings.append(f"⚠️ [{filename}] {field}: {msg}")
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def validate_permission_value(val, path_ctx, filename):
|
|
56
|
-
if isinstance(val, str):
|
|
57
|
-
if val not in VALID_PERMISSION_VALUES:
|
|
58
|
-
err(
|
|
59
|
-
filename,
|
|
60
|
-
f"permission.{path_ctx}",
|
|
61
|
-
f"invalid value '{val}'; must be one of {sorted(VALID_PERMISSION_VALUES)}",
|
|
62
|
-
)
|
|
63
|
-
elif isinstance(val, dict):
|
|
64
|
-
for key, subval in val.items():
|
|
65
|
-
validate_permission_value(subval, f"{path_ctx}.{key}", filename)
|
|
66
|
-
else:
|
|
67
|
-
err(
|
|
68
|
-
filename,
|
|
69
|
-
f"permission.{path_ctx}",
|
|
70
|
-
f"unexpected type '{type(val).__name__}'; expected string or dict",
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def validate_file(filepath): # noqa: C901, PLR0912, PLR0915
|
|
75
|
-
global valid_count # noqa: PLW0603
|
|
76
|
-
filename = filepath.name
|
|
77
|
-
text = filepath.read_text(encoding="utf-8")
|
|
78
|
-
|
|
79
|
-
if not text.startswith("---"):
|
|
80
|
-
err(filename, "frontmatter", "file does not start with '---'")
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
parts = text.split("---")
|
|
84
|
-
if len(parts) < 3: # noqa: PLR2004
|
|
85
|
-
err(filename, "frontmatter", "missing closing '---' delimiter")
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
raw_yaml = parts[1]
|
|
89
|
-
try:
|
|
90
|
-
data = yaml.safe_load(raw_yaml)
|
|
91
|
-
except yaml.YAMLError as e:
|
|
92
|
-
err(filename, "frontmatter", f"YAML parse error: {e}")
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
if not isinstance(data, dict):
|
|
96
|
-
err(filename, "frontmatter", "frontmatter is not a mapping")
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
# Check for unknown fields
|
|
100
|
-
unknown = set(data.keys()) - VALID_FIELDS
|
|
101
|
-
for field in sorted(unknown):
|
|
102
|
-
err(filename, field, f"unknown field '{field}'; valid: {sorted(VALID_FIELDS)}")
|
|
103
|
-
|
|
104
|
-
# description: must be present
|
|
105
|
-
if "description" not in data:
|
|
106
|
-
err(filename, "description", "description field is required")
|
|
107
|
-
elif not isinstance(data["description"], str) or not data["description"].strip():
|
|
108
|
-
err(filename, "description", "must be a non-empty string")
|
|
109
|
-
|
|
110
|
-
# mode: must be present and valid
|
|
111
|
-
if "mode" not in data:
|
|
112
|
-
err(filename, "mode", "mode field is required")
|
|
113
|
-
elif data["mode"] not in VALID_MODES:
|
|
114
|
-
err(
|
|
115
|
-
filename,
|
|
116
|
-
"mode",
|
|
117
|
-
f"invalid value '{data['mode']}'; must be one of {sorted(VALID_MODES)}",
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
# permission: validate keys and values
|
|
121
|
-
if "permission" in data:
|
|
122
|
-
perm = data["permission"]
|
|
123
|
-
if isinstance(perm, dict):
|
|
124
|
-
for key in perm:
|
|
125
|
-
if key not in VALID_PERMISSION_KEYS:
|
|
126
|
-
err(
|
|
127
|
-
filename,
|
|
128
|
-
f"permission.{key}",
|
|
129
|
-
f"invalid permission key '{key}'; valid: {sorted(VALID_PERMISSION_KEYS)}",
|
|
130
|
-
)
|
|
131
|
-
validate_permission_value(perm[key], key, filename)
|
|
132
|
-
else:
|
|
133
|
-
err(
|
|
134
|
-
filename,
|
|
135
|
-
"permission",
|
|
136
|
-
f"unexpected type '{type(perm).__name__}'; expected mapping",
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
# temperature: between 0.0 and 2.0
|
|
140
|
-
if "temperature" in data:
|
|
141
|
-
t = data["temperature"]
|
|
142
|
-
if not isinstance(t, (int, float)):
|
|
143
|
-
err(filename, "temperature", f"expected a number, got '{type(t).__name__}'")
|
|
144
|
-
elif t < 0.0 or t > 2.0: # noqa: PLR2004
|
|
145
|
-
err(filename, "temperature", f"value {t} is out of range [0.0, 2.0]")
|
|
146
|
-
|
|
147
|
-
# steps: must be a positive integer
|
|
148
|
-
if "steps" in data:
|
|
149
|
-
s = data["steps"]
|
|
150
|
-
if not isinstance(s, int) or isinstance(s, bool):
|
|
151
|
-
err(
|
|
152
|
-
filename,
|
|
153
|
-
"steps",
|
|
154
|
-
f"expected a positive integer, got '{type(s).__name__}'",
|
|
155
|
-
)
|
|
156
|
-
elif s < 1:
|
|
157
|
-
err(filename, "steps", f"value {s} is not a positive integer")
|
|
158
|
-
|
|
159
|
-
# Body content checks
|
|
160
|
-
body = "---".join(parts[2:])
|
|
161
|
-
|
|
162
|
-
bash_value = None
|
|
163
|
-
if isinstance(data.get("permission"), dict):
|
|
164
|
-
bash_value = data["permission"].get("bash")
|
|
165
|
-
|
|
166
|
-
if bash_value == "deny":
|
|
167
|
-
if "⛔ TOOLS NOT AVAILABLE" not in body:
|
|
168
|
-
err(
|
|
169
|
-
filename,
|
|
170
|
-
"body",
|
|
171
|
-
"permission.bash is 'deny' but body is missing '## ⛔ TOOLS NOT AVAILABLE' section",
|
|
172
|
-
)
|
|
173
|
-
elif "bash" not in body.split("⛔ TOOLS NOT AVAILABLE")[1].split("\n")[0:20]:
|
|
174
|
-
warn(
|
|
175
|
-
filename,
|
|
176
|
-
"body",
|
|
177
|
-
"permission.bash is 'deny' but 'bash' may not be listed as unavailable in the section",
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
edit_value = None
|
|
181
|
-
if isinstance(data.get("permission"), dict):
|
|
182
|
-
edit_value = data["permission"].get("edit")
|
|
183
|
-
|
|
184
|
-
if edit_value == "deny":
|
|
185
|
-
edit_mentions = body.lower().count("edit")
|
|
186
|
-
if edit_mentions > 0:
|
|
187
|
-
warn(
|
|
188
|
-
filename,
|
|
189
|
-
"body",
|
|
190
|
-
f"permission.edit is 'deny' but 'edit' appears {edit_mentions} time(s) in body",
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
valid_count += 1
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def main():
|
|
197
|
-
all_files = []
|
|
198
|
-
for d in AGENT_DIRS:
|
|
199
|
-
if not d.exists():
|
|
200
|
-
print(f"⚠️ Config error: directory not found: {d}", file=sys.stderr)
|
|
201
|
-
sys.exit(2)
|
|
202
|
-
all_files.extend(sorted(d.glob("*.md")))
|
|
203
|
-
|
|
204
|
-
if not all_files:
|
|
205
|
-
print("⚠️ Config error: no agent .md files found", file=sys.stderr)
|
|
206
|
-
sys.exit(2)
|
|
207
|
-
|
|
208
|
-
for f in all_files:
|
|
209
|
-
validate_file(f)
|
|
210
|
-
|
|
211
|
-
for msg in errors:
|
|
212
|
-
print(msg, file=sys.stderr)
|
|
213
|
-
for msg in warnings:
|
|
214
|
-
print(msg, file=sys.stderr)
|
|
215
|
-
|
|
216
|
-
print(
|
|
217
|
-
f"✅ {valid_count} files valid, {len(warnings)} warnings, {len(errors)} errors"
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
if errors:
|
|
221
|
-
sys.exit(1)
|
|
222
|
-
sys.exit(0)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if __name__ == "__main__":
|
|
226
|
-
main()
|