-
Notifications
You must be signed in to change notification settings - Fork 252
Expand file tree
/
Copy pathonboard.py
More file actions
145 lines (126 loc) · 5.62 KB
/
onboard.py
File metadata and controls
145 lines (126 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python3
"""agentic-stack onboarding wizard — populates .agent/memory/personal/PREFERENCES.md."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from onboard_ui import print_banner, intro, note, outro, step_done, BAR, R, MUTED, GREEN, PURPLE, WHITE, B, ORANGE
from onboard_widgets import ask_text, ask_select, ask_confirm
from onboard_render import render
from onboard_write import write_prefs, is_customized, REL
from onboard_features import write_features
_CI_VARS = ("CI", "GITHUB_ACTIONS", "CIRCLECI", "BUILDKITE", "JENKINS_URL", "TRAVIS")
def _is_ci():
if not sys.stdin.isatty(): return True
return any(os.environ.get(v) for v in _CI_VARS)
def _parse_args():
args = sys.argv[1:]
flags = {a for a in args if a.startswith("-")}
pos = [a for a in args if not a.startswith("-")]
return (
pos[0] if pos else os.getcwd(),
"--yes" in flags or "-y" in flags,
"--force" in flags,
"--reconfigure" in flags,
)
def _wizard(target, force):
"""Run the interactive Q&A. Returns answers dict, or None to abort."""
if is_customized(target) and not force:
note("Already configured",
["PREFERENCES.md already has custom content.",
"Pass --reconfigure to update it."])
return None
intro("agentic-stack setup")
note("What this does", [
"Fills .agent/memory/personal/PREFERENCES.md —",
"the FIRST file your AI reads every session.",
"Takes about 30 seconds.",
])
a = {}
a["name"] = ask_text("What should I call you?",
hint="press Enter to skip")
a["languages"] = ask_text("Primary language(s)?",
default="unspecified",
hint="e.g. TypeScript, Python, Rust")
a["style"] = ask_select("Explanation style?",
["concise", "detailed"])
a["tests"] = ask_select("Test strategy?",
["test-after", "tdd", "minimal"])
a["commits"] = ask_select("Commit message style?",
["conventional commits", "free-form", "emoji"])
a["review"] = ask_select("Code review depth?",
["critical issues only", "everything"])
# ── Optional features (beta, opt-in) ───────────────────────────────
note("Optional features", [
f"{ORANGE}[BETA]{R}{MUTED} features — off by default, opt-in only.",
"You can change this later with agentic-stack <harness> --reconfigure.",
"Mission Control beta runs only when launched; turn it off by",
"stopping the local server or editing .agent/memory/.features.json.",
])
a["feature_memory_search"] = ask_confirm(
f"Enable FTS memory search {ORANGE}[BETA]{R}?",
default=False,
)
a["feature_tldraw"] = ask_confirm(
f"Enable tldraw visual memory {ORANGE}[BETA]{R}?",
default=False,
)
a["feature_mission_control"] = ask_confirm(
f"Enable Mission Control web UI {ORANGE}[BETA]{R}?",
default=False,
)
return a
def main():
target, yes, force, reconf = _parse_args()
if _is_ci() and not yes:
print(f"[onboard] non-interactive — skipping wizard (edit {REL} manually)")
sys.exit(0)
print_banner()
if yes:
path = write_prefs(target, render({}), force=True)
# --yes defaults all optional beta features to off
features_file = write_features(target, {
"memory_search_fts": {"enabled": False, "beta": True},
"tldraw": {"enabled": False, "beta": True},
"mission_control": {"enabled": False, "beta": True},
})
print(f"{GREEN}◆{R} {WHITE}{B}PREFERENCES.md{R} written with defaults")
print(f"{MUTED} {path}{R}")
print(f"{MUTED} {features_file} (all beta features off){R}\n")
print(f"{MUTED} Mission Control beta is off by default; if enabled later, turn it off by stopping the server or editing .features.json.{R}\n")
sys.exit(0)
try:
answers = _wizard(target, force=reconf)
if answers is None:
sys.exit(0)
path = write_prefs(target, render(answers), force=reconf)
features = {
"memory_search_fts": {
"enabled": bool(answers.get("feature_memory_search")),
"beta": True,
},
"tldraw": {
"enabled": bool(answers.get("feature_tldraw")),
"beta": True,
},
"mission_control": {
"enabled": bool(answers.get("feature_mission_control")),
"beta": True,
},
}
features_file = write_features(target, features)
outro([
f"PREFERENCES.md written",
f"{path}",
f"Features: {features_file}",
"Edit either file any time — your AI re-reads them every session.",
"Mission Control beta can be turned off by stopping the local server",
"or setting mission_control.enabled=false in .agent/memory/.features.json.",
"Tip: git add .agent/memory/ to track your brain.",
"Want Claude Code to score YOUR stack's deploys/migrations as",
"high-stakes? Edit .agent/protocols/hook_patterns.json —",
"add service names like 'vercel' or 'supabase' under high_stakes.",
])
except KeyboardInterrupt:
print(f"\n\n{MUTED} Setup cancelled.{R}\n")
sys.exit(1)
if __name__ == "__main__":
main()