thehood 0.1.0-preview.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 (274) hide show
  1. package/CODE_OF_CONDUCT.md +21 -0
  2. package/CONTRIBUTING.md +58 -0
  3. package/LICENSE +21 -0
  4. package/PRIVACY.md +49 -0
  5. package/README.md +264 -0
  6. package/SECURITY.md +31 -0
  7. package/dist/bridges/chatgptWebBridge.d.ts +2 -0
  8. package/dist/bridges/chatgptWebBridge.js +981 -0
  9. package/dist/bridges/chatgptWebBridge.js.map +1 -0
  10. package/dist/cli/args.d.ts +9 -0
  11. package/dist/cli/args.js +82 -0
  12. package/dist/cli/args.js.map +1 -0
  13. package/dist/cli/format.d.ts +56 -0
  14. package/dist/cli/format.js +752 -0
  15. package/dist/cli/format.js.map +1 -0
  16. package/dist/cli/main.d.ts +2 -0
  17. package/dist/cli/main.js +996 -0
  18. package/dist/cli/main.js.map +1 -0
  19. package/dist/cli/mcpConfig.d.ts +36 -0
  20. package/dist/cli/mcpConfig.js +98 -0
  21. package/dist/cli/mcpConfig.js.map +1 -0
  22. package/dist/index.d.ts +37 -0
  23. package/dist/index.js +38 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/mcp/protocol.d.ts +44 -0
  26. package/dist/mcp/protocol.js +33 -0
  27. package/dist/mcp/protocol.js.map +1 -0
  28. package/dist/mcp/server.d.ts +1 -0
  29. package/dist/mcp/server.js +106 -0
  30. package/dist/mcp/server.js.map +1 -0
  31. package/dist/mcp/tools.d.ts +10 -0
  32. package/dist/mcp/tools.js +2200 -0
  33. package/dist/mcp/tools.js.map +1 -0
  34. package/dist/mcp/validation.d.ts +8 -0
  35. package/dist/mcp/validation.js +67 -0
  36. package/dist/mcp/validation.js.map +1 -0
  37. package/dist/providers/chatgptWeb.d.ts +2 -0
  38. package/dist/providers/chatgptWeb.js +26 -0
  39. package/dist/providers/chatgptWeb.js.map +1 -0
  40. package/dist/providers/claudeCode.d.ts +4 -0
  41. package/dist/providers/claudeCode.js +32 -0
  42. package/dist/providers/claudeCode.js.map +1 -0
  43. package/dist/providers/codexCli.d.ts +6 -0
  44. package/dist/providers/codexCli.js +25 -0
  45. package/dist/providers/codexCli.js.map +1 -0
  46. package/dist/providers/codexCliModels.d.ts +23 -0
  47. package/dist/providers/codexCliModels.js +147 -0
  48. package/dist/providers/codexCliModels.js.map +1 -0
  49. package/dist/providers/localCommand.d.ts +26 -0
  50. package/dist/providers/localCommand.js +614 -0
  51. package/dist/providers/localCommand.js.map +1 -0
  52. package/dist/providers/markdownPayload.d.ts +7 -0
  53. package/dist/providers/markdownPayload.js +29 -0
  54. package/dist/providers/markdownPayload.js.map +1 -0
  55. package/dist/providers/responseSchema.d.ts +3 -0
  56. package/dist/providers/responseSchema.js +187 -0
  57. package/dist/providers/responseSchema.js.map +1 -0
  58. package/dist/providers/router.d.ts +3 -0
  59. package/dist/providers/router.js +21 -0
  60. package/dist/providers/router.js.map +1 -0
  61. package/dist/providers/stub.d.ts +2 -0
  62. package/dist/providers/stub.js +177 -0
  63. package/dist/providers/stub.js.map +1 -0
  64. package/dist/providers/types.d.ts +37 -0
  65. package/dist/providers/types.js +2 -0
  66. package/dist/providers/types.js.map +1 -0
  67. package/dist/runtime/agentBoard.d.ts +79 -0
  68. package/dist/runtime/agentBoard.js +166 -0
  69. package/dist/runtime/agentBoard.js.map +1 -0
  70. package/dist/runtime/agentBoardArtifact.d.ts +9 -0
  71. package/dist/runtime/agentBoardArtifact.js +171 -0
  72. package/dist/runtime/agentBoardArtifact.js.map +1 -0
  73. package/dist/runtime/agentRunner.d.ts +17 -0
  74. package/dist/runtime/agentRunner.js +92 -0
  75. package/dist/runtime/agentRunner.js.map +1 -0
  76. package/dist/runtime/approvalInbox.d.ts +54 -0
  77. package/dist/runtime/approvalInbox.js +143 -0
  78. package/dist/runtime/approvalInbox.js.map +1 -0
  79. package/dist/runtime/approvalPolicy.d.ts +11 -0
  80. package/dist/runtime/approvalPolicy.js +58 -0
  81. package/dist/runtime/approvalPolicy.js.map +1 -0
  82. package/dist/runtime/artifacts.d.ts +23 -0
  83. package/dist/runtime/artifacts.js +48 -0
  84. package/dist/runtime/artifacts.js.map +1 -0
  85. package/dist/runtime/browserManager.d.ts +37 -0
  86. package/dist/runtime/browserManager.js +356 -0
  87. package/dist/runtime/browserManager.js.map +1 -0
  88. package/dist/runtime/canonicalMemory.d.ts +23 -0
  89. package/dist/runtime/canonicalMemory.js +134 -0
  90. package/dist/runtime/canonicalMemory.js.map +1 -0
  91. package/dist/runtime/chatGptPageReadiness.d.ts +16 -0
  92. package/dist/runtime/chatGptPageReadiness.js +74 -0
  93. package/dist/runtime/chatGptPageReadiness.js.map +1 -0
  94. package/dist/runtime/commandRunner.d.ts +18 -0
  95. package/dist/runtime/commandRunner.js +115 -0
  96. package/dist/runtime/commandRunner.js.map +1 -0
  97. package/dist/runtime/commandSafety.d.ts +7 -0
  98. package/dist/runtime/commandSafety.js +61 -0
  99. package/dist/runtime/commandSafety.js.map +1 -0
  100. package/dist/runtime/config.d.ts +10 -0
  101. package/dist/runtime/config.js +107 -0
  102. package/dist/runtime/config.js.map +1 -0
  103. package/dist/runtime/crewLanes.d.ts +2 -0
  104. package/dist/runtime/crewLanes.js +123 -0
  105. package/dist/runtime/crewLanes.js.map +1 -0
  106. package/dist/runtime/criticPolicy.d.ts +17 -0
  107. package/dist/runtime/criticPolicy.js +50 -0
  108. package/dist/runtime/criticPolicy.js.map +1 -0
  109. package/dist/runtime/defaults.d.ts +5 -0
  110. package/dist/runtime/defaults.js +100 -0
  111. package/dist/runtime/defaults.js.map +1 -0
  112. package/dist/runtime/directives.d.ts +3 -0
  113. package/dist/runtime/directives.js +218 -0
  114. package/dist/runtime/directives.js.map +1 -0
  115. package/dist/runtime/doctor.d.ts +36 -0
  116. package/dist/runtime/doctor.js +185 -0
  117. package/dist/runtime/doctor.js.map +1 -0
  118. package/dist/runtime/errors.d.ts +20 -0
  119. package/dist/runtime/errors.js +41 -0
  120. package/dist/runtime/errors.js.map +1 -0
  121. package/dist/runtime/externalTransfer.d.ts +20 -0
  122. package/dist/runtime/externalTransfer.js +156 -0
  123. package/dist/runtime/externalTransfer.js.map +1 -0
  124. package/dist/runtime/fanout.d.ts +64 -0
  125. package/dist/runtime/fanout.js +263 -0
  126. package/dist/runtime/fanout.js.map +1 -0
  127. package/dist/runtime/gitEvidence.d.ts +10 -0
  128. package/dist/runtime/gitEvidence.js +80 -0
  129. package/dist/runtime/gitEvidence.js.map +1 -0
  130. package/dist/runtime/handoffs.d.ts +32 -0
  131. package/dist/runtime/handoffs.js +100 -0
  132. package/dist/runtime/handoffs.js.map +1 -0
  133. package/dist/runtime/ids.d.ts +2 -0
  134. package/dist/runtime/ids.js +4 -0
  135. package/dist/runtime/ids.js.map +1 -0
  136. package/dist/runtime/localStateIgnore.d.ts +9 -0
  137. package/dist/runtime/localStateIgnore.js +98 -0
  138. package/dist/runtime/localStateIgnore.js.map +1 -0
  139. package/dist/runtime/loop.d.ts +14 -0
  140. package/dist/runtime/loop.js +1863 -0
  141. package/dist/runtime/loop.js.map +1 -0
  142. package/dist/runtime/loopRecommendation.d.ts +109 -0
  143. package/dist/runtime/loopRecommendation.js +566 -0
  144. package/dist/runtime/loopRecommendation.js.map +1 -0
  145. package/dist/runtime/loopResponsibilities.d.ts +2 -0
  146. package/dist/runtime/loopResponsibilities.js +395 -0
  147. package/dist/runtime/loopResponsibilities.js.map +1 -0
  148. package/dist/runtime/loopRunner.d.ts +28 -0
  149. package/dist/runtime/loopRunner.js +81 -0
  150. package/dist/runtime/loopRunner.js.map +1 -0
  151. package/dist/runtime/operatorNextActions.d.ts +2 -0
  152. package/dist/runtime/operatorNextActions.js +344 -0
  153. package/dist/runtime/operatorNextActions.js.map +1 -0
  154. package/dist/runtime/paths.d.ts +9 -0
  155. package/dist/runtime/paths.js +14 -0
  156. package/dist/runtime/paths.js.map +1 -0
  157. package/dist/runtime/permissions.d.ts +9 -0
  158. package/dist/runtime/permissions.js +73 -0
  159. package/dist/runtime/permissions.js.map +1 -0
  160. package/dist/runtime/progressPacket.d.ts +12 -0
  161. package/dist/runtime/progressPacket.js +512 -0
  162. package/dist/runtime/progressPacket.js.map +1 -0
  163. package/dist/runtime/protectedPaths.d.ts +6 -0
  164. package/dist/runtime/protectedPaths.js +48 -0
  165. package/dist/runtime/protectedPaths.js.map +1 -0
  166. package/dist/runtime/providers.d.ts +13 -0
  167. package/dist/runtime/providers.js +60 -0
  168. package/dist/runtime/providers.js.map +1 -0
  169. package/dist/runtime/reconciliation.d.ts +17 -0
  170. package/dist/runtime/reconciliation.js +283 -0
  171. package/dist/runtime/reconciliation.js.map +1 -0
  172. package/dist/runtime/redaction.d.ts +1 -0
  173. package/dist/runtime/redaction.js +5 -0
  174. package/dist/runtime/redaction.js.map +1 -0
  175. package/dist/runtime/remoteRepoContext.d.ts +77 -0
  176. package/dist/runtime/remoteRepoContext.js +316 -0
  177. package/dist/runtime/remoteRepoContext.js.map +1 -0
  178. package/dist/runtime/repoContext.d.ts +50 -0
  179. package/dist/runtime/repoContext.js +399 -0
  180. package/dist/runtime/repoContext.js.map +1 -0
  181. package/dist/runtime/repoGateway.d.ts +64 -0
  182. package/dist/runtime/repoGateway.js +308 -0
  183. package/dist/runtime/repoGateway.js.map +1 -0
  184. package/dist/runtime/responseContracts.d.ts +3 -0
  185. package/dist/runtime/responseContracts.js +86 -0
  186. package/dist/runtime/responseContracts.js.map +1 -0
  187. package/dist/runtime/reviewLanes.d.ts +2 -0
  188. package/dist/runtime/reviewLanes.js +343 -0
  189. package/dist/runtime/reviewLanes.js.map +1 -0
  190. package/dist/runtime/reviewRouting.d.ts +51 -0
  191. package/dist/runtime/reviewRouting.js +152 -0
  192. package/dist/runtime/reviewRouting.js.map +1 -0
  193. package/dist/runtime/revisionPacket.d.ts +38 -0
  194. package/dist/runtime/revisionPacket.js +144 -0
  195. package/dist/runtime/revisionPacket.js.map +1 -0
  196. package/dist/runtime/revisionTrail.d.ts +2 -0
  197. package/dist/runtime/revisionTrail.js +162 -0
  198. package/dist/runtime/revisionTrail.js.map +1 -0
  199. package/dist/runtime/role-assignment.d.ts +4 -0
  200. package/dist/runtime/role-assignment.js +21 -0
  201. package/dist/runtime/role-assignment.js.map +1 -0
  202. package/dist/runtime/roleRoster.d.ts +28 -0
  203. package/dist/runtime/roleRoster.js +96 -0
  204. package/dist/runtime/roleRoster.js.map +1 -0
  205. package/dist/runtime/runInsights.d.ts +121 -0
  206. package/dist/runtime/runInsights.js +305 -0
  207. package/dist/runtime/runInsights.js.map +1 -0
  208. package/dist/runtime/runMonitor.d.ts +33 -0
  209. package/dist/runtime/runMonitor.js +143 -0
  210. package/dist/runtime/runMonitor.js.map +1 -0
  211. package/dist/runtime/runtime.d.ts +15 -0
  212. package/dist/runtime/runtime.js +199 -0
  213. package/dist/runtime/runtime.js.map +1 -0
  214. package/dist/runtime/runtimeInfo.d.ts +9 -0
  215. package/dist/runtime/runtimeInfo.js +76 -0
  216. package/dist/runtime/runtimeInfo.js.map +1 -0
  217. package/dist/runtime/store.d.ts +4 -0
  218. package/dist/runtime/store.js +48 -0
  219. package/dist/runtime/store.js.map +1 -0
  220. package/dist/runtime/summons.d.ts +25 -0
  221. package/dist/runtime/summons.js +403 -0
  222. package/dist/runtime/summons.js.map +1 -0
  223. package/dist/runtime/teamPresets.d.ts +14 -0
  224. package/dist/runtime/teamPresets.js +153 -0
  225. package/dist/runtime/teamPresets.js.map +1 -0
  226. package/dist/runtime/types.d.ts +505 -0
  227. package/dist/runtime/types.js +28 -0
  228. package/dist/runtime/types.js.map +1 -0
  229. package/dist/runtime/validationCommands.d.ts +18 -0
  230. package/dist/runtime/validationCommands.js +106 -0
  231. package/dist/runtime/validationCommands.js.map +1 -0
  232. package/dist/tui/dashboard.d.ts +41 -0
  233. package/dist/tui/dashboard.js +1115 -0
  234. package/dist/tui/dashboard.js.map +1 -0
  235. package/docs/ARCHITECTURE.md +277 -0
  236. package/docs/CLI_SPEC.md +396 -0
  237. package/docs/CODEX_SETUP.md +288 -0
  238. package/docs/COMPLETION_CONTRACT.md +52 -0
  239. package/docs/CONTRIBUTOR_GUIDE.md +70 -0
  240. package/docs/DEMO.md +62 -0
  241. package/docs/GLOSSARY.md +46 -0
  242. package/docs/GOAL_LOOP_SCHEDULE.md +50 -0
  243. package/docs/KNOWN_LIMITATIONS.md +29 -0
  244. package/docs/LICENSING.md +21 -0
  245. package/docs/LOOP_RECIPES.md +290 -0
  246. package/docs/LOOP_SELECTION_UX.md +118 -0
  247. package/docs/MCP_SPEC.md +689 -0
  248. package/docs/MEMORY_AND_RECONCILIATION.md +222 -0
  249. package/docs/NPM_PUBLISHING.md +51 -0
  250. package/docs/OPEN_DECISIONS.md +81 -0
  251. package/docs/PROMPT_SCHEMAS.md +411 -0
  252. package/docs/PROVIDER_ADAPTERS.md +323 -0
  253. package/docs/PROVIDER_MATRIX.md +21 -0
  254. package/docs/PUBLIC_REPO_READINESS.md +49 -0
  255. package/docs/RESEARCH_NOTES.md +92 -0
  256. package/docs/ROADMAP.md +94 -0
  257. package/docs/ROLE_CONTRACTS.md +252 -0
  258. package/docs/RUNTIME_LOOP.md +240 -0
  259. package/docs/SECURITY_AND_PRIVACY.md +161 -0
  260. package/docs/TESTING_AND_VERIFICATION.md +180 -0
  261. package/docs/TRUST_MODEL.md +65 -0
  262. package/docs/decisions/0001-runtime-first-cli-and-mcp.md +23 -0
  263. package/docs/decisions/0002-provider-neutral-role-mapping.md +43 -0
  264. package/docs/decisions/0003-separate-implementation-and-verification.md +27 -0
  265. package/docs/product/README.md +14 -0
  266. package/docs/product/model-selection.md +88 -0
  267. package/docs/product/positioning.md +37 -0
  268. package/docs/product/pro-usage-modes.md +70 -0
  269. package/docs/product/roadmap.md +57 -0
  270. package/docs/product/role-policy.md +89 -0
  271. package/docs/product/runtime-invariants.md +44 -0
  272. package/docs/release/v0.1.0-preview.0.md +48 -0
  273. package/examples/stub-demo/README.md +25 -0
  274. package/package.json +55 -0
@@ -0,0 +1,1115 @@
1
+ import path from "node:path";
2
+ export const settingsPageIds = [
3
+ "overview",
4
+ "crew",
5
+ "providers",
6
+ "budgets",
7
+ "safety",
8
+ "browser",
9
+ "commands",
10
+ "all"
11
+ ];
12
+ const ansi = {
13
+ amber: "\x1b[1;38;5;214m",
14
+ amberDim: "\x1b[38;5;178m",
15
+ cyan: "\x1b[38;5;81m",
16
+ green: "\x1b[38;5;82m",
17
+ red: "\x1b[1;38;5;203m",
18
+ purple: "\x1b[38;5;141m",
19
+ muted: "\x1b[90m",
20
+ bold: "\x1b[1m",
21
+ reset: "\x1b[0m"
22
+ };
23
+ const wideMasthead = [
24
+ "████████╗██╗ ██╗███████╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗",
25
+ "╚══██╔══╝██║ ██║██╔════╝ ██║ ██║██╔═══██╗██╔═══██╗██╔══██╗",
26
+ " ██║ ███████║█████╗ ███████║██║ ██║██║ ██║██║ ██║",
27
+ " ██║ ██╔══██║██╔══╝ ██╔══██║██║ ██║██║ ██║██║ ██║",
28
+ " ██║ ██║ ██║███████╗ ██║ ██║╚██████╔╝╚██████╔╝██████╔╝",
29
+ " ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝"
30
+ ];
31
+ const mastheadSubtitles = [
32
+ "repo = neighborhood • agents = crew • runs = jobs • approvals = checkpoints • verifier = closer",
33
+ "repo = neighborhood • agents = crew • runs = jobs • approvals = checkpoints",
34
+ "repo = neighborhood • agents = crew • runs = jobs",
35
+ "repo = neighborhood • agents = crew"
36
+ ];
37
+ const markLines = [
38
+ " .-''''-.",
39
+ " .' TH '.",
40
+ " / [====] \\",
41
+ " | | ██ | |",
42
+ " \\ '----' /",
43
+ " '. crew .'",
44
+ " '-..-'"
45
+ ];
46
+ const terminalWidth = () => process.stdout.columns ?? 80;
47
+ const useAnsiColor = () => Boolean(process.stdout.isTTY && !process.env.NO_COLOR && process.env.TERM !== "dumb");
48
+ const normalizeWidth = (width) => Math.max(72, Math.min(width, 132));
49
+ const textLength = (value) => Array.from(value).length;
50
+ const style = (value, tone, useColor) => useColor ? `${ansi[tone]}${value}${ansi.reset}` : value;
51
+ const modeLabel = (value) => value.replace(/_/g, "-");
52
+ const phaseLabel = (phase) => phase.replace(/_/g, " ");
53
+ const laneStateLabel = (state) => state.replace(/_/g, " ");
54
+ const loopResponsibilityStatusLabel = (status) => status.replace(/_/g, " ");
55
+ const quoteArg = (value) => /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
56
+ const padEndText = (value, width) => `${value}${" ".repeat(Math.max(0, width - textLength(value)))}`;
57
+ const truncateEnd = (value, maxLength) => {
58
+ if (maxLength <= 0) {
59
+ return "";
60
+ }
61
+ return textLength(value) <= maxLength
62
+ ? value
63
+ : `${Array.from(value).slice(0, Math.max(0, maxLength - 3)).join("")}...`;
64
+ };
65
+ const truncateMiddle = (value, maxLength) => {
66
+ const chars = Array.from(value);
67
+ if (chars.length <= maxLength) {
68
+ return value;
69
+ }
70
+ if (maxLength <= 3) {
71
+ return ".".repeat(maxLength);
72
+ }
73
+ const left = Math.ceil((maxLength - 3) / 2);
74
+ const right = Math.floor((maxLength - 3) / 2);
75
+ return `${chars.slice(0, left).join("")}...${chars.slice(chars.length - right).join("")}`;
76
+ };
77
+ const centered = (value, width) => {
78
+ const remaining = Math.max(0, width - textLength(value));
79
+ const left = Math.floor(remaining / 2);
80
+ return `${" ".repeat(left)}${value}${" ".repeat(remaining - left)}`;
81
+ };
82
+ const frame = (title, lines, width, useColor, tone = "amberDim") => {
83
+ const safeTitle = truncateEnd(title, Math.max(0, width - 8));
84
+ const topFill = "─".repeat(Math.max(0, width - textLength(safeTitle) - 5));
85
+ const top = `╭─ ${safeTitle} ${topFill}╮`;
86
+ const bottom = `╰${"─".repeat(Math.max(0, width - 2))}╯`;
87
+ const innerWidth = Math.max(0, width - 4);
88
+ const body = lines.map((line) => {
89
+ const content = truncateEnd(line, innerWidth);
90
+ return `│ ${padEndText(content, innerWidth)} │`;
91
+ });
92
+ return [
93
+ style(top, tone, useColor),
94
+ ...body,
95
+ style(bottom, tone, useColor)
96
+ ].join("\n");
97
+ };
98
+ const tableRow = (columns) => columns.map(([value, width]) => padEndText(truncateEnd(value, width), width)).join(" ").trimEnd();
99
+ const shortRunId = (runId) => runId.startsWith("run_") ? `run_${runId.slice(4, 12)}` : truncateEnd(runId, 12);
100
+ const localTime = (value) => {
101
+ const date = new Date(value);
102
+ if (Number.isNaN(date.getTime())) {
103
+ return "unknown";
104
+ }
105
+ return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
106
+ };
107
+ const providerState = (health, providerId) => {
108
+ const provider = health.providers.find((candidate) => candidate.id === providerId);
109
+ if (!provider) {
110
+ return "not configured";
111
+ }
112
+ return provider.issues.length === 0 ? "ready" : provider.issues.join(", ");
113
+ };
114
+ const assignmentValue = (assignment) => assignment ? `${assignment.provider}:${assignment.model}` : "unassigned";
115
+ const repoArgs = (repoPath) => repoPath === "." || repoPath === process.cwd() ? [] : ["--repo", quoteArg(repoPath)];
116
+ const cliCommandPrefix = () => {
117
+ const entry = process.argv[1];
118
+ if (!entry) {
119
+ return ["thehood"];
120
+ }
121
+ if (path.basename(entry) === "thehood") {
122
+ return ["thehood"];
123
+ }
124
+ const absoluteEntry = path.isAbsolute(entry) ? entry : path.resolve(process.cwd(), entry);
125
+ const relativeEntry = path.relative(process.cwd(), absoluteEntry);
126
+ const displayEntry = relativeEntry && !relativeEntry.startsWith("..") && !path.isAbsolute(relativeEntry)
127
+ ? relativeEntry.startsWith(".") ? relativeEntry : `.${path.sep}${relativeEntry}`
128
+ : absoluteEntry;
129
+ return [quoteArg(displayEntry)];
130
+ };
131
+ const cliCommand = (repoPath, args) => [...cliCommandPrefix(), ...args, ...repoArgs(repoPath)].join(" ");
132
+ const roleSetCommand = (repoPath, role, assignment) => cliCommand(repoPath, ["roles", "set", role, assignment ? assignmentValue(assignment) : "<provider:model>"]);
133
+ const teamApplyCommand = (repoPath, presetId) => cliCommand(repoPath, ["teams", "apply", presetId]);
134
+ const configSetCommand = (repoPath, key, value) => cliCommand(repoPath, ["config", "set", key, String(value)]);
135
+ const approvalModeCommand = (repoPath, mode) => cliCommand(repoPath, ["approvals", "policy", "set", "mode", mode]);
136
+ const externalTransferModeCommand = (repoPath, mode) => cliCommand(repoPath, ["approvals", "policy", "set", "external-transfers", mode]);
137
+ const browserCommand = (subcommand) => [...cliCommandPrefix(), "browser", subcommand].join(" ");
138
+ const settingsCommandGroups = {
139
+ corePages: ["crew", "providers", "budgets", "safety", "browser", "commands"]
140
+ };
141
+ const statusWord = (ready) => ready ? "ready" : "needs attention";
142
+ const roleAlias = (role) => {
143
+ switch (role) {
144
+ case "orchestrator":
145
+ return "ORCHESTRATOR";
146
+ case "planner":
147
+ return "PLANNER";
148
+ case "researcher":
149
+ return "RESEARCHER";
150
+ case "implementer":
151
+ return "IMPLEMENTER";
152
+ case "qa":
153
+ return "QA";
154
+ case "verifier":
155
+ return "VERIFIER";
156
+ case "critic":
157
+ return "CRITIC";
158
+ case "integrator":
159
+ return "INTEGRATOR";
160
+ case "citation":
161
+ return "CITATION";
162
+ }
163
+ };
164
+ const roleFlavor = (role) => {
165
+ switch (role) {
166
+ case "orchestrator":
167
+ return "shot caller";
168
+ case "implementer":
169
+ return "builder";
170
+ case "qa":
171
+ return "tester";
172
+ case "verifier":
173
+ return "closer";
174
+ case "critic":
175
+ return "challenger";
176
+ case "planner":
177
+ return "planner";
178
+ case "researcher":
179
+ return "scout";
180
+ case "integrator":
181
+ return "runtime keys";
182
+ case "citation":
183
+ return "receipts";
184
+ }
185
+ };
186
+ const rosterState = (item) => item.issues.length > 0 ? item.issues.join(", ") : item.state;
187
+ const nextActions = (browser) => {
188
+ const actions = [];
189
+ if (!browser.cdpReachable) {
190
+ actions.push("thehood browser start");
191
+ }
192
+ else if (!browser.chatGptTabFound) {
193
+ actions.push("open ChatGPT in the TheHood browser profile");
194
+ }
195
+ else if (browser.issues.includes("chatgpt_auth_required")) {
196
+ actions.push("sign in to ChatGPT in the TheHood browser profile");
197
+ }
198
+ else if (!browser.chatGptComposerReady) {
199
+ actions.push("open a ready ChatGPT composer in the TheHood browser profile");
200
+ }
201
+ actions.push("thehood doctor --repo <repo>");
202
+ actions.push("thehood plan \"...\"");
203
+ if (browser.readyForBridge) {
204
+ actions.push("thehood plan \"...\" --orchestrator chatgpt-web:chatgpt-pro");
205
+ }
206
+ return actions;
207
+ };
208
+ const phaseRank = (run) => {
209
+ switch (run.phase) {
210
+ case "approval_gate":
211
+ return 0;
212
+ case "transfer_gate":
213
+ return 1;
214
+ case "provider_wait":
215
+ return 2;
216
+ case "running":
217
+ return 3;
218
+ case "failed":
219
+ return 4;
220
+ case "completed":
221
+ return 5;
222
+ case "aborted":
223
+ return 6;
224
+ }
225
+ };
226
+ const prioritizeRuns = (runs) => [...runs].sort((left, right) => {
227
+ const rank = phaseRank(left) - phaseRank(right);
228
+ if (rank !== 0) {
229
+ return rank;
230
+ }
231
+ return right.updatedAt.localeCompare(left.updatedAt);
232
+ });
233
+ const primaryNextAction = (input, currentRun) => {
234
+ const runAction = currentRun?.operatorNextActions[0];
235
+ if (runAction?.commandHint) {
236
+ return runAction.commandHint;
237
+ }
238
+ if (runAction?.label) {
239
+ return runAction.label;
240
+ }
241
+ return nextActions(input.browser)[0] ?? "no immediate action";
242
+ };
243
+ const buildView = (input) => {
244
+ const prioritizedRuns = prioritizeRuns(input.runMonitor);
245
+ const currentRun = prioritizedRuns[0];
246
+ const readyCrew = input.roleRoster.filter((role) => rosterState(role) === "ready").length;
247
+ return {
248
+ input,
249
+ prioritizedRuns,
250
+ currentRun,
251
+ readyCrew,
252
+ totalCrew: input.roleRoster.length,
253
+ pendingCheckpoints: input.approvalInbox.pendingApprovals.length,
254
+ nextAction: primaryNextAction(input, currentRun)
255
+ };
256
+ };
257
+ const mastheadSubtitle = (width) => {
258
+ const subtitle = mastheadSubtitles.find((candidate) => textLength(candidate) <= width);
259
+ const fallback = mastheadSubtitles[mastheadSubtitles.length - 1] ?? "";
260
+ return subtitle ?? truncateEnd(fallback, width);
261
+ };
262
+ const renderMasthead = (width, useColor) => {
263
+ const bodyWidth = width - 4;
264
+ const subtitle = mastheadSubtitle(bodyWidth);
265
+ const body = [
266
+ "",
267
+ ...wideMasthead.map((line) => centered(line, bodyWidth)),
268
+ "",
269
+ centered(subtitle, bodyWidth)
270
+ ];
271
+ return style(frame("THEHOOD", body, width, false, "amber"), "amber", useColor);
272
+ };
273
+ export const renderHeader = (width = terminalWidth()) => renderMasthead(normalizeWidth(width), useAnsiColor());
274
+ const activeJobLines = (view, width) => {
275
+ const run = view.currentRun;
276
+ const input = view.input;
277
+ const approvalState = view.pendingCheckpoints > 0 ? `${view.pendingCheckpoints} pending` : "clear";
278
+ if (!run) {
279
+ return [
280
+ "ACTIVE JOB",
281
+ "no jobs found",
282
+ `repo ${truncateMiddle(input.repoPath, width - 5)}`,
283
+ `runtime ${input.health.runtime.name} ${input.health.runtime.version}`,
284
+ `browser ${statusWord(input.browser.readyForBridge)}`,
285
+ `next ${truncateEnd(view.nextAction, width - 5)}`
286
+ ];
287
+ }
288
+ return [
289
+ "ACTIVE JOB",
290
+ `${shortRunId(run.runId)} ${run.state} / ${run.mode}`,
291
+ `phase ${phaseLabel(run.phase)} checkpoints ${approvalState}`,
292
+ `repo ${truncateMiddle(input.repoPath, width - 5)}`,
293
+ `signal ${truncateEnd(run.detail, width - 7)}`,
294
+ `next ${truncateEnd(view.nextAction, width - 5)}`
295
+ ];
296
+ };
297
+ const crewLines = (input, width) => {
298
+ const primaryRoles = ["orchestrator", "implementer", "qa", "verifier", "critic"];
299
+ const roles = primaryRoles
300
+ .map((role) => input.roleRoster.find((item) => item.role === role))
301
+ .filter((item) => Boolean(item));
302
+ const guests = roles
303
+ .filter((role) => role.assignment && role.assignment.provider !== "codex-cli")
304
+ .map((role) => `${roleAlias(role.role)}:${role.assignmentLabel}`);
305
+ const roleWidth = 13;
306
+ const flavorWidth = 11;
307
+ const assignmentWidth = Math.max(8, width - roleWidth - flavorWidth - 4);
308
+ return [
309
+ "CREW + CAPABILITIES",
310
+ ...roles.map((role) => tableRow([
311
+ [roleAlias(role.role), roleWidth],
312
+ [truncateEnd(role.assignmentLabel, assignmentWidth), assignmentWidth],
313
+ [truncateEnd(roleFlavor(role.role), flavorWidth), flavorWidth]
314
+ ])),
315
+ tableRow([
316
+ ["GUESTS", roleWidth],
317
+ [guests.length > 0 ? truncateEnd(guests.join(", "), assignmentWidth) : "none active", assignmentWidth],
318
+ ["runtime", flavorWidth]
319
+ ])
320
+ ];
321
+ };
322
+ const markColumnLines = (input, width) => [
323
+ "MARK / BLOCK",
324
+ ...markLines,
325
+ `runtime ${truncateEnd(`${input.health.runtime.name} ${input.health.runtime.version}`, width - 8)}`,
326
+ `browser ${statusWord(input.browser.readyForBridge)}`
327
+ ];
328
+ const equalizeColumns = (columns) => {
329
+ const height = Math.max(...columns.map((column) => column.length));
330
+ return columns.map((column) => [
331
+ ...column,
332
+ ...Array.from({ length: Math.max(0, height - column.length) }, () => "")
333
+ ]);
334
+ };
335
+ const joinColumns = (columns) => {
336
+ const equalized = equalizeColumns(columns.map((column) => column.lines));
337
+ const height = equalized[0]?.length ?? 0;
338
+ return Array.from({ length: height }, (_, index) => columns.map((column, columnIndex) => padEndText(truncateEnd(equalized[columnIndex]?.[index] ?? "", column.width), column.width)).join(" "));
339
+ };
340
+ const runOwner = (run) => run.provider ?? run.lane ?? "runtime";
341
+ const runSignal = (run) => run.operatorNextActions[0]?.label ?? run.detail;
342
+ const runMonitorRows = (runs, width, limit) => {
343
+ if (runs.length === 0) {
344
+ return ["no jobs found"];
345
+ }
346
+ if (width < 92) {
347
+ const signalWidth = Math.max(16, width - 36);
348
+ return [
349
+ tableRow([
350
+ ["RUN", 12],
351
+ ["STATE", 13],
352
+ ["TIME", 5],
353
+ ["SIGNAL", signalWidth]
354
+ ]),
355
+ ...runs.slice(0, limit).map((run) => tableRow([
356
+ [shortRunId(run.runId), 12],
357
+ [phaseLabel(run.phase), 13],
358
+ [localTime(run.updatedAt), 5],
359
+ [truncateEnd(runSignal(run), signalWidth), signalWidth]
360
+ ]))
361
+ ];
362
+ }
363
+ const signalWidth = Math.max(16, width - 69);
364
+ return [
365
+ tableRow([
366
+ ["PRI", 3],
367
+ ["RUN", 12],
368
+ ["STATE", 13],
369
+ ["MODE", 8],
370
+ ["OWNER", 18],
371
+ ["TIME", 5],
372
+ ["SIGNAL", signalWidth]
373
+ ]),
374
+ ...runs.slice(0, limit).map((run, index) => tableRow([
375
+ [String(index + 1).padStart(2, "0"), 3],
376
+ [shortRunId(run.runId), 12],
377
+ [phaseLabel(run.phase), 13],
378
+ [run.mode, 8],
379
+ [truncateEnd(runOwner(run), 18), 18],
380
+ [localTime(run.updatedAt), 5],
381
+ [truncateEnd(runSignal(run), signalWidth), signalWidth]
382
+ ]))
383
+ ];
384
+ };
385
+ const approvalCommand = (approval, command) => [
386
+ "thehood",
387
+ "ui",
388
+ "approvals",
389
+ "--repo",
390
+ quoteArg(approval.repoPath),
391
+ `--${command}`,
392
+ approval.runId
393
+ ].join(" ");
394
+ const transferPreviewCommand = (approval) => [
395
+ "thehood",
396
+ "transfer",
397
+ "preview",
398
+ approval.runId,
399
+ "--repo",
400
+ quoteArg(approval.repoPath)
401
+ ].join(" ");
402
+ const autopilotTransferPreviewCommand = (approval) => [
403
+ "thehood",
404
+ "transfer",
405
+ "preview",
406
+ approval.runId,
407
+ "--repo",
408
+ quoteArg(approval.repoPath)
409
+ ].join(" ");
410
+ const checkpointRows = (inbox, width, limit) => {
411
+ if (inbox.pendingApprovals.length === 0) {
412
+ return [
413
+ tableRow([["CLEAR", 8], ["edit", 12], ["blocked by runtime policy", Math.max(16, width - 24)]]),
414
+ tableRow([["CLEAR", 8], ["deps", 12], ["no dependency installs in this UI slice", Math.max(16, width - 24)]]),
415
+ tableRow([["GATED", 8], ["network", 12], ["runtime approval policy controls external effects", Math.max(16, width - 24)]])
416
+ ];
417
+ }
418
+ return inbox.pendingApprovals.slice(0, limit).flatMap((approval, index) => {
419
+ const lines = [
420
+ tableRow([
421
+ [`!! ${index + 1}`, 5],
422
+ [shortRunId(approval.runId), 12],
423
+ [approval.state, 17],
424
+ [truncateEnd(approval.reason, Math.max(18, width - 40)), Math.max(18, width - 40)]
425
+ ]),
426
+ `approve ${truncateEnd(approvalCommand(approval, "approve"), Math.max(20, width - 8))}`
427
+ ];
428
+ if (approval.artifacts.some((artifact) => artifact.kind === "transfer_manifest")) {
429
+ lines.push(`preview ${truncateEnd(transferPreviewCommand(approval), Math.max(20, width - 8))}`);
430
+ }
431
+ return lines;
432
+ });
433
+ };
434
+ const autopilotRows = (inbox, width, limit) => {
435
+ if (inbox.recentAutopilotApprovals.length === 0) {
436
+ return ["no recent autopilot approvals"];
437
+ }
438
+ return [
439
+ tableRow([["TIME", 5], ["GATE", 24], ["NOTE", Math.max(16, width - 33)]]),
440
+ ...inbox.recentAutopilotApprovals.slice(0, limit).flatMap((approval) => {
441
+ const lines = [
442
+ tableRow([
443
+ [localTime(approval.createdAt), 5],
444
+ [truncateEnd(approval.gate ?? "autopilot", 24), 24],
445
+ [truncateEnd(approval.policyReason ?? approval.message, Math.max(16, width - 33)), Math.max(16, width - 33)]
446
+ ])
447
+ ];
448
+ if (approval.artifact?.kind === "transfer_manifest") {
449
+ lines.push(`preview ${truncateEnd(autopilotTransferPreviewCommand(approval), Math.max(20, width - 8))}`);
450
+ }
451
+ return lines;
452
+ })
453
+ ];
454
+ };
455
+ const handoffEndpoint = (label, assignment, fallback) => {
456
+ const endpoint = label ?? fallback;
457
+ return assignment ? `${endpoint} (${assignment})` : endpoint;
458
+ };
459
+ const handoffDestination = (handoff) => {
460
+ if (handoff.toLabel) {
461
+ return handoffEndpoint(handoff.toLabel, handoff.toAssignment, "Runtime");
462
+ }
463
+ if (handoff.kind === "approval_gate" || handoff.kind === "approval_auto_approved") {
464
+ return "Approval Gate";
465
+ }
466
+ if (handoff.kind === "completion") {
467
+ return "Completed";
468
+ }
469
+ return "Runtime";
470
+ };
471
+ const handoffRows = (inbox, width, limit) => {
472
+ if (inbox.recentHandoffs.length === 0) {
473
+ return ["no recent handoffs"];
474
+ }
475
+ return inbox.recentHandoffs.slice(0, limit).map((handoff) => tableRow([
476
+ [truncateEnd(handoffEndpoint(handoff.fromLabel, handoff.fromAssignment, "Runtime"), 24), 24],
477
+ ["->", 2],
478
+ [truncateEnd(handoffDestination(handoff), 24), 24],
479
+ [truncateEnd(handoff.gate ?? handoff.kind, Math.max(10, width - 56)), Math.max(10, width - 56)]
480
+ ]));
481
+ };
482
+ const laneOwnerLabel = (lane) => lane.owner.assignment ? `${lane.owner.label} (${lane.owner.assignment})` : lane.owner.label;
483
+ const laneSatisfactionLabel = (lane) => lane.required
484
+ ? lane.satisfiesRequired ? "satisfies" : "missing"
485
+ : "advisory";
486
+ const reviewLaneRows = (run, width) => {
487
+ if (!run || run.reviewLanes.length === 0) {
488
+ return [
489
+ tableRow([["safety", 12], ["verifier", 16], ["no approval weakening", Math.max(16, width - 32)]]),
490
+ tableRow([["display", 12], ["qa", 16], ["wide and compact output stays readable", Math.max(16, width - 32)]]),
491
+ tableRow([["regression", 12], ["critic", 16], ["static data still renders", Math.max(16, width - 32)]])
492
+ ];
493
+ }
494
+ return run.reviewLanes.slice(0, 4).map((lane) => tableRow([
495
+ [lane.kind, 12],
496
+ [truncateEnd(laneStateLabel(lane.state), 16), 16],
497
+ [truncateEnd(`${laneSatisfactionLabel(lane)} ${laneOwnerLabel(lane)}`, Math.max(16, width - 32)), Math.max(16, width - 32)]
498
+ ]));
499
+ };
500
+ const responsibilityRows = (run, width) => {
501
+ if (!run || run.loopResponsibilities.length === 0) {
502
+ return ["loop responsibilities not reported"];
503
+ }
504
+ return run.loopResponsibilities.slice(0, 5).map((item) => tableRow([
505
+ [item.kind, 17],
506
+ [loopResponsibilityStatusLabel(item.status), 12],
507
+ [truncateEnd(item.owner.assignment ?? item.owner.label, Math.max(16, width - 33)), Math.max(16, width - 33)]
508
+ ]));
509
+ };
510
+ const crewLaneRows = (run, width) => {
511
+ if (!run || run.crewLanes.length === 0) {
512
+ return ["crew trail not reported"];
513
+ }
514
+ return run.crewLanes.slice(0, 6).map((lane) => tableRow([
515
+ [lane.kind, 17],
516
+ [loopResponsibilityStatusLabel(lane.status), 12],
517
+ [lane.authority, 10],
518
+ [truncateEnd(lane.owner.assignment ?? lane.owner.label, Math.max(16, width - 43)), Math.max(16, width - 43)]
519
+ ]));
520
+ };
521
+ const revisionTrailRows = (run, width) => {
522
+ if (!run || run.revisionTrail.length === 0) {
523
+ return [];
524
+ }
525
+ return run.revisionTrail.slice(-3).map((item) => tableRow([
526
+ [truncateEnd(item.reasonCode ?? "revision", 22), 22],
527
+ [truncateEnd(item.status, 16), 16],
528
+ [truncateEnd(item.sourceRole ?? "source", 10), 10],
529
+ [truncateEnd(item.repairResponseRef ? "repair linked" : item.packetArtifactRef, Math.max(16, width - 52)), Math.max(16, width - 52)]
530
+ ]));
531
+ };
532
+ const browserLines = (input, width) => [
533
+ "BROWSER / REMOTE SURFACE",
534
+ `cdp ${input.browser.cdpReachable ? "reachable" : "unreachable"} ${truncateEnd(input.browser.cdpUrl, Math.max(10, width - 18))}`,
535
+ `profile ${truncateMiddle(input.browser.profilePath, Math.max(10, width - 10))}`,
536
+ `tab ${input.browser.chatGptTabFound ? "found" : "not found"} auth ${input.browser.chatGptAuthenticated ? "ready" : "not ready"}`,
537
+ `composer ${input.browser.chatGptComposerReady ? "ready" : "not ready"} provider ${truncateEnd(providerState(input.health, "chatgpt-web"), Math.max(8, width - 35))}`
538
+ ];
539
+ const statusRail = (view, width, useColor) => {
540
+ const input = view.input;
541
+ const rail = [
542
+ `health ${input.health.runtime.name}`,
543
+ `policy ${modeLabel(input.approvalPolicy.mode)}`,
544
+ `crew ${view.readyCrew}/${view.totalCrew}`,
545
+ `checkpoints ${view.pendingCheckpoints > 0 ? `${view.pendingCheckpoints} pending` : "clear"}`,
546
+ `browser ${statusWord(input.browser.readyForBridge)}`,
547
+ "refs-only"
548
+ ].map((item) => `[${item}]`).join(" ");
549
+ return style(truncateEnd(rail, width), view.pendingCheckpoints > 0 ? "red" : "green", useColor);
550
+ };
551
+ const promptRail = (view, width, useColor) => style(truncateEnd(`hood> next: ${view.nextAction}`, width), "amber", useColor);
552
+ const exactApprovalCommandLines = (inbox) => {
553
+ if (inbox.pendingApprovals.length === 0) {
554
+ return [];
555
+ }
556
+ return [
557
+ "Exact Checkpoint Commands",
558
+ ...inbox.pendingApprovals.flatMap((approval) => [
559
+ ` ${approval.runId}`,
560
+ ` approve ${approvalCommand(approval, "approve")}`,
561
+ ` reject ${approvalCommand(approval, "reject")}`,
562
+ ` revise ${approvalCommand(approval, "revise")}`,
563
+ ` resume thehood continue ${approval.runId} --repo ${quoteArg(approval.repoPath)}`,
564
+ ...(approval.artifacts.some((artifact) => artifact.kind === "transfer_manifest")
565
+ ? [` preview ${transferPreviewCommand(approval)}`]
566
+ : [])
567
+ ])
568
+ ];
569
+ };
570
+ const commandCenterWide = (view, width, useColor) => {
571
+ const innerWidth = width - 4;
572
+ const gapTotal = 4;
573
+ const leftWidth = 22;
574
+ const rightWidth = width >= 124 ? 48 : 44;
575
+ const centerWidth = Math.max(30, innerWidth - leftWidth - rightWidth - gapTotal);
576
+ const sideBySide = joinColumns([
577
+ { width: leftWidth, lines: markColumnLines(view.input, leftWidth) },
578
+ { width: centerWidth, lines: activeJobLines(view, centerWidth) },
579
+ { width: rightWidth, lines: crewLines(view.input, rightWidth) }
580
+ ]);
581
+ const halfGap = 2;
582
+ const leftPanelWidth = Math.floor((innerWidth - halfGap) / 2);
583
+ const rightPanelWidth = innerWidth - halfGap - leftPanelWidth;
584
+ const checkpointAndAuto = joinColumns([
585
+ { width: leftPanelWidth, lines: ["CHECKPOINTS / APPROVAL GATES", ...checkpointRows(view.input.approvalInbox, leftPanelWidth, 3)] },
586
+ { width: rightPanelWidth, lines: ["AUTOPILOT LEDGER", ...autopilotRows(view.input.approvalInbox, rightPanelWidth, 4)] }
587
+ ]);
588
+ const handoffAndReview = joinColumns([
589
+ { width: leftPanelWidth, lines: ["HANDOFFS", ...handoffRows(view.input.approvalInbox, leftPanelWidth, 4)] },
590
+ { width: rightPanelWidth, lines: ["REVIEW LANES", ...reviewLaneRows(view.currentRun, rightPanelWidth)] }
591
+ ]);
592
+ const revisionRows = revisionTrailRows(view.currentRun, innerWidth);
593
+ const lines = [
594
+ ...sideBySide,
595
+ "",
596
+ "RUN MONITOR / JOB BOARD",
597
+ ...runMonitorRows(view.prioritizedRuns, innerWidth, 5),
598
+ "",
599
+ ...checkpointAndAuto,
600
+ "",
601
+ ...handoffAndReview,
602
+ "",
603
+ "CREW TRAIL",
604
+ ...crewLaneRows(view.currentRun, innerWidth),
605
+ ...(revisionRows.length > 0
606
+ ? [
607
+ "",
608
+ "REVISION TRAIL",
609
+ ...revisionRows
610
+ ]
611
+ : []),
612
+ "",
613
+ "LOOP RESPONSIBILITIES",
614
+ ...responsibilityRows(view.currentRun, innerWidth)
615
+ ];
616
+ return frame(`THEHOOD COMMAND CENTER mode: ${view.currentRun?.mode.toUpperCase() ?? "IDLE"}`, lines, width, useColor, "amber");
617
+ };
618
+ const commandCenterCompact = (view, width, useColor) => {
619
+ const innerWidth = width - 4;
620
+ const crew = crewLines(view.input, innerWidth).slice(1);
621
+ const revisionRows = revisionTrailRows(view.currentRun, innerWidth);
622
+ const lines = [
623
+ "MARK ACTIVE JOB",
624
+ tableRow([[".-TH-.", 12], [view.currentRun ? `${shortRunId(view.currentRun.runId)} ${view.currentRun.state} / ${view.currentRun.mode}` : "no active job", innerWidth - 14]]),
625
+ tableRow([["(crew)", 12], [`checkpoints ${view.pendingCheckpoints > 0 ? `${view.pendingCheckpoints} pending` : "clear"} autopilot ${modeLabel(view.input.approvalPolicy.mode)}`, innerWidth - 14]]),
626
+ tableRow([["`---'", 12], [`repo ${truncateMiddle(view.input.repoPath, innerWidth - 19)}`, innerWidth - 14]]),
627
+ tableRow([["", 12], [`next ${truncateEnd(view.nextAction, innerWidth - 19)}`, innerWidth - 14]]),
628
+ "",
629
+ "CREW",
630
+ ...crew.slice(0, 5),
631
+ "",
632
+ "BROWSER / REMOTE SURFACE",
633
+ ...browserLines(view.input, innerWidth).slice(1),
634
+ "",
635
+ "JOB BOARD",
636
+ ...runMonitorRows(view.prioritizedRuns, innerWidth, 4),
637
+ "",
638
+ "CHECKPOINTS / APPROVAL GATES",
639
+ ...checkpointRows(view.input.approvalInbox, innerWidth, 2),
640
+ "",
641
+ "AUTOPILOT LEDGER",
642
+ ...autopilotRows(view.input.approvalInbox, innerWidth, 3),
643
+ "",
644
+ "HANDOFFS",
645
+ ...handoffRows(view.input.approvalInbox, innerWidth, 3),
646
+ "",
647
+ "CREW TRAIL",
648
+ ...crewLaneRows(view.currentRun, innerWidth),
649
+ ...(revisionRows.length > 0
650
+ ? [
651
+ "",
652
+ "REVISION TRAIL",
653
+ ...revisionRows
654
+ ]
655
+ : []),
656
+ "",
657
+ "REVIEW LANES",
658
+ ...reviewLaneRows(view.currentRun, innerWidth)
659
+ ];
660
+ return frame(`THEHOOD COMMAND CENTER mode: ${view.currentRun?.mode.toUpperCase() ?? "IDLE"}`, lines, width, useColor, "amber");
661
+ };
662
+ const teamPresetIsActive = (input, preset) => Object.entries(preset.roles).every(([role, assignment]) => assignmentValue(input.config.roles[role]) === assignmentValue(assignment));
663
+ const settingsOverviewLines = (input, width) => [
664
+ "HOOD PROFILE",
665
+ `repo ${truncateMiddle(input.repoPath, width - 5)}`,
666
+ `config ${truncateMiddle(input.configPath, width - 8)}`,
667
+ `version ${input.config.version}`,
668
+ `runtime ${input.health.runtime.name} ${input.health.runtime.version}`,
669
+ `browser ${statusWord(input.browser.readyForBridge)}`
670
+ ];
671
+ const settingsCrewRows = (input, width) => {
672
+ const commandWidth = Math.max(24, width - 45);
673
+ return [
674
+ tableRow([
675
+ ["ROLE", 12],
676
+ ["ASSIGNMENT", 18],
677
+ ["STATE", 9],
678
+ ["COMMAND", commandWidth]
679
+ ]),
680
+ ...input.roleRoster.map((role) => tableRow([
681
+ [role.role, 12],
682
+ [truncateEnd(assignmentValue(role.assignment), 18), 18],
683
+ [truncateEnd(rosterState(role), 9), 9],
684
+ [truncateEnd(roleSetCommand(input.repoPath, role.role, role.assignment), commandWidth), commandWidth]
685
+ ]))
686
+ ];
687
+ };
688
+ const settingsTeamRows = (input, width) => {
689
+ const commandWidth = Math.max(24, width - 40);
690
+ return [
691
+ tableRow([
692
+ ["ON", 4],
693
+ ["PRESET", 16],
694
+ ["SUMMARY", 14],
695
+ ["COMMAND", commandWidth]
696
+ ]),
697
+ ...input.teamPresets.map((preset) => tableRow([
698
+ [teamPresetIsActive(input, preset) ? "yes" : "", 4],
699
+ [preset.id, 16],
700
+ [truncateEnd(preset.summary, 14), 14],
701
+ [truncateEnd(teamApplyCommand(input.repoPath, preset.id), commandWidth), commandWidth]
702
+ ]))
703
+ ];
704
+ };
705
+ const settingsBudgetLines = (input, width) => [
706
+ tableRow([
707
+ ["SETTING", 18],
708
+ ["VALUE", 7],
709
+ ["COMMAND", Math.max(24, width - 29)]
710
+ ]),
711
+ tableRow([
712
+ ["max iterations", 18],
713
+ [String(input.config.defaults.maxIterations), 7],
714
+ [truncateEnd(configSetCommand(input.repoPath, "max-iterations", input.config.defaults.maxIterations), Math.max(24, width - 29)), Math.max(24, width - 29)]
715
+ ]),
716
+ tableRow([
717
+ ["fanout max items", 18],
718
+ [String(input.config.defaults.fanoutMaxItems), 7],
719
+ [truncateEnd(configSetCommand(input.repoPath, "fanout-max-items", input.config.defaults.fanoutMaxItems), Math.max(24, width - 29)), Math.max(24, width - 29)]
720
+ ])
721
+ ];
722
+ const settingsApprovalLines = (input, width) => {
723
+ const commandWidth = Math.max(24, width - 29);
724
+ return [
725
+ tableRow([["MODE", 18], ["VALUE", 7], ["COMMAND", commandWidth]]),
726
+ tableRow([
727
+ ["approval posture", 18],
728
+ [modeLabel(input.config.approvalPolicy.mode), 7],
729
+ [truncateEnd(approvalModeCommand(input.repoPath, modeLabel(input.config.approvalPolicy.mode)), commandWidth), commandWidth]
730
+ ]),
731
+ tableRow([
732
+ ["external transfers", 18],
733
+ [modeLabel(input.config.approvalPolicy.externalTransfers.mode), 7],
734
+ [truncateEnd(externalTransferModeCommand(input.repoPath, modeLabel(input.config.approvalPolicy.externalTransfers.mode)), commandWidth), commandWidth]
735
+ ]),
736
+ tableRow([
737
+ ["max auto bytes", 18],
738
+ [String(input.config.approvalPolicy.externalTransfers.maxAutoApproveBytes), 7],
739
+ [truncateEnd("edit .thehood/config.json approvalPolicy.externalTransfers.maxAutoApproveBytes", commandWidth), commandWidth]
740
+ ]),
741
+ tableRow([
742
+ ["transfer rules", 18],
743
+ [String(input.config.approvalPolicy.externalTransfers.rules.length), 7],
744
+ [truncateEnd("edit .thehood/config.json approvalPolicy.externalTransfers.rules", commandWidth), commandWidth]
745
+ ])
746
+ ];
747
+ };
748
+ const settingsSafetyLines = (input, width) => [
749
+ tableRow([["RAIL", 28], ["VALUE", 8], ["CONTROL", Math.max(16, width - 40)]]),
750
+ tableRow([
751
+ ["edit requires approval", 28],
752
+ [String(input.config.defaults.editRequiresApproval), 8],
753
+ [truncateEnd("edit .thehood/config.json defaults.editRequiresApproval", Math.max(16, width - 40)), Math.max(16, width - 40)]
754
+ ]),
755
+ tableRow([
756
+ ["dependency install approval", 28],
757
+ [String(input.config.defaults.dependencyInstallRequiresApproval), 8],
758
+ [truncateEnd("edit .thehood/config.json defaults.dependencyInstallRequiresApproval", Math.max(16, width - 40)), Math.max(16, width - 40)]
759
+ ]),
760
+ tableRow([
761
+ ["network requires approval", 28],
762
+ [String(input.config.defaults.networkRequiresApproval), 8],
763
+ [truncateEnd("edit .thehood/config.json defaults.networkRequiresApproval", Math.max(16, width - 40)), Math.max(16, width - 40)]
764
+ ]),
765
+ tableRow([
766
+ ["protected test paths", 28],
767
+ [String(input.config.defaults.protectedTestPaths.length), 8],
768
+ [truncateEnd(input.config.defaults.protectedTestPaths.join(", "), Math.max(16, width - 40)), Math.max(16, width - 40)]
769
+ ])
770
+ ];
771
+ const settingsProviderRows = (input, width) => {
772
+ const modelsWidth = Math.max(14, width - 58);
773
+ return [
774
+ tableRow([
775
+ ["PROVIDER", 16],
776
+ ["STATE", 9],
777
+ ["MODE", 13],
778
+ ["HEALTH", 12],
779
+ ["MODELS", modelsWidth]
780
+ ]),
781
+ ...input.providers.map((provider) => tableRow([
782
+ [provider.id, 16],
783
+ [provider.enabled ? "enabled" : "disabled", 9],
784
+ [provider.defaultAccessMode, 13],
785
+ [truncateEnd(providerState(input.health, provider.id), 12), 12],
786
+ [truncateEnd(provider.models.join(", "), modelsWidth), modelsWidth]
787
+ ]))
788
+ ];
789
+ };
790
+ const settingsBrowserLines = (input, width) => [
791
+ `cdp ${input.browser.cdpReachable ? "reachable" : "unreachable"} ${truncateEnd(input.browser.cdpUrl, Math.max(10, width - 18))}`,
792
+ `profile ${truncateMiddle(input.browser.profilePath, Math.max(10, width - 10))}`,
793
+ `tab ${input.browser.chatGptTabFound ? "found" : "not found"}`,
794
+ `auth ${input.browser.chatGptAuthenticated ? "ready" : "not ready"} composer ${input.browser.chatGptComposerReady ? "ready" : "not ready"}`,
795
+ `start ${truncateEnd(browserCommand("start"), Math.max(10, width - 7))}`,
796
+ `status ${truncateEnd(browserCommand("status"), Math.max(10, width - 8))}`,
797
+ `stop ${truncateEnd(browserCommand("stop"), Math.max(10, width - 6))}`
798
+ ];
799
+ const settingsActionDeckLines = (input, width) => [
800
+ "Source-Of-Truth Commands",
801
+ " Safety",
802
+ ` ${approvalModeCommand(input.repoPath, "manual")}`,
803
+ ` ${approvalModeCommand(input.repoPath, "auto-low-risk")}`,
804
+ ` ${approvalModeCommand(input.repoPath, "autopilot")}`,
805
+ ` ${externalTransferModeCommand(input.repoPath, "manual")}`,
806
+ ` ${externalTransferModeCommand(input.repoPath, "auto-low-risk")}`,
807
+ " Budgets",
808
+ ` ${configSetCommand(input.repoPath, "max-iterations", input.config.defaults.maxIterations)}`,
809
+ ` ${configSetCommand(input.repoPath, "fanout-max-items", input.config.defaults.fanoutMaxItems)}`,
810
+ " Team",
811
+ ...input.teamPresets.map((preset) => ` ${teamApplyCommand(input.repoPath, preset.id)}`),
812
+ " Browser",
813
+ ` ${browserCommand("status")}`,
814
+ ` ${browserCommand("start")}`,
815
+ ` ${browserCommand("stop")}`,
816
+ "",
817
+ "Current Crew",
818
+ ...input.roleRoster.map((role) => ` ${truncateEnd(roleSetCommand(input.repoPath, role.role, role.assignment), width - 2)}`)
819
+ ];
820
+ const settingsUnderlyingCommandLines = (input, width) => [
821
+ "Underlying Commands",
822
+ ` ${approvalModeCommand(input.repoPath, "manual")}`,
823
+ ` ${approvalModeCommand(input.repoPath, "auto-low-risk")}`,
824
+ ` ${approvalModeCommand(input.repoPath, "autopilot")}`,
825
+ ` ${configSetCommand(input.repoPath, "max-iterations", input.config.defaults.maxIterations)}`,
826
+ ` ${configSetCommand(input.repoPath, "fanout-max-items", input.config.defaults.fanoutMaxItems)}`,
827
+ ` ${externalTransferModeCommand(input.repoPath, "manual")}`,
828
+ ` ${externalTransferModeCommand(input.repoPath, "auto-low-risk")}`,
829
+ ` ${browserCommand("status")}`,
830
+ ` ${browserCommand("start")}`,
831
+ ` ${browserCommand("stop")}`,
832
+ ` ${cliCommand(input.repoPath, ["teams"])}`,
833
+ ` ${cliCommand(input.repoPath, ["providers"])}`,
834
+ ` ${cliCommand(input.repoPath, ["roster"])}`,
835
+ "",
836
+ "Crew Role Commands",
837
+ ...input.roleRoster.map((role) => ` ${roleSetCommand(input.repoPath, role.role, role.assignment)}`),
838
+ "",
839
+ "Editable In Config",
840
+ ` ${truncateMiddle(input.configPath, width - 4)}`,
841
+ " approvalPolicy.externalTransfers.maxAutoApproveBytes",
842
+ " approvalPolicy.externalTransfers.rules",
843
+ " defaults.editRequiresApproval",
844
+ " defaults.dependencyInstallRequiresApproval",
845
+ " defaults.networkRequiresApproval",
846
+ " defaults.protectedTestPaths",
847
+ " providers.<id>"
848
+ ];
849
+ const settingsCommandDeckLines = (input, width) => [
850
+ ...settingsActionDeckLines(input, width),
851
+ "",
852
+ ...settingsUnderlyingCommandLines(input, width)
853
+ ];
854
+ const settingsPageDescription = {
855
+ overview: "compact status and page menu",
856
+ crew: "role assignments and set commands",
857
+ providers: "provider states and available models",
858
+ budgets: "iteration and fan-out limits",
859
+ safety: "approval policy and protected rails",
860
+ browser: "ChatGPT Web bridge status",
861
+ commands: "exact CLI commands and config keys",
862
+ all: "full settings cockpit"
863
+ };
864
+ const settingsPageCommand = (input, page) => page === "overview"
865
+ ? cliCommand(input.repoPath, ["ui", "settings"])
866
+ : cliCommand(input.repoPath, ["ui", "settings", page]);
867
+ const settingsSummaryLines = (input, width) => {
868
+ const readyCrew = input.roleRoster.filter((role) => rosterState(role) === "ready").length;
869
+ const enabledProviders = input.providers.filter((provider) => provider.enabled).length;
870
+ const readyProviders = input.providers.filter((provider) => provider.enabled && providerState(input.health, provider.id) === "ready").length;
871
+ return [
872
+ ...settingsOverviewLines(input, width),
873
+ `policy ${modeLabel(input.config.approvalPolicy.mode)} transfers ${modeLabel(input.config.approvalPolicy.externalTransfers.mode)}`,
874
+ `crew ${readyCrew}/${input.roleRoster.length} ready providers ${readyProviders}/${enabledProviders} ready`,
875
+ `budgets max iterations ${input.config.defaults.maxIterations} fanout ${input.config.defaults.fanoutMaxItems}`,
876
+ `hard stops protected tests, secret-risk transfers, destructive/dependency/network commands`
877
+ ];
878
+ };
879
+ const settingsCrewSnapshotLines = (input, width) => {
880
+ const primaryRoles = ["orchestrator", "implementer", "qa", "verifier", "critic"];
881
+ const roles = primaryRoles
882
+ .map((role) => input.roleRoster.find((item) => item.role === role))
883
+ .filter((item) => Boolean(item));
884
+ const assignmentWidth = Math.max(12, width - 31);
885
+ return [
886
+ tableRow([["ROLE", 14], ["ASSIGNMENT", assignmentWidth], ["STATE", 13]]),
887
+ ...roles.map((role) => tableRow([
888
+ [role.role, 14],
889
+ [truncateEnd(assignmentValue(role.assignment), assignmentWidth), assignmentWidth],
890
+ [truncateEnd(rosterState(role), 13), 13]
891
+ ]))
892
+ ];
893
+ };
894
+ const settingsPageMenuLines = (input, width) => {
895
+ const pages = [...settingsCommandGroups.corePages];
896
+ const descriptionWidth = width >= 96 ? 34 : 24;
897
+ const commandWidth = Math.max(16, width - descriptionWidth - 14);
898
+ return [
899
+ tableRow([["PAGE", 10], ["WHAT", descriptionWidth], ["COMMAND", commandWidth]]),
900
+ ...pages.map((page) => tableRow([
901
+ [page, 10],
902
+ [truncateEnd(settingsPageDescription[page], descriptionWidth), descriptionWidth],
903
+ [truncateEnd(settingsPageCommand(input, page), commandWidth), commandWidth]
904
+ ]))
905
+ ];
906
+ };
907
+ const renderSettingsOverview = (input, width, useColor) => {
908
+ const innerWidth = width - 4;
909
+ const lines = [
910
+ ...settingsSummaryLines(input, innerWidth),
911
+ "",
912
+ "CREW SNAPSHOT",
913
+ ...settingsCrewSnapshotLines(input, innerWidth),
914
+ "",
915
+ "OPEN A SETTINGS PAGE",
916
+ ...settingsPageMenuLines(input, innerWidth)
917
+ ];
918
+ return frame("THEHOOD SETTINGS COCKPIT", lines, width, useColor, "amber");
919
+ };
920
+ const renderSettingsCrew = (input, width, useColor) => {
921
+ const innerWidth = width - 4;
922
+ const lines = [
923
+ "CREW ASSIGNMENTS",
924
+ ...settingsCrewRows(input, innerWidth)
925
+ ];
926
+ return frame("THEHOOD SETTINGS / CREW", lines, width, useColor, "amber");
927
+ };
928
+ const renderSettingsProviders = (input, width, useColor) => {
929
+ const innerWidth = width - 4;
930
+ const lines = [
931
+ "PROVIDER BAY",
932
+ ...settingsProviderRows(input, innerWidth)
933
+ ];
934
+ return frame("THEHOOD SETTINGS / PROVIDERS", lines, width, useColor, "amber");
935
+ };
936
+ const renderSettingsBudgets = (input, width, useColor) => {
937
+ const innerWidth = width - 4;
938
+ const lines = [
939
+ "BUDGETS",
940
+ ...settingsBudgetLines(input, innerWidth)
941
+ ];
942
+ return frame("THEHOOD SETTINGS / BUDGETS", lines, width, useColor, "amber");
943
+ };
944
+ const renderSettingsSafety = (input, width, useColor) => {
945
+ const innerWidth = width - 4;
946
+ const lines = [
947
+ "AUTOPILOT / TRANSFERS",
948
+ ...settingsApprovalLines(input, innerWidth),
949
+ "",
950
+ "SAFETY RAILS",
951
+ ...settingsSafetyLines(input, innerWidth)
952
+ ];
953
+ return frame("THEHOOD SETTINGS / SAFETY", lines, width, useColor, "amber");
954
+ };
955
+ const renderSettingsBrowser = (input, width, useColor) => {
956
+ const innerWidth = width - 4;
957
+ const lines = [
958
+ "BROWSER BRIDGE",
959
+ ...settingsBrowserLines(input, innerWidth)
960
+ ];
961
+ return frame("THEHOOD SETTINGS / BROWSER", lines, width, useColor, "amber");
962
+ };
963
+ const renderSettingsCommands = (input, width, useColor) => {
964
+ const innerWidth = width - 4;
965
+ return frame("THEHOOD SETTINGS / COMMANDS", settingsCommandDeckLines(input, innerWidth), width, useColor, "amber");
966
+ };
967
+ const settingsWide = (input, width, useColor) => {
968
+ const innerWidth = width - 4;
969
+ const gap = 2;
970
+ const halfWidth = Math.floor((innerWidth - gap) / 2);
971
+ const rightWidth = innerWidth - gap - halfWidth;
972
+ const top = joinColumns([
973
+ { width: halfWidth, lines: settingsOverviewLines(input, halfWidth) },
974
+ { width: rightWidth, lines: ["BUDGETS", ...settingsBudgetLines(input, rightWidth)] }
975
+ ]);
976
+ const policy = joinColumns([
977
+ { width: halfWidth, lines: ["AUTOPILOT / TRANSFERS", ...settingsApprovalLines(input, halfWidth)] },
978
+ { width: rightWidth, lines: ["SAFETY RAILS", ...settingsSafetyLines(input, rightWidth)] }
979
+ ]);
980
+ const bridge = joinColumns([
981
+ { width: halfWidth, lines: ["PROVIDER BAY", ...settingsProviderRows(input, halfWidth)] },
982
+ { width: rightWidth, lines: ["BROWSER BRIDGE", ...settingsBrowserLines(input, rightWidth)] }
983
+ ]);
984
+ const lines = [
985
+ ...top,
986
+ "",
987
+ "CREW ASSIGNMENTS",
988
+ ...settingsCrewRows(input, innerWidth),
989
+ "",
990
+ ...settingsActionDeckLines(input, innerWidth),
991
+ "",
992
+ "TEAM PRESETS",
993
+ ...settingsTeamRows(input, innerWidth),
994
+ "",
995
+ ...policy,
996
+ "",
997
+ ...bridge,
998
+ "",
999
+ ...settingsUnderlyingCommandLines(input, innerWidth)
1000
+ ];
1001
+ return frame("THEHOOD SETTINGS / ALL", lines, width, useColor, "amber");
1002
+ };
1003
+ const settingsCompact = (input, width, useColor) => {
1004
+ const innerWidth = width - 4;
1005
+ const lines = [
1006
+ ...settingsOverviewLines(input, innerWidth),
1007
+ "",
1008
+ "CREW ASSIGNMENTS",
1009
+ ...settingsCrewRows(input, innerWidth),
1010
+ "",
1011
+ ...settingsActionDeckLines(input, innerWidth),
1012
+ "",
1013
+ "TEAM PRESETS",
1014
+ ...settingsTeamRows(input, innerWidth),
1015
+ "",
1016
+ "BUDGETS",
1017
+ ...settingsBudgetLines(input, innerWidth),
1018
+ "",
1019
+ "AUTOPILOT / TRANSFERS",
1020
+ ...settingsApprovalLines(input, innerWidth),
1021
+ "",
1022
+ "SAFETY RAILS",
1023
+ ...settingsSafetyLines(input, innerWidth),
1024
+ "",
1025
+ "PROVIDER BAY",
1026
+ ...settingsProviderRows(input, innerWidth),
1027
+ "",
1028
+ "BROWSER BRIDGE",
1029
+ ...settingsBrowserLines(input, innerWidth),
1030
+ "",
1031
+ ...settingsUnderlyingCommandLines(input, innerWidth)
1032
+ ];
1033
+ return frame("THEHOOD SETTINGS / ALL", lines, width, useColor, "amber");
1034
+ };
1035
+ const renderSettingsPage = (input, page, width, useColor) => {
1036
+ switch (page) {
1037
+ case "overview":
1038
+ return renderSettingsOverview(input, width, useColor);
1039
+ case "crew":
1040
+ return renderSettingsCrew(input, width, useColor);
1041
+ case "providers":
1042
+ return renderSettingsProviders(input, width, useColor);
1043
+ case "budgets":
1044
+ return renderSettingsBudgets(input, width, useColor);
1045
+ case "safety":
1046
+ return renderSettingsSafety(input, width, useColor);
1047
+ case "browser":
1048
+ return renderSettingsBrowser(input, width, useColor);
1049
+ case "commands":
1050
+ return renderSettingsCommands(input, width, useColor);
1051
+ case "all":
1052
+ return width >= 110 ? settingsWide(input, width, useColor) : settingsCompact(input, width, useColor);
1053
+ }
1054
+ };
1055
+ export const renderApprovalInbox = (inbox, options = {}) => {
1056
+ const width = normalizeWidth(options.width ?? terminalWidth());
1057
+ const useColor = options.color ?? useAnsiColor();
1058
+ const pendingTitle = `CHECKPOINTS / APPROVAL GATES pending: ${inbox.pendingApprovals.length} auto-cleared: ${inbox.recentAutopilotApprovals.length} handoffs: ${inbox.recentHandoffs.length}`;
1059
+ const pendingLines = [
1060
+ "runtime owns approval state; commands below act through runtime gates",
1061
+ "autopilot still stops for protected test changes, secret-risk transfers, destructive/dependency/network commands",
1062
+ "",
1063
+ ...checkpointRows(inbox, width - 4, Math.max(1, inbox.pendingApprovals.length)),
1064
+ ...(inbox.pendingApprovals.some((approval) => approval.artifacts.length > 0)
1065
+ ? [
1066
+ "",
1067
+ "RECEIPTS",
1068
+ ...inbox.pendingApprovals.flatMap((approval) => approval.artifacts.map((artifact) => `${shortRunId(approval.runId)} ${artifact.kind}: ${artifact.summary}`))
1069
+ ]
1070
+ : [])
1071
+ ];
1072
+ const autopilotLines = autopilotRows(inbox, width - 4, Math.max(1, inbox.recentAutopilotApprovals.length));
1073
+ const handoffLines = handoffRows(inbox, width - 4, Math.max(1, inbox.recentHandoffs.length));
1074
+ const exactCommands = exactApprovalCommandLines(inbox);
1075
+ return [
1076
+ frame(pendingTitle, pendingLines, width, useColor, "amber"),
1077
+ "",
1078
+ frame("AUTOPILOT LEDGER", autopilotLines, width, useColor, "cyan"),
1079
+ "",
1080
+ frame("HANDOFFS", handoffLines, width, useColor, "amberDim"),
1081
+ ...(exactCommands.length > 0 ? ["", ...exactCommands] : [])
1082
+ ].join("\n");
1083
+ };
1084
+ export const renderDashboard = (input, options = {}) => {
1085
+ const width = normalizeWidth(options.width ?? terminalWidth());
1086
+ const useColor = options.color ?? useAnsiColor();
1087
+ const view = buildView(input);
1088
+ const commandCenter = width >= 110
1089
+ ? commandCenterWide(view, width, useColor)
1090
+ : commandCenterCompact(view, width, useColor);
1091
+ return [
1092
+ renderMasthead(width, useColor),
1093
+ "",
1094
+ commandCenter,
1095
+ "",
1096
+ statusRail(view, width, useColor),
1097
+ promptRail(view, width, useColor)
1098
+ ].join("\n");
1099
+ };
1100
+ export const renderSettingsCockpit = (input, options = {}) => {
1101
+ const width = normalizeWidth(options.width ?? terminalWidth());
1102
+ const useColor = options.color ?? useAnsiColor();
1103
+ const page = options.page ?? "overview";
1104
+ const settings = renderSettingsPage(input, page, width, useColor);
1105
+ const backCommand = settingsPageCommand(input, "overview");
1106
+ return [
1107
+ renderMasthead(width, useColor),
1108
+ "",
1109
+ settings,
1110
+ "",
1111
+ style(truncateEnd(`[settings page ${page}] [source .thehood/config.json] [runtime enforces] [ui displays commands]`, width), "green", useColor),
1112
+ style(truncateEnd(page === "overview" ? `run: ${settingsPageCommand(input, "crew")}` : `run: ${backCommand}`, width), "amber", useColor)
1113
+ ].join("\n");
1114
+ };
1115
+ //# sourceMappingURL=dashboard.js.map