@vextlabs/theron-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +15 -0
  2. package/LICENSE.txt +190 -0
  3. package/README.md +98 -0
  4. package/bin/theron +22 -0
  5. package/bin/theron.js +25 -0
  6. package/dist/api.d.ts +111 -0
  7. package/dist/api.js +328 -0
  8. package/dist/api.js.map +1 -0
  9. package/dist/auth.d.ts +15 -0
  10. package/dist/auth.js +92 -0
  11. package/dist/auth.js.map +1 -0
  12. package/dist/banner.d.ts +29 -0
  13. package/dist/banner.js +191 -0
  14. package/dist/banner.js.map +1 -0
  15. package/dist/cap_config.d.ts +28 -0
  16. package/dist/cap_config.js +83 -0
  17. package/dist/cap_config.js.map +1 -0
  18. package/dist/config.d.ts +18 -0
  19. package/dist/config.js +65 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/connections.d.ts +3 -0
  22. package/dist/connections.js +105 -0
  23. package/dist/connections.js.map +1 -0
  24. package/dist/import_claude.d.ts +3 -0
  25. package/dist/import_claude.js +268 -0
  26. package/dist/import_claude.js.map +1 -0
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +237 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/onboard.d.ts +3 -0
  31. package/dist/onboard.js +234 -0
  32. package/dist/onboard.js.map +1 -0
  33. package/dist/profile_match.d.ts +15 -0
  34. package/dist/profile_match.js +107 -0
  35. package/dist/profile_match.js.map +1 -0
  36. package/dist/profiles/index.d.ts +20 -0
  37. package/dist/profiles/index.js +56 -0
  38. package/dist/profiles/index.js.map +1 -0
  39. package/dist/profiles/seeds.d.ts +4 -0
  40. package/dist/profiles/seeds.js +500 -0
  41. package/dist/profiles/seeds.js.map +1 -0
  42. package/dist/profiles/types.d.ts +35 -0
  43. package/dist/profiles/types.js +18 -0
  44. package/dist/profiles/types.js.map +1 -0
  45. package/dist/render.d.ts +18 -0
  46. package/dist/render.js +82 -0
  47. package/dist/render.js.map +1 -0
  48. package/dist/repl.d.ts +13 -0
  49. package/dist/repl.js +821 -0
  50. package/dist/repl.js.map +1 -0
  51. package/dist/streaming.d.ts +28 -0
  52. package/dist/streaming.js +118 -0
  53. package/dist/streaming.js.map +1 -0
  54. package/dist/tools/bash.d.ts +8 -0
  55. package/dist/tools/bash.js +57 -0
  56. package/dist/tools/bash.js.map +1 -0
  57. package/dist/tools/edit.d.ts +9 -0
  58. package/dist/tools/edit.js +42 -0
  59. package/dist/tools/edit.js.map +1 -0
  60. package/dist/tools/glob.d.ts +7 -0
  61. package/dist/tools/glob.js +40 -0
  62. package/dist/tools/glob.js.map +1 -0
  63. package/dist/tools/grep.d.ts +9 -0
  64. package/dist/tools/grep.js +73 -0
  65. package/dist/tools/grep.js.map +1 -0
  66. package/dist/tools/index.d.ts +31 -0
  67. package/dist/tools/index.js +180 -0
  68. package/dist/tools/index.js.map +1 -0
  69. package/dist/tools/ls.d.ts +6 -0
  70. package/dist/tools/ls.js +25 -0
  71. package/dist/tools/ls.js.map +1 -0
  72. package/dist/tools/read.d.ts +8 -0
  73. package/dist/tools/read.js +43 -0
  74. package/dist/tools/read.js.map +1 -0
  75. package/dist/tools/stoa.d.ts +34 -0
  76. package/dist/tools/stoa.js +103 -0
  77. package/dist/tools/stoa.js.map +1 -0
  78. package/dist/tools/write.d.ts +7 -0
  79. package/dist/tools/write.js +15 -0
  80. package/dist/tools/write.js.map +1 -0
  81. package/dist/verifiers/ai_ism_check.d.ts +2 -0
  82. package/dist/verifiers/ai_ism_check.js +48 -0
  83. package/dist/verifiers/ai_ism_check.js.map +1 -0
  84. package/dist/verifiers/arithmetic_recheck.d.ts +2 -0
  85. package/dist/verifiers/arithmetic_recheck.js +74 -0
  86. package/dist/verifiers/arithmetic_recheck.js.map +1 -0
  87. package/dist/verifiers/citation_presence.d.ts +2 -0
  88. package/dist/verifiers/citation_presence.js +42 -0
  89. package/dist/verifiers/citation_presence.js.map +1 -0
  90. package/dist/verifiers/em_dash_check.d.ts +2 -0
  91. package/dist/verifiers/em_dash_check.js +23 -0
  92. package/dist/verifiers/em_dash_check.js.map +1 -0
  93. package/dist/verifiers/index.d.ts +16 -0
  94. package/dist/verifiers/index.js +123 -0
  95. package/dist/verifiers/index.js.map +1 -0
  96. package/dist/verifiers/lint.d.ts +2 -0
  97. package/dist/verifiers/lint.js +90 -0
  98. package/dist/verifiers/lint.js.map +1 -0
  99. package/dist/verifiers/style_lint.d.ts +2 -0
  100. package/dist/verifiers/style_lint.js +115 -0
  101. package/dist/verifiers/style_lint.js.map +1 -0
  102. package/dist/verifiers/test_smoke.d.ts +2 -0
  103. package/dist/verifiers/test_smoke.js +94 -0
  104. package/dist/verifiers/test_smoke.js.map +1 -0
  105. package/dist/verifiers/typecheck.d.ts +2 -0
  106. package/dist/verifiers/typecheck.js +98 -0
  107. package/dist/verifiers/typecheck.js.map +1 -0
  108. package/dist/verifiers/types.d.ts +33 -0
  109. package/dist/verifiers/types.js +23 -0
  110. package/dist/verifiers/types.js.map +1 -0
  111. package/package.json +60 -0
@@ -0,0 +1,234 @@
1
+ // `theron onboard` — one-command migration wizard.
2
+ //
3
+ // Anna 2026-05-10: "Setup a full ready-to-come-over-to-Vext-Labs
4
+ // integration walk-through. SUPER EASY — painless. Even me — going
5
+ // from using Claude all day to only using Theron."
6
+ //
7
+ // The wizard handles every step a new-to-Theron user would otherwise
8
+ // have to remember. Each step is opt-in (asks first), so re-running
9
+ // the wizard is safe — it skips steps the user already finished.
10
+ //
11
+ // Steps (in order):
12
+ // 1. Sign in (or confirm existing credentials).
13
+ // 2. Pick top 3 specialists to pin (based on stated work focus).
14
+ // 3. Install the `cl` shell alias for muscle memory.
15
+ // 4. Connect Microsoft 365 OAuth.
16
+ // 5. Connect Google Workspace OAuth.
17
+ // 6. Install the Office add-in (sideload manifest).
18
+ // 7. Offer to import a Claude conversation export.
19
+ import readline from "node:readline";
20
+ import path from "node:path";
21
+ import fs from "node:fs";
22
+ import os from "node:os";
23
+ import { spawnSync } from "node:child_process";
24
+ import chalk from "chalk";
25
+ import { listProfiles } from "./profiles/index.js";
26
+ import { loadCredentials } from "./auth.js";
27
+ const AMBER = "#FFAE00";
28
+ const HOME = os.homedir();
29
+ const SHELL_RC = path.join(HOME, ".zshrc");
30
+ const BASH_RC = path.join(HOME, ".bashrc");
31
+ export async function onboardCommand(opts = {}) {
32
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
33
+ const ask = (q) => new Promise((res) => rl.question(q, res));
34
+ const yes = async (q, def = 'y') => {
35
+ const a = (await ask(`${q} ${def === 'y' ? '[Y/n]' : '[y/N]'} `)).trim().toLowerCase();
36
+ if (!a)
37
+ return def === 'y';
38
+ return a === 'y' || a === 'yes';
39
+ };
40
+ banner();
41
+ // ── Step 1 — Sign in ────────────────────────────────────────────
42
+ step(1, "Sign in");
43
+ const creds = await loadCredentials();
44
+ if (creds.api_key) {
45
+ info(`Already signed in. API key in ~/.theron/credentials.`);
46
+ }
47
+ else {
48
+ info("You're not signed in yet.");
49
+ if (await yes("Run `theron login` now?", 'y')) {
50
+ const r = spawnSync(process.execPath, [process.argv[1], "login"], { stdio: 'inherit' });
51
+ if (r.status !== 0) {
52
+ warn("Login was canceled or failed — anonymous mode works too, just with lower rate limits.");
53
+ }
54
+ }
55
+ }
56
+ // ── Step 2 — Pick top 3 specialists ────────────────────────────
57
+ step(2, "Pick your top 3 specialists");
58
+ info("Theron has 33 specialists in the chat picker. We'll pin 3 to the top of yours based on what kind of work you do most.");
59
+ const focusInput = await ask(`What kind of work fills your days? (e.g. "code, design, writing"): `);
60
+ const focus = focusInput.trim().toLowerCase();
61
+ const picked = pickSpecialists(focus, listProfiles());
62
+ if (picked.length > 0) {
63
+ info(`Pinning: ${picked.map((p) => `@${p.slug}`).join(", ")}`);
64
+ savePinnedSpecialists(picked.map((p) => p.slug));
65
+ }
66
+ else {
67
+ info("No specific focus — leaving your picker default.");
68
+ }
69
+ // ── Step 3 — `cl` shell alias ──────────────────────────────────
70
+ step(3, "Install the `cl` shell alias (same muscle memory)");
71
+ info("Type `cl <thing>` instead of `theron <thing>` — shorter to type, same effect.");
72
+ if (await yes("Add `alias cl=theron` to your shell config?", 'y')) {
73
+ installShellAlias();
74
+ }
75
+ else {
76
+ info("Skipped. Add it later with: echo 'alias cl=theron' >> ~/.zshrc");
77
+ }
78
+ // ── Step 4 — Microsoft 365 OAuth ──────────────────────────────
79
+ step(4, "Connect Microsoft 365");
80
+ info("Connecting MS unlocks the ms365.* Stoa caps — Excel, PowerPoint, Word, Outlook — auto-token-injected at every cap call.");
81
+ if (await yes("Open the Microsoft consent flow in your browser?", 'y')) {
82
+ const apiUrl = opts.apiUrl ?? "https://tryvext.com";
83
+ const url = `${apiUrl}/api/oauth/microsoft/start${creds.api_key ? `?apikey=${encodeURIComponent(creds.api_key)}` : ''}`;
84
+ openInBrowser(url);
85
+ info(`Opened: ${url}`);
86
+ info("After consent, the token persists server-side. No client-side state change needed.");
87
+ }
88
+ else {
89
+ info("Skipped. Run `theron onboard` again later to retry, or visit /api/oauth/microsoft/start.");
90
+ }
91
+ // ── Step 5 — Google Workspace OAuth ────────────────────────────
92
+ step(5, "Connect Google Workspace");
93
+ info("Connecting Google unlocks the gws.* Stoa caps — Sheets, Docs, Slides, Gmail, Calendar, Drive.");
94
+ if (await yes("Open the Google consent flow?", 'y')) {
95
+ const apiUrl = opts.apiUrl ?? "https://tryvext.com";
96
+ const url = `${apiUrl}/api/oauth/google/start${creds.api_key ? `?apikey=${encodeURIComponent(creds.api_key)}` : ''}`;
97
+ openInBrowser(url);
98
+ info(`Opened: ${url}`);
99
+ }
100
+ else {
101
+ info("Skipped.");
102
+ }
103
+ // ── Step 6 — Office add-in ────────────────────────────────────
104
+ step(6, "Install the Office 365 add-in");
105
+ info("Sideloads the Theron task pane into Excel + PowerPoint + Word. Microsoft Add-in Validator: GREEN.");
106
+ if (await yes("Open the sideload manifest now?", 'y')) {
107
+ openInBrowser("https://tryvext.com/office-addin/manifest.xml");
108
+ info("Sideload steps: https://learn.microsoft.com/office/dev/add-ins/testing/sideload-office-add-ins-for-testing");
109
+ info("Once sideloaded, look for the 'Open Theron' button in the ribbon.");
110
+ }
111
+ else {
112
+ info("Skipped.");
113
+ }
114
+ // ── Step 7 — Claude export import ─────────────────────────────
115
+ step(7, "Import your Claude history (optional)");
116
+ info("Drop a Claude data-export zip and Theron extracts your preferences (voice, project context, recurring decisions) and seeds long-term memory.");
117
+ info("Get the zip at: https://claude.ai → Settings → Privacy → Download Data → email arrives with a zip.");
118
+ const importPath = (await ask("Path to data-export.zip (or blank to skip): ")).trim();
119
+ if (importPath) {
120
+ const r = spawnSync(process.execPath, [process.argv[1], "import-claude", importPath], { stdio: 'inherit' });
121
+ if (r.status !== 0) {
122
+ warn("Claude import failed or was canceled.");
123
+ }
124
+ }
125
+ else {
126
+ info("Skipped. Run later with: theron import-claude <zip>");
127
+ }
128
+ // ── Done ──────────────────────────────────────────────────────
129
+ rl.close();
130
+ console.log();
131
+ console.log(chalk.hex(AMBER).bold("✓ You're on Theron."));
132
+ console.log();
133
+ console.log(" Try it now:");
134
+ console.log(" " + chalk.hex(AMBER)("cl") + " 'review the last PR I opened'");
135
+ console.log(" " + chalk.hex(AMBER)("cl") + " --mode legal 'draft an NDA between us and Acme'");
136
+ console.log(" " + chalk.hex(AMBER)("cl") + " --mode finance 'build a 3-year ARR model at 15% YoY'");
137
+ console.log();
138
+ console.log(" Surface picker: https://theron.tryvext.com");
139
+ console.log(" Voice mode: https://tryvext.com/voice");
140
+ console.log(" Office add-in: already sideloaded — look for the ribbon button");
141
+ console.log(" /admin/stoa/pending — review caps the Hive agents propose (staff)");
142
+ console.log();
143
+ return 0;
144
+ }
145
+ // ── helpers ────────────────────────────────────────────────────────
146
+ function banner() {
147
+ console.log();
148
+ console.log(chalk.hex(AMBER).bold(" Theron · onboarding wizard"));
149
+ console.log(chalk.dim(" Same muscle memory. Better loop. ~8 minutes."));
150
+ console.log();
151
+ }
152
+ function step(n, title) {
153
+ console.log();
154
+ console.log(chalk.dim(`─── Step ${n}/7 ───────────────────────────────────────────────`));
155
+ console.log(chalk.bold(title));
156
+ }
157
+ function info(s) {
158
+ console.log(" " + s);
159
+ }
160
+ function warn(s) {
161
+ console.log(" " + chalk.yellow(s));
162
+ }
163
+ // Pin top 3 specialists based on a free-text "what kind of work" answer.
164
+ // Simple keyword match against profile slug + label + description; takes
165
+ // the top 3 by score. Beats asking the user to pick from 33 by name.
166
+ function pickSpecialists(focus, all) {
167
+ if (!focus)
168
+ return [];
169
+ const tokens = focus.split(/[\s,;]+/).map((t) => t.trim().toLowerCase()).filter(Boolean);
170
+ if (tokens.length === 0)
171
+ return [];
172
+ // Score: each token-in-slug = 5; in-label = 3; in-description = 1.
173
+ const scored = all.map((p) => {
174
+ let score = 0;
175
+ for (const t of tokens) {
176
+ if (p.slug.includes(t))
177
+ score += 5;
178
+ if (p.label.toLowerCase().includes(t))
179
+ score += 3;
180
+ if (p.description.toLowerCase().includes(t))
181
+ score += 1;
182
+ }
183
+ return { profile: p, score };
184
+ });
185
+ scored.sort((a, b) => b.score - a.score);
186
+ return scored.filter((s) => s.score > 0).slice(0, 3).map((s) => s.profile);
187
+ }
188
+ // Persist pinned specialists to ~/.theron/pinned.json — the REPL reads
189
+ // this on launch and seeds the session pin list.
190
+ function savePinnedSpecialists(slugs) {
191
+ const dir = path.join(HOME, ".theron");
192
+ try {
193
+ if (!fs.existsSync(dir))
194
+ fs.mkdirSync(dir, { mode: 0o700 });
195
+ fs.writeFileSync(path.join(dir, "pinned.json"), JSON.stringify({ pinned: slugs }, null, 2), { mode: 0o600 });
196
+ info(`Saved to ~/.theron/pinned.json`);
197
+ }
198
+ catch (err) {
199
+ warn(`Couldn't save pinned specialists: ${err instanceof Error ? err.message : String(err)}`);
200
+ }
201
+ }
202
+ // Add `alias cl=theron` to ~/.zshrc (or ~/.bashrc fallback). Idempotent
203
+ // — won't duplicate the line on re-runs.
204
+ function installShellAlias() {
205
+ const target = fs.existsSync(SHELL_RC) ? SHELL_RC : BASH_RC;
206
+ const line = "alias cl='theron' # Theron — same muscle memory";
207
+ try {
208
+ const existing = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : "";
209
+ if (existing.includes("alias cl=") || existing.includes("alias cl ")) {
210
+ info(`\`cl\` alias already in ${target} — skipping.`);
211
+ return;
212
+ }
213
+ fs.appendFileSync(target, `\n# Added by theron onboard\n${line}\n`);
214
+ info(`Added \`alias cl=theron\` to ${target}.`);
215
+ info(`Reload your shell or run: source ${target}`);
216
+ }
217
+ catch (err) {
218
+ warn(`Couldn't write to ${target}: ${err instanceof Error ? err.message : String(err)}`);
219
+ info("Manual install: echo \"alias cl='theron'\" >> ~/.zshrc");
220
+ }
221
+ }
222
+ function openInBrowser(url) {
223
+ // macOS / Linux / Windows shell open.
224
+ const opener = process.platform === "darwin" ? "open" :
225
+ process.platform === "win32" ? "start" :
226
+ "xdg-open";
227
+ try {
228
+ spawnSync(opener, [url], { stdio: 'ignore' });
229
+ }
230
+ catch {
231
+ info(`Open this in your browser: ${url}`);
232
+ }
233
+ }
234
+ //# sourceMappingURL=onboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboard.js","sourceRoot":"","sources":["../src/onboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type TheronProfile } from "./profiles/index.js";
2
+ export interface ProfileMatch {
3
+ profile: TheronProfile;
4
+ score: number;
5
+ }
6
+ /**
7
+ * Score every profile against the user's prompt; return the top-N.
8
+ * The returned scores are NOT cosine-normalized — they're absolute
9
+ * TF-IDF sums. Useful for thresholding ("only suggest a switch if
10
+ * score > 0.05") and for the UI to show relative magnitudes.
11
+ *
12
+ * Empty / very-short prompts return [] — we don't want to suggest a
13
+ * profile switch on "hi".
14
+ */
15
+ export declare function rankProfilesForPrompt(prompt: string, topN?: number): ProfileMatch[];
@@ -0,0 +1,107 @@
1
+ // profile_match — pick the top-N most relevant profiles for a given prompt.
2
+ //
3
+ // Pattern lifted from jcode (MIT): instead of pre-loading all 33
4
+ // profiles as system-prompt context every turn (token-heavy + noisy),
5
+ // rank them by similarity to the user's actual prompt and inject only
6
+ // the top-3. The profile registry stays small enough on disk that
7
+ // we don't need a real embedding model — a TF-IDF over the title +
8
+ // description + welcome blurb is enough signal to route correctly.
9
+ //
10
+ // When the user explicitly typed `/mode <slug>` they've made a
11
+ // deterministic choice; this matcher is bypassed. It only fires when
12
+ // the user is on the default profile and we want to softly suggest
13
+ // "Theron-Legal would handle this better — try /mode legal".
14
+ import { listProfiles } from "./profiles/index.js";
15
+ // Cheap stopword filter — same shape jcode uses. Keeps domain terms
16
+ // (api, contract, deck) but drops "the / and / what / how".
17
+ const STOPWORDS = new Set([
18
+ "the", "a", "an", "and", "or", "but", "for", "of", "to", "in", "on",
19
+ "at", "by", "with", "is", "are", "was", "were", "be", "been", "being",
20
+ "have", "has", "had", "do", "does", "did", "will", "would", "should",
21
+ "could", "may", "might", "must", "can", "as", "from", "if", "this",
22
+ "that", "these", "those", "i", "you", "we", "they", "he", "she", "it",
23
+ "my", "your", "our", "their", "his", "her", "its", "what", "which",
24
+ "who", "whom", "whose", "where", "when", "why", "how", "not", "no",
25
+ "so", "than", "then", "into", "about", "up", "down", "out", "over",
26
+ "under", "again", "more", "most", "some", "such", "any", "all", "each",
27
+ "every", "both", "few", "many", "much", "other", "another", "either",
28
+ "neither", "one", "two", "make", "made", "get", "got", "go", "going",
29
+ "want", "need", "like", "just", "really", "very", "even",
30
+ ]);
31
+ function tokenize(text) {
32
+ return text
33
+ .toLowerCase()
34
+ .replace(/[^a-z0-9\s_\-]/g, " ")
35
+ .split(/\s+/)
36
+ .filter((t) => t.length >= 3 && !STOPWORDS.has(t));
37
+ }
38
+ function vectorize(profile) {
39
+ // The descriptor we score against is title + description + welcome +
40
+ // any prompt-starters. These together capture what the profile is
41
+ // good at without leaking implementation details.
42
+ const corpus = [
43
+ profile.label,
44
+ profile.description,
45
+ profile.welcome ?? "",
46
+ ...(profile.promptStarters ?? []),
47
+ ].join(" ");
48
+ const tokens = tokenize(corpus);
49
+ const terms = new Map();
50
+ for (const t of tokens)
51
+ terms.set(t, (terms.get(t) ?? 0) + 1);
52
+ return { slug: profile.slug, terms, length: tokens.length };
53
+ }
54
+ // Inverse-document-frequency for the corpus of profiles. Computed once
55
+ // per call — 33 profiles fit easily in memory.
56
+ function computeIdf(docs) {
57
+ const N = docs.length;
58
+ const df = new Map();
59
+ for (const d of docs) {
60
+ for (const term of d.terms.keys()) {
61
+ df.set(term, (df.get(term) ?? 0) + 1);
62
+ }
63
+ }
64
+ const idf = new Map();
65
+ for (const [term, freq] of df.entries()) {
66
+ idf.set(term, Math.log(1 + N / freq));
67
+ }
68
+ return idf;
69
+ }
70
+ function tfIdfScore(promptTokens, doc, idf) {
71
+ let score = 0;
72
+ if (doc.length === 0)
73
+ return 0;
74
+ for (const t of promptTokens) {
75
+ const tf = (doc.terms.get(t) ?? 0) / doc.length;
76
+ const i = idf.get(t) ?? 0;
77
+ score += tf * i;
78
+ }
79
+ return score;
80
+ }
81
+ /**
82
+ * Score every profile against the user's prompt; return the top-N.
83
+ * The returned scores are NOT cosine-normalized — they're absolute
84
+ * TF-IDF sums. Useful for thresholding ("only suggest a switch if
85
+ * score > 0.05") and for the UI to show relative magnitudes.
86
+ *
87
+ * Empty / very-short prompts return [] — we don't want to suggest a
88
+ * profile switch on "hi".
89
+ */
90
+ export function rankProfilesForPrompt(prompt, topN = 3) {
91
+ const t = prompt.trim();
92
+ if (t.length < 12)
93
+ return [];
94
+ const promptTokens = tokenize(t);
95
+ if (promptTokens.length === 0)
96
+ return [];
97
+ const profiles = listProfiles();
98
+ const docs = profiles.map(vectorize);
99
+ const idf = computeIdf(docs);
100
+ const scored = profiles.map((p, i) => ({
101
+ profile: p,
102
+ score: tfIdfScore(promptTokens, docs[i], idf),
103
+ }));
104
+ scored.sort((a, b) => b.score - a.score);
105
+ return scored.filter((m) => m.score > 0).slice(0, topN);
106
+ }
107
+ //# sourceMappingURL=profile_match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile_match.js","sourceRoot":"","sources":["../src/profile_match.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { TheronProfile } from "./types.js";
2
+ import { DEFAULT_PROFILE_SLUG } from "./seeds.js";
3
+ /** Look up a profile by slug. Case-insensitive. Returns null if missing. */
4
+ export declare function getProfile(slug: string | undefined | null): TheronProfile | null;
5
+ /** Look up a profile, falling back to the default code profile. Used by
6
+ * the REPL when the user hasn't named one. */
7
+ export declare function getProfileOrDefault(slug: string | undefined | null): TheronProfile;
8
+ /** List all profiles in registration order. Used by /mode list and the
9
+ * help screen. */
10
+ export declare function listProfiles(): TheronProfile[];
11
+ /** Overlay a server-provided roster onto the local seed registry. Each
12
+ * remote entry merges over the local one by slug. Called once at REPL
13
+ * startup; the registry locks after the first overlay so a malicious
14
+ * server can't rewrite the registry mid-session (e.g. via a tool
15
+ * message that smuggles in a profile-update frame). */
16
+ export declare function overlayRemoteProfiles(remote: TheronProfile[]): void;
17
+ /** Convenience: profile slug → label, for compact log lines. */
18
+ export declare function profileLabel(slug: string | undefined | null): string;
19
+ export { DEFAULT_PROFILE_SLUG };
20
+ export type { TheronProfile };
@@ -0,0 +1,56 @@
1
+ // Theron CLI profile registry.
2
+ //
3
+ // The CLI ships with a local baseline (SEED_PROFILES, 33 specs) and
4
+ // can optionally overlay a server-fetched roster at launch. The server
5
+ // always wins if both define the same slug — that lets us hot-update
6
+ // the prompts and verifiers without a CLI reinstall.
7
+ import { SEED_PROFILES, DEFAULT_PROFILE_SLUG } from "./seeds.js";
8
+ const REGISTRY = new Map();
9
+ for (const p of SEED_PROFILES)
10
+ REGISTRY.set(p.slug, p);
11
+ let registryLocked = false;
12
+ /** Look up a profile by slug. Case-insensitive. Returns null if missing. */
13
+ export function getProfile(slug) {
14
+ if (!slug)
15
+ return null;
16
+ return REGISTRY.get(slug.toLowerCase()) ?? null;
17
+ }
18
+ /** Look up a profile, falling back to the default code profile. Used by
19
+ * the REPL when the user hasn't named one. */
20
+ export function getProfileOrDefault(slug) {
21
+ return getProfile(slug) ?? REGISTRY.get(DEFAULT_PROFILE_SLUG);
22
+ }
23
+ /** List all profiles in registration order. Used by /mode list and the
24
+ * help screen. */
25
+ export function listProfiles() {
26
+ return Array.from(REGISTRY.values());
27
+ }
28
+ /** Overlay a server-provided roster onto the local seed registry. Each
29
+ * remote entry merges over the local one by slug. Called once at REPL
30
+ * startup; the registry locks after the first overlay so a malicious
31
+ * server can't rewrite the registry mid-session (e.g. via a tool
32
+ * message that smuggles in a profile-update frame). */
33
+ export function overlayRemoteProfiles(remote) {
34
+ if (registryLocked)
35
+ return;
36
+ for (const p of remote) {
37
+ if (!p?.slug)
38
+ continue;
39
+ // Shallow merge so remote can override any field while keeping the
40
+ // local baseline as a fallback for anything they omit.
41
+ const local = REGISTRY.get(p.slug.toLowerCase());
42
+ const merged = {
43
+ ...(local ?? p),
44
+ ...p,
45
+ slug: p.slug.toLowerCase(),
46
+ };
47
+ REGISTRY.set(merged.slug, merged);
48
+ }
49
+ registryLocked = true;
50
+ }
51
+ /** Convenience: profile slug → label, for compact log lines. */
52
+ export function profileLabel(slug) {
53
+ return getProfile(slug)?.label ?? "General";
54
+ }
55
+ export { DEFAULT_PROFILE_SLUG };
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/profiles/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,qEAAqE;AACrE,qDAAqD;AAGrD,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;AAClD,KAAK,MAAM,CAAC,IAAI,aAAa;IAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAEvD,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CAAC,IAA+B;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;AAClD,CAAC;AAED;+CAC+C;AAC/C,MAAM,UAAU,mBAAmB,CAAC,IAA+B;IACjE,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,oBAAoB,CAAE,CAAC;AACjE,CAAC;AAED;mBACmB;AACnB,MAAM,UAAU,YAAY;IAC1B,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;wDAIwD;AACxD,MAAM,UAAU,qBAAqB,CAAC,MAAuB;IAC3D,IAAI,cAAc;QAAE,OAAO;IAC3B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,EAAE,IAAI;YAAE,SAAS;QACvB,mEAAmE;QACnE,uDAAuD;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACjD,MAAM,MAAM,GAAkB;YAC5B,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC;YACf,GAAG,CAAC;YACJ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE;SAC3B,CAAC;QACF,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IACD,cAAc,GAAG,IAAI,CAAC;AACxB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,IAA+B;IAC1D,OAAO,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,SAAS,CAAC;AAC9C,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { TheronProfile } from "./types.js";
2
+ export declare const SEED_PROFILES: TheronProfile[];
3
+ /** Default profile slug used when the user hasn't picked one. */
4
+ export declare const DEFAULT_PROFILE_SLUG = "code";