wizard-central-toolkit 0.2.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.
- wiz_central_toolkit/__init__.py +2 -0
- wiz_central_toolkit/bannerwiz/__main__.py +6 -0
- wiz_central_toolkit/bannerwiz/cli.py +79 -0
- wiz_central_toolkit/bannerwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/bannerwiz/lib/banner_wizard.py +999 -0
- wiz_central_toolkit/bannerwiz/lib/banner_wrapper.py +199 -0
- wiz_central_toolkit/bannerwiz/lib/cli.py +79 -0
- wiz_central_toolkit/codeguardwiz/__init__.py +1 -0
- wiz_central_toolkit/codeguardwiz/__main__.py +19 -0
- wiz_central_toolkit/codeguardwiz/cli.py +971 -0
- wiz_central_toolkit/codeguardwiz/codeguard_wizard.py +269 -0
- wiz_central_toolkit/codeguardwiz/rust_engine.py +348 -0
- wiz_central_toolkit/codeguardwiz/static_engine.py +307 -0
- wiz_central_toolkit/common/__init__.py +1 -0
- wiz_central_toolkit/debugwiz/__init__.py +1 -0
- wiz_central_toolkit/debugwiz/__main__.py +360 -0
- wiz_central_toolkit/debugwiz/debug_wizard.py +364 -0
- wiz_central_toolkit/diffwiz/__init__.py +0 -0
- wiz_central_toolkit/diffwiz/__main__.py +21 -0
- wiz_central_toolkit/diffwiz/cli.py +134 -0
- wiz_central_toolkit/diffwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/diffwiz/lib/cli.py +144 -0
- wiz_central_toolkit/diffwiz/lib/diff_wizard.py +89 -0
- wiz_central_toolkit/godfatherwiz/__init__.py +0 -0
- wiz_central_toolkit/godfatherwiz/__main__.py +18 -0
- wiz_central_toolkit/godfatherwiz/ai.py +136 -0
- wiz_central_toolkit/godfatherwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/godfatherwiz/lib/cli.py +925 -0
- wiz_central_toolkit/godfatherwiz/lib/godfather_wizard.py +1552 -0
- wiz_central_toolkit/jsonwiz/__init__.py +0 -0
- wiz_central_toolkit/jsonwiz/__main__.py +21 -0
- wiz_central_toolkit/jsonwiz/cli.py +4 -0
- wiz_central_toolkit/jsonwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/jsonwiz/lib/cli.py +138 -0
- wiz_central_toolkit/jsonwiz/lib/json_wizard.py +133 -0
- wiz_central_toolkit/pythonwiz/__init__.py +0 -0
- wiz_central_toolkit/pythonwiz/__main__.py +21 -0
- wiz_central_toolkit/pythonwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/pythonwiz/lib/cli.py +247 -0
- wiz_central_toolkit/pythonwiz/lib/python_wizard.py +353 -0
- wiz_central_toolkit/readwiz/__init__.py +5 -0
- wiz_central_toolkit/readwiz/__main__.py +15 -0
- wiz_central_toolkit/readwiz/cli.py +46 -0
- wiz_central_toolkit/readwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/readwiz/lib/cli.py +47 -0
- wiz_central_toolkit/readwiz/lib/read_wizard.py +121 -0
- wiz_central_toolkit/rustwiz/__init__.py +5 -0
- wiz_central_toolkit/rustwiz/__main__.py +7 -0
- wiz_central_toolkit/rustwiz/cli.py +4 -0
- wiz_central_toolkit/rustwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/rustwiz/lib/cli.py +53 -0
- wiz_central_toolkit/rustwiz/lib/rust_wizard.py +128 -0
- wiz_central_toolkit/tmuxwiz/__init__.py +0 -0
- wiz_central_toolkit/tmuxwiz/__main__.py +15 -0
- wiz_central_toolkit/tmuxwiz/cli.py +7 -0
- wiz_central_toolkit/tmuxwiz/lib/__init__.py +1 -0
- wiz_central_toolkit/tmuxwiz/lib/cli.py +10 -0
- wiz_central_toolkit/tmuxwiz/lib/tmux_wizard.py +229 -0
- wiz_central_toolkit/todowiz/__init__.py +0 -0
- wiz_central_toolkit/todowiz/__main__.py +22 -0
- wiz_central_toolkit/todowiz/cli.py +4 -0
- wiz_central_toolkit/todowiz/lib/__init__.py +1 -0
- wiz_central_toolkit/todowiz/lib/cli.py +82 -0
- wiz_central_toolkit/todowiz/lib/todo_wizard.py +134 -0
- wiz_central_toolkit/tools_registry.py +62 -0
- wiz_central_toolkit/wiz/__init__.py +0 -0
- wiz_central_toolkit/wiz/__main__.py +141 -0
- wiz_central_toolkit/writewiz/__init__.py +1 -0
- wiz_central_toolkit/writewiz/__main__.py +9 -0
- wiz_central_toolkit/writewiz/cli.py +264 -0
- wiz_central_toolkit/writewiz/write_wizard.py +384 -0
- wizard_central_toolkit-0.2.0.dist-info/METADATA +203 -0
- wizard_central_toolkit-0.2.0.dist-info/RECORD +77 -0
- wizard_central_toolkit-0.2.0.dist-info/WHEEL +5 -0
- wizard_central_toolkit-0.2.0.dist-info/entry_points.txt +50 -0
- wizard_central_toolkit-0.2.0.dist-info/licenses/LICENSE +201 -0
- wizard_central_toolkit-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,925 @@
|
|
|
1
|
+
"""godfather-wizard face ā the Grand Master, fleet dispatcher & REPL."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
import readline
|
|
7
|
+
|
|
8
|
+
from collections import Counter
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from .godfather_wizard import (
|
|
11
|
+
FATHER_LAW, call_sibling, call_internal, call_ollama, speak_if_enabled,
|
|
12
|
+
truth_anchor, classify_route, seated_model, seated_models, seat_for,
|
|
13
|
+
seat_label, seat_unavailable_line, set_model_assignment,
|
|
14
|
+
unset_model_assignment,
|
|
15
|
+
brainstorm_reply, codegen_current_content, codegen_proposal,
|
|
16
|
+
render_codegen_diff, confirm_codegen_write, stage_codegen_content,
|
|
17
|
+
land_codegen_content, fleet_census, resolve_sibling, load_routes,
|
|
18
|
+
save_routes, journal, ledger_rows, _now, schedule_task,
|
|
19
|
+
queue_tasks, render_banner, list_all_slash_commands,
|
|
20
|
+
fleet_invocable_names,
|
|
21
|
+
session_load, session_save)
|
|
22
|
+
|
|
23
|
+
USAGE = """Godfather Wizard ā the Grand Master, fleet dispatcher & REPL
|
|
24
|
+
alias: godfatherwiz
|
|
25
|
+
usage: godfather_wizard <command> [args]
|
|
26
|
+
|
|
27
|
+
CLI Commands:
|
|
28
|
+
godfather_wizard audit <path> truth anchor + codeguard_wizard audit
|
|
29
|
+
godfather_wizard task <intent> todo_wizard intake
|
|
30
|
+
godfather_wizard run <directive> deterministic keyword router
|
|
31
|
+
godfather_wizard routes print routing table
|
|
32
|
+
godfather_wizard fleet bin-lane census
|
|
33
|
+
godfather_wizard law print FATHER LAW
|
|
34
|
+
godfather_wizard truth <path> anchor ground truth (internal senses)
|
|
35
|
+
godfather_wizard diagnose <dir> debug_wizard audit + classify
|
|
36
|
+
godfather_wizard heal <file> debug_wizard propose ā write_wizard wall
|
|
37
|
+
godfather_wizard status fleet-wide health check
|
|
38
|
+
godfather_wizard repl launch interactive REPL cockpit
|
|
39
|
+
godfather_wizard siblings list all registered citizens
|
|
40
|
+
godfather_wizard verify <wizard> single citizen health check
|
|
41
|
+
godfather_wizard consent <file> force write_wizard commit prompt
|
|
42
|
+
godfather_wizard schedule <intent> queue task
|
|
43
|
+
godfather_wizard queue show scheduled tasks
|
|
44
|
+
godfather_wizard history run history across delegations
|
|
45
|
+
godfather_wizard stats fleet-wide execution counts
|
|
46
|
+
godfather_wizard versions law drift detection
|
|
47
|
+
godfather_wizard help [command] full usage, or one subcommand's usage
|
|
48
|
+
godfather_wizard census full fleet roster
|
|
49
|
+
godfather_wizard models show providers, live seats + assignments
|
|
50
|
+
godfather_wizard models set <task> <model>
|
|
51
|
+
assign a seat to default, brainstorm,
|
|
52
|
+
build, debug, diff, or audit ā a bare
|
|
53
|
+
Ollama model name, or provider:model
|
|
54
|
+
(ollama, meta, anthropic, openai,
|
|
55
|
+
google, deepseek)
|
|
56
|
+
godfather_wizard models unset <task>
|
|
57
|
+
remove a per-task model assignment
|
|
58
|
+
|
|
59
|
+
exit codes: 0 ok | 1 failed | 2 out of scope"""
|
|
60
|
+
|
|
61
|
+
CMDS = ("audit", "task", "run", "routes", "fleet", "law", "truth", "diagnose",
|
|
62
|
+
"heal", "status", "repl", "siblings", "verify", "consent", "schedule",
|
|
63
|
+
"queue", "history", "stats", "versions", "help", "census", "models")
|
|
64
|
+
|
|
65
|
+
def _fail(msg):
|
|
66
|
+
print(f"[FAIL] {msg}")
|
|
67
|
+
return 1
|
|
68
|
+
|
|
69
|
+
def _confirm_write_class(pipeline):
|
|
70
|
+
"""The double ask (house law) for a write-class route.
|
|
71
|
+
|
|
72
|
+
First the plain ask; then a typed confirmation naming the
|
|
73
|
+
action being agreed to. Anything but the two exact answers ā
|
|
74
|
+
including EOF ā is a no, and nothing executes.
|
|
75
|
+
"""
|
|
76
|
+
action = " ".join(pipeline)
|
|
77
|
+
try:
|
|
78
|
+
first = input(f"ā ļø write-class route: {action}\n"
|
|
79
|
+
"this changes files. proceed? [y/N] ")
|
|
80
|
+
except (KeyboardInterrupt, EOFError):
|
|
81
|
+
return False
|
|
82
|
+
if first.strip().lower() != "y":
|
|
83
|
+
return False
|
|
84
|
+
try:
|
|
85
|
+
typed = input(f"confirm by typing: yes, {action}\n> ")
|
|
86
|
+
except (KeyboardInterrupt, EOFError):
|
|
87
|
+
return False
|
|
88
|
+
return typed.strip() == f"yes, {action}"
|
|
89
|
+
|
|
90
|
+
def main():
|
|
91
|
+
try:
|
|
92
|
+
return _dispatch()
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"[FAIL] {type(e).__name__}: {e}")
|
|
95
|
+
return 1
|
|
96
|
+
|
|
97
|
+
def _dispatch():
|
|
98
|
+
args = sys.argv[1:]
|
|
99
|
+
if args and args[0] in ("-h", "--help"):
|
|
100
|
+
print(USAGE)
|
|
101
|
+
return 0
|
|
102
|
+
if not args:
|
|
103
|
+
# bare invocation = the throne room: banner, voice, chat, slash commands
|
|
104
|
+
return run_repl()
|
|
105
|
+
if args[0] not in CMDS:
|
|
106
|
+
print(USAGE)
|
|
107
|
+
return 2
|
|
108
|
+
|
|
109
|
+
cmd = args[0]
|
|
110
|
+
|
|
111
|
+
# === CORE DISPATCHERS ===
|
|
112
|
+
if cmd == "law":
|
|
113
|
+
print(FATHER_LAW)
|
|
114
|
+
return 0
|
|
115
|
+
|
|
116
|
+
if cmd == "versions":
|
|
117
|
+
import hashlib
|
|
118
|
+
print("father_law_sha256:", hashlib.sha256(FATHER_LAW.encode()).hexdigest())
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
if cmd == "census":
|
|
122
|
+
count, names = fleet_census()
|
|
123
|
+
print(f"[OK] {count} certified citizens on the fleet")
|
|
124
|
+
for i, n in enumerate(names, 1):
|
|
125
|
+
print(f" #{i:02d} {n}")
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
if cmd == "models":
|
|
129
|
+
if len(args) == 1:
|
|
130
|
+
rc, out = seated_models()
|
|
131
|
+
print(out)
|
|
132
|
+
return rc
|
|
133
|
+
if args[1] == "set" and len(args) == 4:
|
|
134
|
+
rc, out = set_model_assignment(args[2], args[3])
|
|
135
|
+
print(out)
|
|
136
|
+
return rc
|
|
137
|
+
if args[1] == "unset" and len(args) == 3:
|
|
138
|
+
rc, out = unset_model_assignment(args[2])
|
|
139
|
+
print(out)
|
|
140
|
+
return rc
|
|
141
|
+
print(USAGE)
|
|
142
|
+
return 2
|
|
143
|
+
|
|
144
|
+
if cmd == "help":
|
|
145
|
+
if len(args) < 2:
|
|
146
|
+
print(USAGE)
|
|
147
|
+
return 0
|
|
148
|
+
target = args[1]
|
|
149
|
+
for line in USAGE.splitlines():
|
|
150
|
+
if target in line:
|
|
151
|
+
print(line.strip())
|
|
152
|
+
return 0
|
|
153
|
+
|
|
154
|
+
if cmd == "routes":
|
|
155
|
+
routes = load_routes()
|
|
156
|
+
# The bank also holds scheduled-task records (task_* keys,
|
|
157
|
+
# dict values) ā those are the queue's business, not routes.
|
|
158
|
+
# The routing table shows actual routes only.
|
|
159
|
+
shown = {k: v for k, v in routes.items()
|
|
160
|
+
if not k.startswith("task_") and isinstance(v, list)}
|
|
161
|
+
if not shown:
|
|
162
|
+
print("(routing table empty)")
|
|
163
|
+
return 0
|
|
164
|
+
for k, v in shown.items():
|
|
165
|
+
print(f" {k:25s} -> {' '.join(v)}")
|
|
166
|
+
return 0
|
|
167
|
+
|
|
168
|
+
if cmd == "fleet":
|
|
169
|
+
count, names = fleet_census()
|
|
170
|
+
print(f"[OK] {count} sibling wizards installed")
|
|
171
|
+
for n in names:
|
|
172
|
+
print(f" {n}")
|
|
173
|
+
return 0
|
|
174
|
+
|
|
175
|
+
if cmd == "siblings":
|
|
176
|
+
count, names = fleet_census()
|
|
177
|
+
for i, n in enumerate(names, 1):
|
|
178
|
+
print(f"[{i:2d}] {n}")
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
if cmd == "history":
|
|
182
|
+
rows = ledger_rows()
|
|
183
|
+
if not rows:
|
|
184
|
+
print("(ledger empty ā no dispatches yet)")
|
|
185
|
+
return 0
|
|
186
|
+
for row in rows:
|
|
187
|
+
print(f"{row['at']} {row['event']:16s} {row['target']} {row['detail']}")
|
|
188
|
+
return 0
|
|
189
|
+
|
|
190
|
+
if cmd == "stats":
|
|
191
|
+
counts = Counter(r["event"] for r in ledger_rows())
|
|
192
|
+
print(dict(counts) if counts else "(no events recorded)")
|
|
193
|
+
return 0
|
|
194
|
+
|
|
195
|
+
if cmd == "status":
|
|
196
|
+
# Probe the fleet that is actually installed ā the same census
|
|
197
|
+
# fleet/siblings report. Absent citizens are not probed at all.
|
|
198
|
+
_count, installed = fleet_census()
|
|
199
|
+
if not installed:
|
|
200
|
+
print("[FAIL] no sibling wizards installed")
|
|
201
|
+
return 1
|
|
202
|
+
worst = 0
|
|
203
|
+
for w in installed:
|
|
204
|
+
if resolve_sibling(w) is None:
|
|
205
|
+
print(f"[FAIL] {w} unresponsive")
|
|
206
|
+
worst = 1
|
|
207
|
+
continue
|
|
208
|
+
rc, out = call_sibling([w, "--help"])
|
|
209
|
+
# exit 2 = usage fallthrough on --help, NOT death. Death = rc 1.
|
|
210
|
+
alive = rc in (0, 2)
|
|
211
|
+
print(f"[{'OK' if alive else 'FAIL'}] {w} {'reachable' if alive else 'unresponsive'}")
|
|
212
|
+
if not alive:
|
|
213
|
+
worst = 1
|
|
214
|
+
return worst
|
|
215
|
+
|
|
216
|
+
if cmd == "repl":
|
|
217
|
+
return run_repl()
|
|
218
|
+
|
|
219
|
+
# === PIPELINERS ===
|
|
220
|
+
if cmd == "audit":
|
|
221
|
+
if len(args) < 2:
|
|
222
|
+
return _fail("audit requires a path")
|
|
223
|
+
rc_overall = 0
|
|
224
|
+
print("--- PHASE 1: truth anchor ---")
|
|
225
|
+
rc, out = truth_anchor(args[1])
|
|
226
|
+
print(out or "(no output)")
|
|
227
|
+
if rc != 0:
|
|
228
|
+
rc_overall = 1
|
|
229
|
+
print("--- PHASE 2: codeguard_wizard audit ---")
|
|
230
|
+
rc, out = call_sibling(["codeguard_wizard", "audit", args[1]])
|
|
231
|
+
print(out or "(no output)")
|
|
232
|
+
if rc != 0:
|
|
233
|
+
rc_overall = 1
|
|
234
|
+
print(f"[{'OK' if rc_overall == 0 else 'FAIL'}] audit pipeline complete")
|
|
235
|
+
return rc_overall
|
|
236
|
+
|
|
237
|
+
if cmd == "task":
|
|
238
|
+
if len(args) < 2:
|
|
239
|
+
return _fail("task requires an intent")
|
|
240
|
+
intent = " ".join(args[1:])
|
|
241
|
+
# todowiz alone carries the task now ā there is no second
|
|
242
|
+
# phase, and the output must not claim one ran.
|
|
243
|
+
print("--- todo_wizard intake ---")
|
|
244
|
+
rc, out = call_sibling(["todo_wizard", "add", intent])
|
|
245
|
+
print(out or "(no output)")
|
|
246
|
+
if rc != 0:
|
|
247
|
+
print("[FAIL] task pipeline complete")
|
|
248
|
+
return 1
|
|
249
|
+
print("[OK] task pipeline complete")
|
|
250
|
+
return 0
|
|
251
|
+
|
|
252
|
+
if cmd == "run":
|
|
253
|
+
if len(args) < 2:
|
|
254
|
+
return _fail("run requires a directive")
|
|
255
|
+
directive = " ".join(args[1:])
|
|
256
|
+
rc, anchor = truth_anchor(".")
|
|
257
|
+
print(f"--- truth anchor (law 2) ---")
|
|
258
|
+
print(anchor or "(no output)")
|
|
259
|
+
|
|
260
|
+
pipeline, reason, wall = classify_route(directive, [])
|
|
261
|
+
if pipeline is None and wall:
|
|
262
|
+
# Unmapped mutation intent ā refused at the wall, as
|
|
263
|
+
# it always has been; the lawful road is writewiz.
|
|
264
|
+
print(f"[CONSENT WALL] {reason}")
|
|
265
|
+
print("to mutate: echo 'content' | write_wizard propose <path>")
|
|
266
|
+
print("then answer the wall yourself: write_wizard commit <path>")
|
|
267
|
+
journal("WALL_ROUTED", directive, "mutation detected")
|
|
268
|
+
return 0
|
|
269
|
+
if pipeline is None:
|
|
270
|
+
print("out of scope ā no route matches this directive")
|
|
271
|
+
print("run 'godfather_wizard routes' to see the lawful table")
|
|
272
|
+
journal("ROUTED", directive, "out of scope")
|
|
273
|
+
return 2
|
|
274
|
+
if wall:
|
|
275
|
+
# A write-class route: there is no MCP gate in this
|
|
276
|
+
# project, so the double ask is the load-bearing wall.
|
|
277
|
+
print(f"[CONSENT WALL] {reason}")
|
|
278
|
+
if not _confirm_write_class(pipeline):
|
|
279
|
+
print("[FAIL] consent withheld ā nothing executed")
|
|
280
|
+
journal("WALL_ROUTED", directive, "consent withheld")
|
|
281
|
+
return 0
|
|
282
|
+
journal("WALL_ROUTED", directive, "consent given ā executing")
|
|
283
|
+
|
|
284
|
+
journal("ROUTED", directive, reason)
|
|
285
|
+
print(f"[ROUTE] {reason}")
|
|
286
|
+
print(f"--- executing: {' '.join(pipeline)} ---")
|
|
287
|
+
internal = call_internal(pipeline)
|
|
288
|
+
if internal is not None:
|
|
289
|
+
rc, out = internal
|
|
290
|
+
else:
|
|
291
|
+
rc, out = call_sibling(pipeline)
|
|
292
|
+
print(out or "(no output)")
|
|
293
|
+
if rc == 0:
|
|
294
|
+
print("[OK] pipeline complete")
|
|
295
|
+
else:
|
|
296
|
+
print(f"[FAIL] pipeline ended with exit {rc}")
|
|
297
|
+
return rc
|
|
298
|
+
|
|
299
|
+
if cmd == "truth":
|
|
300
|
+
if len(args) < 2:
|
|
301
|
+
return _fail("truth requires a path")
|
|
302
|
+
rc, out = truth_anchor(args[1])
|
|
303
|
+
print(out or "(no output)")
|
|
304
|
+
return rc
|
|
305
|
+
|
|
306
|
+
if cmd == "diagnose":
|
|
307
|
+
if len(args) < 2:
|
|
308
|
+
return _fail("diagnose requires a directory")
|
|
309
|
+
print("--- PHASE 1: debug_wizard audit ---")
|
|
310
|
+
rc, out = call_sibling(["debug_wizard", "audit", args[1]])
|
|
311
|
+
print(out or "(no output)")
|
|
312
|
+
rc_overall = 1 if rc != 0 else 0
|
|
313
|
+
# Phase 2: classify the worst error file the census can name.
|
|
314
|
+
# debugwiz has no 'triage' ā hotspot ranks the error files and
|
|
315
|
+
# classify breaks the worst one down.
|
|
316
|
+
rc, out = call_sibling(["debug_wizard", "hotspot", args[1]])
|
|
317
|
+
worst_file = None
|
|
318
|
+
for line in out.splitlines():
|
|
319
|
+
m = re.match(r"\[HOT\] \d+ errors\s+(.+)$", line.strip())
|
|
320
|
+
if m:
|
|
321
|
+
worst_file = m.group(1).strip()
|
|
322
|
+
break
|
|
323
|
+
if worst_file is None:
|
|
324
|
+
print("--- PHASE 2: no error files ā nothing to classify ---")
|
|
325
|
+
return rc_overall
|
|
326
|
+
print(f"--- PHASE 2: debug_wizard classify {worst_file} ---")
|
|
327
|
+
rc, out = call_sibling(["debug_wizard", "classify", worst_file])
|
|
328
|
+
print(out or "(no output)")
|
|
329
|
+
if rc == 2: # usage error ā the chain itself misfired
|
|
330
|
+
rc_overall = 1
|
|
331
|
+
return rc_overall
|
|
332
|
+
|
|
333
|
+
if cmd == "heal":
|
|
334
|
+
if len(args) < 2:
|
|
335
|
+
return _fail("heal requires a file path")
|
|
336
|
+
target = Path(args[1]).expanduser()
|
|
337
|
+
audit_root = target if target.is_dir() else target.parent
|
|
338
|
+
print("--- PHASE 1: debug_wizard audit ---")
|
|
339
|
+
rc, out = call_sibling(["debug_wizard", "audit", str(audit_root)])
|
|
340
|
+
print(out or "(no output)")
|
|
341
|
+
if rc == 0:
|
|
342
|
+
print("[OK] nothing sick ā nothing to heal")
|
|
343
|
+
return 0
|
|
344
|
+
if rc != 1:
|
|
345
|
+
# Not the "sickness found" verdict ā the chain itself broke
|
|
346
|
+
# (usage error or a sibling that would not answer).
|
|
347
|
+
return 1
|
|
348
|
+
# Name the patient: the file asked for, if it is among the
|
|
349
|
+
# sick ā otherwise the worst file the hotspot census names.
|
|
350
|
+
rc, out = call_sibling(["debug_wizard", "hotspot", str(audit_root)])
|
|
351
|
+
sick = []
|
|
352
|
+
for line in out.splitlines():
|
|
353
|
+
m = re.match(r"\[HOT\] \d+ errors\s+(.+)$", line.strip())
|
|
354
|
+
if m:
|
|
355
|
+
sick.append(m.group(1).strip())
|
|
356
|
+
if target.is_file():
|
|
357
|
+
patient = None
|
|
358
|
+
for f in sick:
|
|
359
|
+
try:
|
|
360
|
+
if Path(f).resolve() == target.resolve():
|
|
361
|
+
patient = target
|
|
362
|
+
break
|
|
363
|
+
except OSError:
|
|
364
|
+
continue
|
|
365
|
+
if patient is None:
|
|
366
|
+
print(f"[OK] {target} shows no errors ā nothing to heal in it")
|
|
367
|
+
return 0
|
|
368
|
+
elif sick:
|
|
369
|
+
patient = Path(sick[0])
|
|
370
|
+
else:
|
|
371
|
+
return _fail("audit reported sickness but no file could be named")
|
|
372
|
+
print(f"--- PHASE 2: debug_wizard propose {patient} ---")
|
|
373
|
+
rc, out = call_sibling(["debug_wizard", "propose", str(patient)])
|
|
374
|
+
print(out or "(no output)")
|
|
375
|
+
if rc != 0:
|
|
376
|
+
return 1
|
|
377
|
+
fix_target = Path.home() / ".debug-wizard" / f"fix_{patient.stem}.txt"
|
|
378
|
+
print(f"handoff: write_wizard commit {fix_target} # your keystroke, Governor")
|
|
379
|
+
return 0
|
|
380
|
+
|
|
381
|
+
if cmd == "verify":
|
|
382
|
+
if len(args) < 2:
|
|
383
|
+
return _fail("verify requires a wizard name")
|
|
384
|
+
name = args[1]
|
|
385
|
+
if resolve_sibling(name) is None:
|
|
386
|
+
# Not installed is out of scope (Law 5: exit 2) ā a different
|
|
387
|
+
# fact from installed-but-unresponsive, and reported as such.
|
|
388
|
+
print(f"[FAIL] {name} not installed")
|
|
389
|
+
return 2
|
|
390
|
+
rc, out = call_sibling([name, "--help"])
|
|
391
|
+
# exit 2 = usage fallthrough on --help, NOT death. Death = rc 1.
|
|
392
|
+
alive = rc in (0, 2)
|
|
393
|
+
print(f"[{'OK' if alive else 'FAIL'}] {name} {'healthy' if alive else 'unresponsive'}")
|
|
394
|
+
return 0 if alive else 1
|
|
395
|
+
|
|
396
|
+
if cmd == "consent":
|
|
397
|
+
if len(args) < 2:
|
|
398
|
+
return _fail("consent requires a file path")
|
|
399
|
+
print(f"write_wizard commit {args[1]} # your keystroke, Governor")
|
|
400
|
+
return 0
|
|
401
|
+
|
|
402
|
+
if cmd == "schedule":
|
|
403
|
+
if len(args) < 2:
|
|
404
|
+
return _fail("schedule requires an intent")
|
|
405
|
+
intent = " ".join(args[1:])
|
|
406
|
+
task_id = schedule_task(intent)
|
|
407
|
+
print(f"[OK] task scheduled: id={task_id}, intent={intent}")
|
|
408
|
+
return 0
|
|
409
|
+
|
|
410
|
+
if cmd == "queue":
|
|
411
|
+
tasks = queue_tasks()
|
|
412
|
+
if not tasks:
|
|
413
|
+
print("(queue empty)")
|
|
414
|
+
return 0
|
|
415
|
+
for t in tasks:
|
|
416
|
+
print(f"[{t['status']}] {t['id']} ā {t['intent']}")
|
|
417
|
+
return 0
|
|
418
|
+
|
|
419
|
+
print("out of scope")
|
|
420
|
+
return 2
|
|
421
|
+
|
|
422
|
+
# ============================================================================
|
|
423
|
+
# INTERACTIVE REPL MODE ā THE COCKPIT
|
|
424
|
+
# ============================================================================
|
|
425
|
+
|
|
426
|
+
def _load_cfg():
|
|
427
|
+
import json
|
|
428
|
+
from pathlib import Path as _P
|
|
429
|
+
cfgf = _P.home() / ".godfather" / "config.json"
|
|
430
|
+
try:
|
|
431
|
+
return json.loads(cfgf.read_text())
|
|
432
|
+
except Exception:
|
|
433
|
+
return {}
|
|
434
|
+
|
|
435
|
+
def _save_cfg(cfg):
|
|
436
|
+
import json
|
|
437
|
+
from pathlib import Path as _P
|
|
438
|
+
cfgf = _P.home() / ".godfather" / "config.json"
|
|
439
|
+
cfgf.parent.mkdir(parents=True, exist_ok=True)
|
|
440
|
+
cfgf.write_text(json.dumps(cfg, indent=2))
|
|
441
|
+
|
|
442
|
+
def _pick_model():
|
|
443
|
+
"""Numbered model dropdown ā picks from the LIVE Ollama roster, persists choice."""
|
|
444
|
+
import os
|
|
445
|
+
import subprocess
|
|
446
|
+
print("\nš¾ MODEL SELECTOR (live Ollama roster)")
|
|
447
|
+
try:
|
|
448
|
+
r = subprocess.run(["ollama", "list"], capture_output=True, text=True, timeout=60)
|
|
449
|
+
names = [ln.split()[0] for ln in r.stdout.strip().splitlines()[1:] if ln.strip()]
|
|
450
|
+
except (OSError, subprocess.SubprocessError):
|
|
451
|
+
names = []
|
|
452
|
+
if not names:
|
|
453
|
+
print("[FAIL] no models found ā is Ollama running?")
|
|
454
|
+
return
|
|
455
|
+
for i, n in enumerate(names, 1):
|
|
456
|
+
print(f" [{i}] {n}")
|
|
457
|
+
try:
|
|
458
|
+
idx_pick = int(input("š model # ").strip()) - 1
|
|
459
|
+
if 0 <= idx_pick < len(names):
|
|
460
|
+
chosen = names[idx_pick]
|
|
461
|
+
os.environ["GODFATHER_MODEL"] = chosen
|
|
462
|
+
cfg = _load_cfg()
|
|
463
|
+
cfg["model"] = chosen
|
|
464
|
+
_save_cfg(cfg)
|
|
465
|
+
print(f"[OK] oracle set to {chosen} (persisted in ~/.godfather/config.json)")
|
|
466
|
+
else:
|
|
467
|
+
print("[FAIL] invalid selection")
|
|
468
|
+
except ValueError:
|
|
469
|
+
print("[FAIL] enter a number")
|
|
470
|
+
except (KeyboardInterrupt, EOFError):
|
|
471
|
+
print("\n[OK] model selection cancelled ā Sovereignty preserved.")
|
|
472
|
+
|
|
473
|
+
def _pick_voice():
|
|
474
|
+
"""Universal voice picker ā direct disk discovery, no device assumptions.
|
|
475
|
+
Scans every plausible Piper root on THIS machine + env overrides.
|
|
476
|
+
Pairing law: a voice is real only with its .onnx.json config beside it."""
|
|
477
|
+
import os
|
|
478
|
+
from pathlib import Path as _P
|
|
479
|
+
print("\nšļø VOICE SELECTOR (universal disk discovery)")
|
|
480
|
+
|
|
481
|
+
home = _P.home()
|
|
482
|
+
roots = []
|
|
483
|
+
for var in ("VOICE_ROOT", "PAPER_VOICE_ROOT", "PIPER_VOICE_PATH"):
|
|
484
|
+
val = os.environ.get(var)
|
|
485
|
+
if val:
|
|
486
|
+
roots.append(_P(val))
|
|
487
|
+
roots.extend([
|
|
488
|
+
home / "piper" / "voices",
|
|
489
|
+
home / ".local" / "share" / "piper" / "voices",
|
|
490
|
+
home / "piper-voices",
|
|
491
|
+
_P("/usr/share/piper/voices"),
|
|
492
|
+
_P("/usr/local/share/piper/voices"),
|
|
493
|
+
home / ".config" / "piper" / "voices",
|
|
494
|
+
])
|
|
495
|
+
|
|
496
|
+
found = {} # name -> root it lives in
|
|
497
|
+
for root in roots:
|
|
498
|
+
if not root.is_dir():
|
|
499
|
+
continue
|
|
500
|
+
for onnx in root.glob("*.onnx"):
|
|
501
|
+
if (root / (onnx.name + ".json")).exists() or onnx.with_suffix(".onnx.json").exists():
|
|
502
|
+
found.setdefault(onnx.stem, root)
|
|
503
|
+
|
|
504
|
+
if not found:
|
|
505
|
+
print("[FAIL] no paired Piper voices found on this machine")
|
|
506
|
+
print("roots scanned:")
|
|
507
|
+
for r in roots:
|
|
508
|
+
print(f" {r} {'(exists)' if r.is_dir() else ''}")
|
|
509
|
+
print("hint: export VOICE_ROOT=<your voices dir> and retry")
|
|
510
|
+
return
|
|
511
|
+
|
|
512
|
+
# active voice: first .active found in any root
|
|
513
|
+
active = ""
|
|
514
|
+
for r in roots:
|
|
515
|
+
af = r / ".active"
|
|
516
|
+
if af.is_file():
|
|
517
|
+
active = af.read_text().strip()
|
|
518
|
+
break
|
|
519
|
+
|
|
520
|
+
names = sorted(found)
|
|
521
|
+
print(f"found {len(names)} voice(s):")
|
|
522
|
+
for i, v in enumerate(names, 1):
|
|
523
|
+
mark = " <-- active" if v == active else ""
|
|
524
|
+
print(f" [{i:2d}] {v}{mark}")
|
|
525
|
+
|
|
526
|
+
try:
|
|
527
|
+
pick = input("š voice # ").strip()
|
|
528
|
+
idx = int(pick) - 1
|
|
529
|
+
except ValueError:
|
|
530
|
+
print("[FAIL] enter a number")
|
|
531
|
+
return
|
|
532
|
+
except (KeyboardInterrupt, EOFError):
|
|
533
|
+
print("\n[OK] voice selection cancelled ā Sovereignty preserved.")
|
|
534
|
+
return
|
|
535
|
+
if not (0 <= idx < len(names)):
|
|
536
|
+
print(f"[FAIL] pick 1-{len(names)}")
|
|
537
|
+
return
|
|
538
|
+
|
|
539
|
+
chosen = names[idx]
|
|
540
|
+
root = found[chosen]
|
|
541
|
+
(root / ".active").write_text(chosen)
|
|
542
|
+
cfg = _load_cfg()
|
|
543
|
+
cfg["voice"] = chosen
|
|
544
|
+
cfg["voice_root"] = str(root)
|
|
545
|
+
_save_cfg(cfg)
|
|
546
|
+
# offer immediate ear test
|
|
547
|
+
try:
|
|
548
|
+
rc, out = call_sibling(["voicewiz", "speak", "voice set to " + chosen])
|
|
549
|
+
if rc != 0:
|
|
550
|
+
print(f"[OK] voice set to {chosen} (no ear test ā voicewiz speak unavailable)")
|
|
551
|
+
else:
|
|
552
|
+
print(f"[OK] voice set to {chosen} ā did you hear it?")
|
|
553
|
+
except Exception:
|
|
554
|
+
print(f"[OK] voice set to {chosen}")
|
|
555
|
+
|
|
556
|
+
def _classify_repl_input(user_input):
|
|
557
|
+
"""Return the action and arguments for one REPL line."""
|
|
558
|
+
text = user_input.strip()
|
|
559
|
+
|
|
560
|
+
if not text:
|
|
561
|
+
return ("empty", [])
|
|
562
|
+
|
|
563
|
+
if text in ("/quit", "/exit"):
|
|
564
|
+
return ("quit", [])
|
|
565
|
+
|
|
566
|
+
if text == "/help":
|
|
567
|
+
return ("help", [])
|
|
568
|
+
|
|
569
|
+
if text == "/voices":
|
|
570
|
+
return ("voices", [])
|
|
571
|
+
|
|
572
|
+
if text == "/model":
|
|
573
|
+
return ("model", [])
|
|
574
|
+
|
|
575
|
+
if text == "/more-commands":
|
|
576
|
+
return ("more-commands", [])
|
|
577
|
+
|
|
578
|
+
if text.startswith("/wiz"):
|
|
579
|
+
tokens = text.split()
|
|
580
|
+
return ("wiz", tokens[1:])
|
|
581
|
+
|
|
582
|
+
if text.startswith("/"):
|
|
583
|
+
parts = text.split()
|
|
584
|
+
return ("slash", parts)
|
|
585
|
+
|
|
586
|
+
return ("chat", [text])
|
|
587
|
+
|
|
588
|
+
def _codegen_path_key(path):
|
|
589
|
+
return str(Path(path).expanduser().resolve())
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def _prepare_codegen(task, target_token, instruction, last_proposal,
|
|
593
|
+
effective_instruction=None):
|
|
594
|
+
"""Current truth + a proposal for one codegen command.
|
|
595
|
+
|
|
596
|
+
Returns (proposal, current, reused, error_line). A reviewed
|
|
597
|
+
/diff proposal is reused only for the same path and instruction
|
|
598
|
+
and only while the file still matches the reviewed base.
|
|
599
|
+
"""
|
|
600
|
+
target = Path(target_token).expanduser()
|
|
601
|
+
current, error = codegen_current_content(target)
|
|
602
|
+
if error:
|
|
603
|
+
return None, None, False, error
|
|
604
|
+
if task == "debug" and not target.is_file():
|
|
605
|
+
return None, None, False, f"[FAIL] debug requires a file: {target}"
|
|
606
|
+
if last_proposal \
|
|
607
|
+
and _codegen_path_key(last_proposal.get("path", "")) \
|
|
608
|
+
== _codegen_path_key(target) \
|
|
609
|
+
and last_proposal.get("instruction") == instruction \
|
|
610
|
+
and last_proposal.get("base_content") == current:
|
|
611
|
+
proposal = dict(last_proposal)
|
|
612
|
+
proposal["task"] = task
|
|
613
|
+
return proposal, current, True, None
|
|
614
|
+
extra = ""
|
|
615
|
+
if task == "debug":
|
|
616
|
+
notes = instruction or "no user notes"
|
|
617
|
+
if target.suffix == ".py":
|
|
618
|
+
rc, out = call_sibling(["python_wizard", "check", str(target)])
|
|
619
|
+
extra = (f"User notes: {notes}\n"
|
|
620
|
+
f"python_wizard check exit {rc}:\n{out or '(no output)'}")
|
|
621
|
+
else:
|
|
622
|
+
extra = f"User notes: {notes}"
|
|
623
|
+
proposal, error = codegen_proposal(
|
|
624
|
+
task, target, effective_instruction or instruction, current,
|
|
625
|
+
extra_context=extra)
|
|
626
|
+
if error:
|
|
627
|
+
return None, current, False, error
|
|
628
|
+
proposal["instruction"] = instruction
|
|
629
|
+
return proposal, current, False, None
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def _review_codegen_diff(target, proposal, current):
|
|
633
|
+
if target.exists():
|
|
634
|
+
print(f"[NOTE] {target} exists ā this is a full-content revision")
|
|
635
|
+
seat = proposal.get("seat")
|
|
636
|
+
byline = (f"seat: {seat_label(seat)}" if seat
|
|
637
|
+
else proposal["model"])
|
|
638
|
+
print(f"--- {proposal['task']} proposal by {byline} "
|
|
639
|
+
f"for {target} ---")
|
|
640
|
+
print(render_codegen_diff(target, current, proposal["content"]))
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _diff_codegen(args, last_proposal):
|
|
644
|
+
if len(args) < 3:
|
|
645
|
+
print("[FAIL] /diff requires a path and an instruction")
|
|
646
|
+
return last_proposal
|
|
647
|
+
target = Path(args[1]).expanduser()
|
|
648
|
+
instruction = " ".join(args[2:])
|
|
649
|
+
proposal, current, reused, error = _prepare_codegen(
|
|
650
|
+
"diff", args[1], instruction, last_proposal)
|
|
651
|
+
if error:
|
|
652
|
+
print(error)
|
|
653
|
+
return last_proposal
|
|
654
|
+
if reused:
|
|
655
|
+
print("the proposal you reviewed ā no new seat call")
|
|
656
|
+
_review_codegen_diff(target, proposal, current)
|
|
657
|
+
proposal["base_content"] = current
|
|
658
|
+
return proposal
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def _write_codegen(task, args, last_proposal):
|
|
662
|
+
if task == "build" and len(args) < 3:
|
|
663
|
+
print("[FAIL] /build requires a path and an instruction")
|
|
664
|
+
return
|
|
665
|
+
if task == "debug" and len(args) < 2:
|
|
666
|
+
print("[FAIL] /debug requires a file path")
|
|
667
|
+
return
|
|
668
|
+
target = Path(args[1]).expanduser()
|
|
669
|
+
instruction = " ".join(args[2:])
|
|
670
|
+
effective = instruction or "Repair the errors in this file."
|
|
671
|
+
proposal, current, reused, error = _prepare_codegen(
|
|
672
|
+
task, args[1], instruction, last_proposal,
|
|
673
|
+
effective_instruction=effective)
|
|
674
|
+
if error:
|
|
675
|
+
print(error)
|
|
676
|
+
return
|
|
677
|
+
if reused:
|
|
678
|
+
print("the proposal you reviewed ā no new seat call")
|
|
679
|
+
_review_codegen_diff(target, proposal, current)
|
|
680
|
+
if proposal["content"] == current:
|
|
681
|
+
print("[OK] no changes ā nothing staged, nothing written")
|
|
682
|
+
return
|
|
683
|
+
ok, detail = stage_codegen_content(target, proposal["content"])
|
|
684
|
+
if not ok:
|
|
685
|
+
print(f"[FAIL] proposal could not stage via write_wizard: {detail}")
|
|
686
|
+
return
|
|
687
|
+
print(f"[STAGED] {task} proposal for {target} ā {detail}")
|
|
688
|
+
if not confirm_codegen_write(target):
|
|
689
|
+
print("[CONSENT WALL] consent withheld ā nothing written; "
|
|
690
|
+
"the staged proposal stays in write_wizard's quarantine")
|
|
691
|
+
return
|
|
692
|
+
ok, detail = land_codegen_content(target)
|
|
693
|
+
if not ok:
|
|
694
|
+
print(f"[FAIL] write_wizard commit refused: {detail}")
|
|
695
|
+
return
|
|
696
|
+
journal("CODEGEN", target,
|
|
697
|
+
f"task={task} model={proposal['model']} "
|
|
698
|
+
f"instruction={effective}")
|
|
699
|
+
print(f"[OK] {task} proposal written to {target} through write_wizard")
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
def run_repl():
|
|
703
|
+
"""Launch interactive REPL with banner, voice, ollama chat, slash-commands."""
|
|
704
|
+
import os
|
|
705
|
+
render_banner()
|
|
706
|
+
slash_map = list_all_slash_commands()
|
|
707
|
+
session = session_load()
|
|
708
|
+
session["session"] = session.get("session", 0) + 1
|
|
709
|
+
session_save(session)
|
|
710
|
+
cfg = _load_cfg()
|
|
711
|
+
if cfg.get("model"):
|
|
712
|
+
os.environ["GODFATHER_MODEL"] = cfg["model"] # restore persisted choice
|
|
713
|
+
print(f"\nšļø SESSION {session['session']} ā Sovereign Fleet Cockpit\n")
|
|
714
|
+
print("Type /help for slash commands, /quit to exit.\n")
|
|
715
|
+
chat_memory = []
|
|
716
|
+
brainstorm_mode = False
|
|
717
|
+
brainstorm_memory = []
|
|
718
|
+
brainstorm_tool_noted = False
|
|
719
|
+
last_proposal = None
|
|
720
|
+
|
|
721
|
+
while True:
|
|
722
|
+
try:
|
|
723
|
+
raw_input = input("š godfather> ")
|
|
724
|
+
action, args = _classify_repl_input(raw_input)
|
|
725
|
+
except (EOFError, KeyboardInterrupt):
|
|
726
|
+
print("\n[OK] Godfather bows out. Sovereignty preserved.")
|
|
727
|
+
break
|
|
728
|
+
|
|
729
|
+
if not raw_input:
|
|
730
|
+
continue
|
|
731
|
+
|
|
732
|
+
if raw_input in ("/quit", "/exit"):
|
|
733
|
+
print("[OK] Godfather bows out. Sovereignty preserved.")
|
|
734
|
+
break
|
|
735
|
+
|
|
736
|
+
if brainstorm_mode and raw_input.startswith("/") \
|
|
737
|
+
and raw_input.split()[0] != "/brainstorm":
|
|
738
|
+
# Any other slash leaves brainstorm mode.
|
|
739
|
+
brainstorm_mode = False
|
|
740
|
+
brainstorm_memory = []
|
|
741
|
+
brainstorm_tool_noted = False
|
|
742
|
+
|
|
743
|
+
if raw_input.split()[0:1] == ["/brainstorm"]:
|
|
744
|
+
parts = raw_input.split()
|
|
745
|
+
if parts == ["/brainstorm", "end"]:
|
|
746
|
+
brainstorm_mode = False
|
|
747
|
+
brainstorm_memory = []
|
|
748
|
+
brainstorm_tool_noted = False
|
|
749
|
+
print("[OK] brainstorm mode ended ā hands remain off")
|
|
750
|
+
continue
|
|
751
|
+
if not brainstorm_mode:
|
|
752
|
+
brainstorm_mode = True
|
|
753
|
+
brainstorm_memory = []
|
|
754
|
+
brainstorm_tool_noted = False
|
|
755
|
+
print("[OK] brainstorm mode ā hands off: the seat can "
|
|
756
|
+
"talk only; no tools run and no files change. "
|
|
757
|
+
"Use /brainstorm end to leave.")
|
|
758
|
+
if len(parts) > 1:
|
|
759
|
+
text = raw_input[len("/brainstorm"):].strip()
|
|
760
|
+
rc, reply, brainstorm_memory = brainstorm_reply(
|
|
761
|
+
text, brainstorm_memory)
|
|
762
|
+
print(f"\nš godfather: {reply}\n" if rc == 0 else reply)
|
|
763
|
+
if rc == 0:
|
|
764
|
+
if any(line.strip().upper().startswith("TOOL:")
|
|
765
|
+
for line in reply.splitlines()) \
|
|
766
|
+
and not brainstorm_tool_noted:
|
|
767
|
+
print("[NOTE] brainstorm is hands-off: a TOOL: "
|
|
768
|
+
"line was treated as text; nothing executed.")
|
|
769
|
+
brainstorm_tool_noted = True
|
|
770
|
+
speak_if_enabled(reply)
|
|
771
|
+
continue
|
|
772
|
+
|
|
773
|
+
if raw_input.split()[0:1] == ["/build"]:
|
|
774
|
+
_write_codegen("build", raw_input.split(), last_proposal)
|
|
775
|
+
continue
|
|
776
|
+
|
|
777
|
+
if raw_input.split()[0:1] == ["/debug"]:
|
|
778
|
+
_write_codegen("debug", raw_input.split(), last_proposal)
|
|
779
|
+
continue
|
|
780
|
+
|
|
781
|
+
if raw_input.split()[0:1] == ["/diff"]:
|
|
782
|
+
# /diff already names diffwiz's two-file comparison. The
|
|
783
|
+
# codegen form is /diff <path> <instruction...>. Longer
|
|
784
|
+
# forms are unambiguously codegen; a three-token form is
|
|
785
|
+
# codegen when its last token is not a second file.
|
|
786
|
+
parts = raw_input.split()
|
|
787
|
+
if len(parts) > 3 or (len(parts) == 3 and not Path(
|
|
788
|
+
parts[2]).expanduser().is_file()):
|
|
789
|
+
last_proposal = _diff_codegen(parts, last_proposal)
|
|
790
|
+
continue
|
|
791
|
+
|
|
792
|
+
if raw_input == "/help":
|
|
793
|
+
print("š AVAILABLE SLASH COMMANDS:")
|
|
794
|
+
for cmd, desc in slash_map.items():
|
|
795
|
+
print(f" {cmd:15s} ā {desc}")
|
|
796
|
+
print(" /diff <file> <instruction> ā godfather_wizard diff review"
|
|
797
|
+
" (proposal form; the two-file form above compares two files)")
|
|
798
|
+
print(" /voices ā pick voice (numbered dropdown)")
|
|
799
|
+
print(" /model ā pick model (numbered dropdown)")
|
|
800
|
+
print(" /more-commands ā full fleet arsenal (all citizens)")
|
|
801
|
+
continue
|
|
802
|
+
|
|
803
|
+
if raw_input == "/voices":
|
|
804
|
+
_pick_voice()
|
|
805
|
+
continue
|
|
806
|
+
|
|
807
|
+
if raw_input == "/model":
|
|
808
|
+
_pick_model()
|
|
809
|
+
continue
|
|
810
|
+
|
|
811
|
+
if raw_input == "/more-commands":
|
|
812
|
+
count, names = fleet_census()
|
|
813
|
+
print(f"\nš FLEET ARSENAL ā {count} citizens")
|
|
814
|
+
print("=" * 60)
|
|
815
|
+
for w in names:
|
|
816
|
+
rc, out = call_sibling([w, "--help"])
|
|
817
|
+
lines = out.splitlines()
|
|
818
|
+
print(f"\n{w.upper()} ({'ā
' if rc == 0 else 'ā'}):")
|
|
819
|
+
for line in lines[:10]:
|
|
820
|
+
print(f" {line}")
|
|
821
|
+
if len(lines) > 10:
|
|
822
|
+
print(f" ... ({len(lines) - 10} more, run '{w} --help')")
|
|
823
|
+
print("\n" + "=" * 60)
|
|
824
|
+
continue
|
|
825
|
+
|
|
826
|
+
if raw_input.startswith("/"):
|
|
827
|
+
cmd = raw_input.split()[0]
|
|
828
|
+
if cmd in slash_map:
|
|
829
|
+
# ARG LAW: keep the full pipeline (cmd + subcommand),
|
|
830
|
+
# and split user args into proper argv tokens. Passing the
|
|
831
|
+
# whole arg string as ONE element made citizens receive
|
|
832
|
+
# 'FLEET-STATE.md' as an unknown subcommand.
|
|
833
|
+
pipeline = slash_map[cmd].split()
|
|
834
|
+
args = raw_input[len(cmd):].strip().split()
|
|
835
|
+
internal = call_internal(pipeline + args)
|
|
836
|
+
if internal is not None:
|
|
837
|
+
rc, out = internal
|
|
838
|
+
else:
|
|
839
|
+
rc, out = call_sibling(pipeline + args)
|
|
840
|
+
print(out or "(no output)")
|
|
841
|
+
elif cmd == "/wiz" and len(raw_input.split()) > 1:
|
|
842
|
+
# UNIVERSAL DELEGATION: /wiz <citizen> [args...]
|
|
843
|
+
# Resolves against the installed fleet ā a citizen that
|
|
844
|
+
# does not exist on disk cannot be invoked. Zero fabrication.
|
|
845
|
+
parts = raw_input.split()
|
|
846
|
+
target = parts[1]
|
|
847
|
+
wiz_args = parts[2:]
|
|
848
|
+
real = resolve_sibling(target)
|
|
849
|
+
if real is None:
|
|
850
|
+
candidates = [n for n in fleet_invocable_names()
|
|
851
|
+
if n.startswith(target)]
|
|
852
|
+
if not candidates:
|
|
853
|
+
print(f"out of scope ā no citizen named '{target}' installed")
|
|
854
|
+
continue
|
|
855
|
+
if len(candidates) == 1:
|
|
856
|
+
real = resolve_sibling(candidates[0])
|
|
857
|
+
else:
|
|
858
|
+
print(f"[FAIL] ambiguous citizen '{target}': {candidates}")
|
|
859
|
+
continue
|
|
860
|
+
print(f"[ROUTE] universal delegation -> {real.name} {' '.join(wiz_args)}".rstrip())
|
|
861
|
+
rc, out = call_sibling([real.name] + wiz_args)
|
|
862
|
+
print(out or "(no output)")
|
|
863
|
+
else:
|
|
864
|
+
print(f"[FAIL] unknown slash command: {cmd}")
|
|
865
|
+
print("run /help to see available commands, or /wiz <citizen> [args]")
|
|
866
|
+
continue
|
|
867
|
+
|
|
868
|
+
if brainstorm_mode:
|
|
869
|
+
rc, reply, brainstorm_memory = brainstorm_reply(
|
|
870
|
+
raw_input, brainstorm_memory)
|
|
871
|
+
print(f"\nš godfather: {reply}\n" if rc == 0 else reply)
|
|
872
|
+
if rc == 0:
|
|
873
|
+
if any(line.strip().upper().startswith("TOOL:")
|
|
874
|
+
for line in reply.splitlines()) \
|
|
875
|
+
and not brainstorm_tool_noted:
|
|
876
|
+
print("[NOTE] brainstorm is hands-off: a TOOL: "
|
|
877
|
+
"line was treated as text; nothing executed.")
|
|
878
|
+
brainstorm_tool_noted = True
|
|
879
|
+
speak_if_enabled(reply)
|
|
880
|
+
continue
|
|
881
|
+
|
|
882
|
+
# Plain text: the deterministic router answers FIRST (R4).
|
|
883
|
+
# Only unmapped text reaches the oracle, and only when a
|
|
884
|
+
# model seat is actually serving ā seatless unmapped text
|
|
885
|
+
# is plainly out of scope, never an oracle error.
|
|
886
|
+
pipeline, reason, wall = classify_route(raw_input, [])
|
|
887
|
+
if pipeline is None and wall:
|
|
888
|
+
print(f"[CONSENT WALL] {reason}")
|
|
889
|
+
print("to mutate: echo 'content' | write_wizard propose <path>")
|
|
890
|
+
print("then answer the wall yourself: write_wizard commit <path>")
|
|
891
|
+
journal("WALL_ROUTED", raw_input, "mutation detected")
|
|
892
|
+
continue
|
|
893
|
+
if pipeline is not None:
|
|
894
|
+
if wall and not _confirm_write_class(pipeline):
|
|
895
|
+
print("[FAIL] consent withheld ā nothing executed")
|
|
896
|
+
journal("WALL_ROUTED", raw_input, "consent withheld")
|
|
897
|
+
continue
|
|
898
|
+
if wall:
|
|
899
|
+
journal("WALL_ROUTED", raw_input, "consent given ā executing")
|
|
900
|
+
journal("ROUTED", raw_input, reason)
|
|
901
|
+
print(f"[ROUTE] {reason}")
|
|
902
|
+
internal = call_internal(pipeline)
|
|
903
|
+
if internal is not None:
|
|
904
|
+
rc, out = internal
|
|
905
|
+
else:
|
|
906
|
+
rc, out = call_sibling(pipeline)
|
|
907
|
+
print(out or "(no output)")
|
|
908
|
+
continue
|
|
909
|
+
|
|
910
|
+
seat = seat_for("default")
|
|
911
|
+
if seat is None:
|
|
912
|
+
print("out of scope ā no route matches this text, "
|
|
913
|
+
"and no model seat is serving")
|
|
914
|
+
continue
|
|
915
|
+
# Unmapped text + a live seat ā chat WITH HANDS: the oracle
|
|
916
|
+
# may request real tools; it only ever suggests.
|
|
917
|
+
print(f"š¤ consulting oracle (seat: {seat_label(seat)})...",
|
|
918
|
+
flush=True)
|
|
919
|
+
from .godfather_wizard import oracle_tool_loop
|
|
920
|
+
reply, chat_memory = oracle_tool_loop(raw_input, seat, chat_memory)
|
|
921
|
+
print(f"\nš godfather: {reply}\n")
|
|
922
|
+
speak_if_enabled(reply) # the whole word, uncut
|
|
923
|
+
|
|
924
|
+
if __name__ == "__main__":
|
|
925
|
+
sys.exit(main())
|