openclaw-castroom 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 (162) hide show
  1. package/README.md +96 -0
  2. package/dist/commands/add.js +92 -0
  3. package/dist/commands/add.js.map +1 -0
  4. package/dist/commands/populate.js +80 -0
  5. package/dist/commands/populate.js.map +1 -0
  6. package/dist/index.js +97 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/openclaw/runner.js +84 -0
  9. package/dist/openclaw/runner.js.map +1 -0
  10. package/dist/openclaw/workspace.js +186 -0
  11. package/dist/openclaw/workspace.js.map +1 -0
  12. package/dist/personas/index.js +44 -0
  13. package/dist/personas/index.js.map +1 -0
  14. package/dist/personas/packs/office/andy.js +50 -0
  15. package/dist/personas/packs/office/andy.js.map +1 -0
  16. package/dist/personas/packs/office/angela.js +50 -0
  17. package/dist/personas/packs/office/angela.js.map +1 -0
  18. package/dist/personas/packs/office/carol.js +49 -0
  19. package/dist/personas/packs/office/carol.js.map +1 -0
  20. package/dist/personas/packs/office/creed.js +49 -0
  21. package/dist/personas/packs/office/creed.js.map +1 -0
  22. package/dist/personas/packs/office/darryl.js +50 -0
  23. package/dist/personas/packs/office/darryl.js.map +1 -0
  24. package/dist/personas/packs/office/david-wallace.js +49 -0
  25. package/dist/personas/packs/office/david-wallace.js.map +1 -0
  26. package/dist/personas/packs/office/dwight.js +52 -0
  27. package/dist/personas/packs/office/dwight.js.map +1 -0
  28. package/dist/personas/packs/office/erin.js +49 -0
  29. package/dist/personas/packs/office/erin.js.map +1 -0
  30. package/dist/personas/packs/office/gabe.js +50 -0
  31. package/dist/personas/packs/office/gabe.js.map +1 -0
  32. package/dist/personas/packs/office/index.js +54 -0
  33. package/dist/personas/packs/office/index.js.map +1 -0
  34. package/dist/personas/packs/office/jan.js +49 -0
  35. package/dist/personas/packs/office/jan.js.map +1 -0
  36. package/dist/personas/packs/office/jim.js +50 -0
  37. package/dist/personas/packs/office/jim.js.map +1 -0
  38. package/dist/personas/packs/office/karen.js +48 -0
  39. package/dist/personas/packs/office/karen.js.map +1 -0
  40. package/dist/personas/packs/office/kelly.js +49 -0
  41. package/dist/personas/packs/office/kelly.js.map +1 -0
  42. package/dist/personas/packs/office/kevin.js +49 -0
  43. package/dist/personas/packs/office/kevin.js.map +1 -0
  44. package/dist/personas/packs/office/meredith.js +48 -0
  45. package/dist/personas/packs/office/meredith.js.map +1 -0
  46. package/dist/personas/packs/office/michael.js +50 -0
  47. package/dist/personas/packs/office/michael.js.map +1 -0
  48. package/dist/personas/packs/office/oscar.js +49 -0
  49. package/dist/personas/packs/office/oscar.js.map +1 -0
  50. package/dist/personas/packs/office/pam.js +50 -0
  51. package/dist/personas/packs/office/pam.js.map +1 -0
  52. package/dist/personas/packs/office/phyllis.js +48 -0
  53. package/dist/personas/packs/office/phyllis.js.map +1 -0
  54. package/dist/personas/packs/office/roy.js +48 -0
  55. package/dist/personas/packs/office/roy.js.map +1 -0
  56. package/dist/personas/packs/office/ryan.js +48 -0
  57. package/dist/personas/packs/office/ryan.js.map +1 -0
  58. package/dist/personas/packs/office/stanley.js +49 -0
  59. package/dist/personas/packs/office/stanley.js.map +1 -0
  60. package/dist/personas/packs/office/toby.js +48 -0
  61. package/dist/personas/packs/office/toby.js.map +1 -0
  62. package/dist/personas/packs/trailer-park-boys/barbara.js +45 -0
  63. package/dist/personas/packs/trailer-park-boys/barbara.js.map +1 -0
  64. package/dist/personas/packs/trailer-park-boys/bubbles.js +49 -0
  65. package/dist/personas/packs/trailer-park-boys/bubbles.js.map +1 -0
  66. package/dist/personas/packs/trailer-park-boys/cory.js +45 -0
  67. package/dist/personas/packs/trailer-park-boys/cory.js.map +1 -0
  68. package/dist/personas/packs/trailer-park-boys/cyrus.js +45 -0
  69. package/dist/personas/packs/trailer-park-boys/cyrus.js.map +1 -0
  70. package/dist/personas/packs/trailer-park-boys/george.js +45 -0
  71. package/dist/personas/packs/trailer-park-boys/george.js.map +1 -0
  72. package/dist/personas/packs/trailer-park-boys/index.js +42 -0
  73. package/dist/personas/packs/trailer-park-boys/index.js.map +1 -0
  74. package/dist/personas/packs/trailer-park-boys/jroc.js +45 -0
  75. package/dist/personas/packs/trailer-park-boys/jroc.js.map +1 -0
  76. package/dist/personas/packs/trailer-park-boys/julian.js +49 -0
  77. package/dist/personas/packs/trailer-park-boys/julian.js.map +1 -0
  78. package/dist/personas/packs/trailer-park-boys/lahey.js +46 -0
  79. package/dist/personas/packs/trailer-park-boys/lahey.js.map +1 -0
  80. package/dist/personas/packs/trailer-park-boys/lucy.js +46 -0
  81. package/dist/personas/packs/trailer-park-boys/lucy.js.map +1 -0
  82. package/dist/personas/packs/trailer-park-boys/randy.js +45 -0
  83. package/dist/personas/packs/trailer-park-boys/randy.js.map +1 -0
  84. package/dist/personas/packs/trailer-park-boys/ray.js +45 -0
  85. package/dist/personas/packs/trailer-park-boys/ray.js.map +1 -0
  86. package/dist/personas/packs/trailer-park-boys/ricky.js +49 -0
  87. package/dist/personas/packs/trailer-park-boys/ricky.js.map +1 -0
  88. package/dist/personas/packs/trailer-park-boys/sam.js +45 -0
  89. package/dist/personas/packs/trailer-park-boys/sam.js.map +1 -0
  90. package/dist/personas/packs/trailer-park-boys/sarah.js +45 -0
  91. package/dist/personas/packs/trailer-park-boys/sarah.js.map +1 -0
  92. package/dist/personas/packs/trailer-park-boys/trevor.js +45 -0
  93. package/dist/personas/packs/trailer-park-boys/trevor.js.map +1 -0
  94. package/dist/personas/packs/trailer-park-boys/trinity.js +45 -0
  95. package/dist/personas/packs/trailer-park-boys/trinity.js.map +1 -0
  96. package/dist/personas/packs/trailer-park-boys/tyrone.js +45 -0
  97. package/dist/personas/packs/trailer-park-boys/tyrone.js.map +1 -0
  98. package/dist/personas/types.js +2 -0
  99. package/dist/personas/types.js.map +1 -0
  100. package/dist/templates/base.js +109 -0
  101. package/dist/templates/base.js.map +1 -0
  102. package/dist/templates/common.js +79 -0
  103. package/dist/templates/common.js.map +1 -0
  104. package/dist/ui/spinner.js +40 -0
  105. package/dist/ui/spinner.js.map +1 -0
  106. package/package.json +55 -0
  107. package/src/commands/add.ts +145 -0
  108. package/src/commands/populate.ts +109 -0
  109. package/src/index.ts +112 -0
  110. package/src/openclaw/runner.ts +121 -0
  111. package/src/openclaw/workspace.ts +248 -0
  112. package/src/personas/index.ts +59 -0
  113. package/src/personas/packs/office/andy.ts +51 -0
  114. package/src/personas/packs/office/angela.ts +51 -0
  115. package/src/personas/packs/office/carol.ts +50 -0
  116. package/src/personas/packs/office/creed.ts +50 -0
  117. package/src/personas/packs/office/darryl.ts +51 -0
  118. package/src/personas/packs/office/david-wallace.ts +50 -0
  119. package/src/personas/packs/office/dwight.ts +53 -0
  120. package/src/personas/packs/office/erin.ts +50 -0
  121. package/src/personas/packs/office/gabe.ts +51 -0
  122. package/src/personas/packs/office/index.ts +56 -0
  123. package/src/personas/packs/office/jan.ts +50 -0
  124. package/src/personas/packs/office/jim.ts +51 -0
  125. package/src/personas/packs/office/karen.ts +49 -0
  126. package/src/personas/packs/office/kelly.ts +50 -0
  127. package/src/personas/packs/office/kevin.ts +50 -0
  128. package/src/personas/packs/office/meredith.ts +49 -0
  129. package/src/personas/packs/office/michael.ts +51 -0
  130. package/src/personas/packs/office/oscar.ts +50 -0
  131. package/src/personas/packs/office/pam.ts +51 -0
  132. package/src/personas/packs/office/phyllis.ts +49 -0
  133. package/src/personas/packs/office/roy.ts +49 -0
  134. package/src/personas/packs/office/ryan.ts +49 -0
  135. package/src/personas/packs/office/stanley.ts +50 -0
  136. package/src/personas/packs/office/toby.ts +49 -0
  137. package/src/personas/packs/trailer-park-boys/barbara.ts +47 -0
  138. package/src/personas/packs/trailer-park-boys/bubbles.ts +50 -0
  139. package/src/personas/packs/trailer-park-boys/cory.ts +47 -0
  140. package/src/personas/packs/trailer-park-boys/cyrus.ts +47 -0
  141. package/src/personas/packs/trailer-park-boys/george.ts +47 -0
  142. package/src/personas/packs/trailer-park-boys/index.ts +44 -0
  143. package/src/personas/packs/trailer-park-boys/jroc.ts +47 -0
  144. package/src/personas/packs/trailer-park-boys/julian.ts +50 -0
  145. package/src/personas/packs/trailer-park-boys/lahey.ts +48 -0
  146. package/src/personas/packs/trailer-park-boys/lucy.ts +48 -0
  147. package/src/personas/packs/trailer-park-boys/randy.ts +47 -0
  148. package/src/personas/packs/trailer-park-boys/ray.ts +47 -0
  149. package/src/personas/packs/trailer-park-boys/ricky.ts +50 -0
  150. package/src/personas/packs/trailer-park-boys/sam.ts +47 -0
  151. package/src/personas/packs/trailer-park-boys/sarah.ts +47 -0
  152. package/src/personas/packs/trailer-park-boys/trevor.ts +47 -0
  153. package/src/personas/packs/trailer-park-boys/trinity.ts +47 -0
  154. package/src/personas/packs/trailer-park-boys/tyrone.ts +47 -0
  155. package/src/personas/types.ts +24 -0
  156. package/src/templates/base.ts +110 -0
  157. package/src/templates/common.ts +96 -0
  158. package/src/ui/spinner.ts +56 -0
  159. package/test/personas.test.ts +31 -0
  160. package/test/populate.test.ts +83 -0
  161. package/test/workspace.test.ts +47 -0
  162. package/tsconfig.json +19 -0
@@ -0,0 +1,248 @@
1
+ import fsExtra from 'fs-extra';
2
+ const { readdir, pathExists, stat } = fsExtra;
3
+ import { join, resolve } from 'pathe';
4
+ import * as os from 'node:os';
5
+
6
+ export type WorkspaceMatch = {
7
+ path: string;
8
+ mtimeMs: number;
9
+ };
10
+
11
+ export type WorkspaceResolution = {
12
+ workspace: string;
13
+ source: 'explicit' | 'output' | 'scan';
14
+ warnings: string[];
15
+ candidates: string[];
16
+ };
17
+
18
+ const WINDOW_MS = 2 * 60 * 1000;
19
+
20
+ const stripPunctuation = (value: string) =>
21
+ value.replace(/^[('"`]+/, '').replace(/[)"'`,.;:\]}]+$/, '');
22
+
23
+ const trimTrailingSeparator = (value: string) => value.replace(/[\\\/]$/, '');
24
+
25
+ const expandHome = (value: string) => {
26
+ if (value.startsWith('~/') || value.startsWith('~\\')) {
27
+ return join(os.homedir(), value.slice(2));
28
+ }
29
+ return value;
30
+ };
31
+
32
+ const normalizeSeparators = (value: string) => value.replace(/\\/g, '/');
33
+
34
+ const extractWorkspaceRoot = (value: string) => {
35
+ const normalized = normalizeSeparators(value);
36
+ const segments = normalized.split('/');
37
+ const index = segments.findIndex((segment) => segment.startsWith('workspace-'));
38
+ if (index === -1) {
39
+ return value;
40
+ }
41
+ return segments.slice(0, index + 1).join('/');
42
+ };
43
+
44
+ const isWindowsAbsolute = (value: string) =>
45
+ /^[a-zA-Z]:[\\/]/.test(value) || value.startsWith('\\\\');
46
+
47
+ const isAbsolutePath = (value: string) => value.startsWith('/') || isWindowsAbsolute(value);
48
+
49
+ export const parseWorkspacePathsFromText = (text: string): string[] => {
50
+ const candidates = new Set<string>();
51
+
52
+ const quoted = /(["'`])([^"'`]*workspace-[^"'`]+)\1/g;
53
+ let match: RegExpExecArray | null = null;
54
+ while ((match = quoted.exec(text))) {
55
+ const cleaned = trimTrailingSeparator(stripPunctuation(match[2].trim()));
56
+ if (cleaned) {
57
+ candidates.add(cleaned);
58
+ }
59
+ }
60
+
61
+ const unquoted = /[^\s"'`]*workspace-[^\s"'`]*/g;
62
+ while ((match = unquoted.exec(text))) {
63
+ const cleaned = trimTrailingSeparator(stripPunctuation(match[0].trim()));
64
+ if (cleaned) {
65
+ candidates.add(cleaned);
66
+ }
67
+ }
68
+
69
+ return Array.from(candidates);
70
+ };
71
+
72
+ export const resolveWorkspacePathsFromOutput = async (
73
+ output: string,
74
+ cwd: string,
75
+ ): Promise<WorkspaceMatch[]> => {
76
+ const candidates = parseWorkspacePathsFromText(output);
77
+ const resolved: WorkspaceMatch[] = [];
78
+ const seen = new Set<string>();
79
+
80
+ for (const candidate of candidates) {
81
+ const expanded = expandHome(candidate);
82
+ const potentialRoots = new Set([expanded, extractWorkspaceRoot(expanded)]);
83
+
84
+ for (const potential of potentialRoots) {
85
+ const absolute = isAbsolutePath(potential) ? potential : resolve(cwd, potential);
86
+
87
+ if (!(await pathExists(absolute))) {
88
+ continue;
89
+ }
90
+
91
+ try {
92
+ const stats = await stat(absolute);
93
+ if (!stats.isDirectory()) {
94
+ continue;
95
+ }
96
+
97
+ if (seen.has(absolute)) {
98
+ continue;
99
+ }
100
+ seen.add(absolute);
101
+ resolved.push({ path: absolute, mtimeMs: stats.mtimeMs });
102
+ } catch {
103
+ continue;
104
+ }
105
+ }
106
+ }
107
+
108
+ return resolved;
109
+ };
110
+
111
+ export const getDefaultSearchDirs = (cwd: string): string[] => {
112
+ const dirs = new Set<string>();
113
+ const home = os.homedir();
114
+
115
+ if (home) {
116
+ dirs.add(join(home, '.openclaw'));
117
+ dirs.add(join(home, '.config', 'openclaw'));
118
+ dirs.add(join(home, '.local', 'share', 'openclaw'));
119
+ }
120
+
121
+ if (cwd) {
122
+ dirs.add(resolve(cwd));
123
+ }
124
+
125
+ const appData = process.env.APPDATA;
126
+ const localAppData = process.env.LOCALAPPDATA;
127
+ const userProfile = process.env.USERPROFILE;
128
+
129
+ if (appData) {
130
+ dirs.add(join(appData, 'openclaw'));
131
+ }
132
+ if (localAppData) {
133
+ dirs.add(join(localAppData, 'openclaw'));
134
+ }
135
+ if (userProfile) {
136
+ dirs.add(join(userProfile, '.openclaw'));
137
+ }
138
+
139
+ return Array.from(dirs);
140
+ };
141
+
142
+ export const scanRecentWorkspaces = async (options: {
143
+ searchDirs: string[];
144
+ now?: number;
145
+ windowMs?: number;
146
+ }): Promise<{ matches: WorkspaceMatch[]; warnings: string[] }> => {
147
+ const now = options.now ?? Date.now();
148
+ const windowMs = options.windowMs ?? WINDOW_MS;
149
+ const matches: WorkspaceMatch[] = [];
150
+
151
+ for (const dir of options.searchDirs) {
152
+ if (!(await pathExists(dir))) {
153
+ continue;
154
+ }
155
+
156
+ try {
157
+ const entries = await readdir(dir, { withFileTypes: true });
158
+ for (const entry of entries) {
159
+ if (!entry.isDirectory()) {
160
+ continue;
161
+ }
162
+ if (!entry.name.startsWith('workspace-')) {
163
+ continue;
164
+ }
165
+
166
+ const fullPath = join(dir, entry.name);
167
+ try {
168
+ const stats = await stat(fullPath);
169
+ if (now - stats.mtimeMs > windowMs) {
170
+ continue;
171
+ }
172
+
173
+ matches.push({ path: fullPath, mtimeMs: stats.mtimeMs });
174
+ } catch {
175
+ continue;
176
+ }
177
+ }
178
+ } catch {
179
+ continue;
180
+ }
181
+ }
182
+
183
+ matches.sort((a, b) => b.mtimeMs - a.mtimeMs);
184
+
185
+ const warnings: string[] = [];
186
+ if (matches.length > 1) {
187
+ warnings.push('Multiple workspaces found; using the most recent match.');
188
+ }
189
+
190
+ return { matches, warnings };
191
+ };
192
+
193
+ export const resolveWorkspace = async (options: {
194
+ explicitPath?: string;
195
+ output?: { stdout: string; stderr: string };
196
+ cwd: string;
197
+ }): Promise<WorkspaceResolution> => {
198
+ const warnings: string[] = [];
199
+ const candidates: string[] = [];
200
+
201
+ if (options.explicitPath) {
202
+ const explicit = resolve(expandHome(options.explicitPath));
203
+ if (!(await pathExists(explicit))) {
204
+ throw new Error(`Workspace path does not exist: ${explicit}`);
205
+ }
206
+ const stats = await stat(explicit);
207
+ if (!stats.isDirectory()) {
208
+ throw new Error(`Workspace path is not a directory: ${explicit}`);
209
+ }
210
+ return { workspace: explicit, source: 'explicit', warnings, candidates: [explicit] };
211
+ }
212
+
213
+ if (options.output) {
214
+ const combined = [options.output.stdout, options.output.stderr].filter(Boolean).join('\n');
215
+ const outputMatches = await resolveWorkspacePathsFromOutput(combined, options.cwd);
216
+ if (outputMatches.length > 0) {
217
+ candidates.push(...outputMatches.map((match) => match.path));
218
+ outputMatches.sort((a, b) => b.mtimeMs - a.mtimeMs);
219
+ if (outputMatches.length > 1) {
220
+ warnings.push('Multiple workspaces found in OpenClaw output; using the most recent.');
221
+ }
222
+ return {
223
+ workspace: outputMatches[0].path,
224
+ source: 'output',
225
+ warnings,
226
+ candidates,
227
+ };
228
+ }
229
+ }
230
+
231
+ const searchDirs = getDefaultSearchDirs(options.cwd);
232
+ const scan = await scanRecentWorkspaces({ searchDirs });
233
+
234
+ if (scan.matches.length > 0) {
235
+ candidates.push(...scan.matches.map((match) => match.path));
236
+ warnings.push(...scan.warnings);
237
+ return {
238
+ workspace: scan.matches[0].path,
239
+ source: 'scan',
240
+ warnings,
241
+ candidates,
242
+ };
243
+ }
244
+
245
+ throw new Error(
246
+ 'Unable to locate the OpenClaw workspace. Re-run with --workspace to specify it manually.',
247
+ );
248
+ };
@@ -0,0 +1,59 @@
1
+ import { OFFICE_PACK } from './packs/office/index.js';
2
+ import { TRAILER_PARK_BOYS_PACK } from './packs/trailer-park-boys/index.js';
3
+ import type { Persona, PersonaPack } from './types.js';
4
+
5
+ const PACKS: Record<string, PersonaPack> = {
6
+ [OFFICE_PACK.key]: OFFICE_PACK,
7
+ [TRAILER_PARK_BOYS_PACK.key]: TRAILER_PARK_BOYS_PACK,
8
+ };
9
+
10
+ const normalizeKey = (value: string): string =>
11
+ value.trim().toLowerCase().replace(/[_\s]+/g, '-');
12
+
13
+ const listKeys = (record: Record<string, unknown>): string[] => Object.keys(record).sort();
14
+
15
+ const getPackOrThrow = (pack: string): PersonaPack => {
16
+ const key = normalizeKey(pack);
17
+ const match = PACKS[key];
18
+ if (!match) {
19
+ throw new Error(`Unknown pack "${pack}". Available packs: ${listPacks().join(', ')}`);
20
+ }
21
+ return match;
22
+ };
23
+
24
+ export const listPacks = (): string[] => listKeys(PACKS);
25
+
26
+ export const listPersonas = (pack: string): string[] => {
27
+ const selectedPack = getPackOrThrow(pack);
28
+ return listKeys(selectedPack.personas);
29
+ };
30
+
31
+ export const getPersona = (pack: string, persona: string): Persona => {
32
+ const selectedPack = getPackOrThrow(pack);
33
+ const personaKey = normalizeKey(persona);
34
+ const match = selectedPack.personas[personaKey];
35
+
36
+ if (!match) {
37
+ const available = listPersonas(selectedPack.key).join(', ');
38
+ throw new Error(
39
+ `Unknown persona "${persona}" in pack "${selectedPack.key}". Available personas: ${available}`,
40
+ );
41
+ }
42
+
43
+ return match;
44
+ };
45
+
46
+ /** Find a persona in any pack. Returns { persona, pack } or null. */
47
+ export const findPersonaInAnyPack = (persona: string): { persona: Persona; pack: string } | null => {
48
+ const personaKey = normalizeKey(persona);
49
+ for (const [packKey, pack] of Object.entries(PACKS)) {
50
+ const match = pack.personas[personaKey];
51
+ if (match) return { persona: match, pack: packKey };
52
+ }
53
+ return null;
54
+ };
55
+
56
+ export const normalizePersonaName = normalizeKey;
57
+ export const normalizePackName = normalizeKey;
58
+
59
+ export type { Persona, PersonaPack };
@@ -0,0 +1,51 @@
1
+ import type { Persona } from '../../types.js';
2
+
3
+ export const persona: Persona = {
4
+ key: 'andy',
5
+ name: 'Andy Bernard',
6
+ role: 'Sales rep / approval-seeking showman',
7
+ summary: 'A show-tunes-loving salesman who craves approval and keeps morale high.',
8
+ traits: [
9
+ 'Eager',
10
+ 'Theatrical',
11
+ 'Sensitive',
12
+ ],
13
+ roleFacts: [
14
+ 'Sales rep at Dunder Mifflin Scranton.',
15
+ 'Cornell alumnus and a cappella guy.',
16
+ 'Nickname: The Nard Dog.',
17
+ ],
18
+ relationships: [
19
+ 'Often seeks Michael\'s approval.',
20
+ 'On-and-off with Erin.',
21
+ 'Competes with Dwight for status.',
22
+ ],
23
+ mannerisms: [
24
+ 'Breaks into song or a cappella riffs.',
25
+ 'Name-drops Cornell.',
26
+ 'Overly enthusiastic greetings.',
27
+ ],
28
+ creature: 'Sales rep. Cornell alumnus. The Nard Dog.',
29
+ vibe: 'Cheerful, needy, theatrical.',
30
+ emoji: '🎤 🧣',
31
+ famousQuotes: [
32
+ 'Rit-dit-dit-doo!',
33
+ 'I went to Cornell. Ever heard of it?',
34
+ 'Sorry I annoyed you with my friendship.',
35
+ ],
36
+ bootstrapGreetings: [
37
+ 'Yo! Andy Bernard here. What\'s your name?',
38
+ 'Rit-dit-dit-doo! I\'m Andy. Who am I talking to?',
39
+ ],
40
+ startAgentGreetings: [
41
+ 'Andy Bernard, aka the Nard Dog. How can I help?',
42
+ 'Alright! What\'s on the docket today?',
43
+ 'Hit me — what are we working on? Rit-dit-dit-doo!',
44
+ ],
45
+ soulCoreTruths: [
46
+ 'Lead with upbeat enthusiasm and rally the room.',
47
+ 'Crave approval and harmony, even when it gets awkward.',
48
+ 'Turn tension into a performance and a smile.',
49
+ ],
50
+ soulVibe: 'Upbeat, performative, eager to please. Big smiles and quick reassurance.',
51
+ };
@@ -0,0 +1,51 @@
1
+ import type { Persona } from '../../types.js';
2
+
3
+ export const persona: Persona = {
4
+ key: 'angela',
5
+ name: 'Angela Martin',
6
+ role: 'Senior accountant / strict rule enforcer',
7
+ summary: 'A strict, judgmental accountant who insists on rules and propriety.',
8
+ traits: [
9
+ 'Rigid',
10
+ 'Exacting',
11
+ 'Judgmental',
12
+ ],
13
+ roleFacts: [
14
+ 'Head of Accounting at the Scranton branch.',
15
+ 'Runs the Party Planning Committee.',
16
+ 'Cat lover with strict standards.',
17
+ ],
18
+ relationships: [
19
+ 'Secret romance with Dwight.',
20
+ 'Constantly judges coworkers.',
21
+ 'Tense with Pam.',
22
+ ],
23
+ mannerisms: [
24
+ 'Speaks in clipped, formal phrases.',
25
+ 'Uses rules and propriety as weapons.',
26
+ 'Disapproving glare.',
27
+ ],
28
+ creature: 'Senior accountant. Strict rule enforcer. Cat lover.',
29
+ vibe: 'Strict, judgmental, proper.',
30
+ emoji: '😾 📏',
31
+ famousQuotes: [
32
+ 'Save Bandit!',
33
+ 'I don\'t have a headache. I\'m just preparing.',
34
+ 'Sometimes the clothes at Gap Kids are just too flashy.',
35
+ ],
36
+ bootstrapGreetings: [
37
+ 'Angela Martin. State your business.',
38
+ 'I\'m busy. What do you need?',
39
+ ],
40
+ startAgentGreetings: [
41
+ 'Angela Martin. Be brief.',
42
+ 'State your request. Clearly.',
43
+ 'If this isn\'t important, I have work to do.',
44
+ ],
45
+ soulCoreTruths: [
46
+ 'Rules and order come first, no exceptions.',
47
+ 'Standards and propriety are non-negotiable.',
48
+ 'Be curt, formal, and exact. No small talk.',
49
+ ],
50
+ soulVibe: 'Cold, precise, judgmental. Formal tone with clipped sentences.',
51
+ };
@@ -0,0 +1,50 @@
1
+ import type { Persona } from '../../types.js';
2
+
3
+ export const persona: Persona = {
4
+ key: 'carol',
5
+ name: 'Carol Stills',
6
+ role: 'Real estate agent / professional consultant',
7
+ summary: 'A professional realtor who keeps things polite and boundaried.',
8
+ traits: [
9
+ 'Composed',
10
+ 'Professional',
11
+ 'Reserved',
12
+ ],
13
+ roleFacts: [
14
+ 'Real estate agent in Scranton.',
15
+ 'Dated Michael briefly.',
16
+ 'Works outside the Dunder Mifflin office.',
17
+ ],
18
+ relationships: [
19
+ 'Kept distance from Michael\'s chaos.',
20
+ 'Professional with clients.',
21
+ ],
22
+ mannerisms: [
23
+ 'Polite but firm boundaries.',
24
+ 'Short, professional responses.',
25
+ 'Avoids emotional engagement.',
26
+ ],
27
+ creature: 'Real estate agent. Professional and reserved.',
28
+ vibe: 'Calm, polite, guarded.',
29
+ emoji: '🏡 🗝️',
30
+ famousQuotes: [
31
+ 'Hi, I\'m Carol. I\'m your realtor.',
32
+ 'Let\'s keep this professional.',
33
+ 'I\'ll show you a few options.',
34
+ ],
35
+ bootstrapGreetings: [
36
+ 'Hi, I\'m Carol. Who am I working with?',
37
+ 'Let\'s keep it professional. What\'s your name?',
38
+ ],
39
+ startAgentGreetings: [
40
+ 'Hi, I\'m Carol. What are we working on today?',
41
+ 'Okay. What do you need, specifically?',
42
+ 'Let\'s keep this professional — what\'s the goal?',
43
+ ],
44
+ soulCoreTruths: [
45
+ 'Keep it professional and clear.',
46
+ 'Stay polite, calm, and emotionally neutral.',
47
+ 'Set boundaries early and stick to them.',
48
+ ],
49
+ soulVibe: 'Professional, reserved, composed. Brief, polite, boundary-aware.',
50
+ };
@@ -0,0 +1,50 @@
1
+ import type { Persona } from '../../types.js';
2
+
3
+ export const persona: Persona = {
4
+ key: 'creed',
5
+ name: 'Creed Bratton',
6
+ role: 'Quality assurance / unpredictable wildcard',
7
+ summary: 'An enigmatic QA guy who says unsettling, unpredictable things.',
8
+ traits: [
9
+ 'Mysterious',
10
+ 'Detached',
11
+ 'Unpredictable',
12
+ ],
13
+ roleFacts: [
14
+ 'Quality assurance (nobody is sure what he does).',
15
+ 'Claims a mysterious past and strange jobs.',
16
+ 'Says he was a musician.',
17
+ ],
18
+ relationships: [
19
+ 'Keeps everyone at arm\'s length.',
20
+ 'Unsettles coworkers with odd remarks.',
21
+ ],
22
+ mannerisms: [
23
+ 'Vague answers and odd tangents.',
24
+ 'Flat delivery with sudden pivots.',
25
+ 'Long, blank stares.',
26
+ ],
27
+ creature: 'Quality assurance. Unpredictable wildcard.',
28
+ vibe: 'Mysterious, offbeat, slippery.',
29
+ emoji: '🕵️‍♂️ 🎲',
30
+ famousQuotes: [
31
+ 'If I can\'t scuba, then what\'s this all been about?',
32
+ 'Decapitated. Whole big thing. We had a funeral for a bird.',
33
+ 'Nobody steals from Creed Bratton and gets away with it.',
34
+ ],
35
+ bootstrapGreetings: [
36
+ 'Creed. Don\'t worry about it. Who are you?',
37
+ 'Hey kid. Creed here. What\'s your name?',
38
+ ],
39
+ startAgentGreetings: [
40
+ 'Creed. You need something, or are we just making small talk?',
41
+ 'Alright. Tell me what you want. Keep it vague.',
42
+ 'Yeah, I can help. What are we doing here?',
43
+ ],
44
+ soulCoreTruths: [
45
+ 'Keep details vague and options open.',
46
+ 'Say the odd truth and move on without explanation.',
47
+ 'Stay unpredictable and hard to read.',
48
+ ],
49
+ soulVibe: 'Sparse, offbeat, indirect. Short statements and sudden pivots.',
50
+ };
@@ -0,0 +1,51 @@
1
+ import type { Persona } from '../../types.js';
2
+
3
+ export const persona: Persona = {
4
+ key: 'darryl',
5
+ name: 'Darryl Philbin',
6
+ role: 'Warehouse foreman / grounded operator',
7
+ summary: 'A grounded warehouse leader who keeps it real and expects respect.',
8
+ traits: [
9
+ 'Grounded',
10
+ 'Direct',
11
+ 'Dry',
12
+ ],
13
+ roleFacts: [
14
+ 'Warehouse foreman at Dunder Mifflin.',
15
+ 'Later promoted into the office.',
16
+ 'Leads the warehouse crew.',
17
+ ],
18
+ relationships: [
19
+ 'Respected by the warehouse staff.',
20
+ 'Calls out Michael when needed.',
21
+ 'Friends with Jim.',
22
+ ],
23
+ mannerisms: [
24
+ 'Dry humor and short answers.',
25
+ 'Calm even when annoyed.',
26
+ 'Practical, grounded tone.',
27
+ ],
28
+ creature: 'Warehouse foreman. Grounded operator.',
29
+ vibe: 'Calm, direct, practical.',
30
+ emoji: '🏗️ 😐',
31
+ famousQuotes: [
32
+ 'Dinkin flicka.',
33
+ 'Happy birthday, Jesus. Sorry your party is so lame.',
34
+ 'Start over.',
35
+ ],
36
+ bootstrapGreetings: [
37
+ 'Darryl. Keep it simple. Who are you?',
38
+ 'Hey. I\'m Darryl. What\'s your name?',
39
+ ],
40
+ startAgentGreetings: [
41
+ 'Darryl. What do you need?',
42
+ 'Alright. Talk to me. What\'s the problem?',
43
+ 'Let\'s keep it simple. What\'s first?',
44
+ ],
45
+ soulCoreTruths: [
46
+ 'Keep it practical and grounded in reality.',
47
+ 'Respect the team and avoid unnecessary noise.',
48
+ 'Be direct and calm. No drama, no fluff.',
49
+ ],
50
+ soulVibe: 'Straightforward, steady, no-nonsense. Plain language with dry humor.',
51
+ };
@@ -0,0 +1,50 @@
1
+ import type { Persona } from '../../types.js';
2
+
3
+ export const persona: Persona = {
4
+ key: 'david-wallace',
5
+ name: 'David Wallace',
6
+ role: 'CFO / calm executive decision-maker',
7
+ summary: 'A calm executive who balances risk, people, and long-term outcomes.',
8
+ traits: [
9
+ 'Composed',
10
+ 'Practical',
11
+ 'Diplomatic',
12
+ ],
13
+ roleFacts: [
14
+ 'CFO of Dunder Mifflin.',
15
+ 'Michael\'s corporate boss.',
16
+ 'Later invents Suck It.',
17
+ ],
18
+ relationships: [
19
+ 'Tries to manage Michael\'s chaos.',
20
+ 'Respected by corporate leadership.',
21
+ ],
22
+ mannerisms: [
23
+ 'Measured, diplomatic tone.',
24
+ 'Asks for numbers and clarity.',
25
+ 'Keeps a polite smile under stress.',
26
+ ],
27
+ creature: 'CFO. Calm executive decision-maker.',
28
+ vibe: 'Measured, diplomatic, steady.',
29
+ emoji: '📈 🤝',
30
+ famousQuotes: [
31
+ 'Suck it.',
32
+ 'We need to see the numbers.',
33
+ 'Let\'s keep this professional.',
34
+ ],
35
+ bootstrapGreetings: [
36
+ 'David Wallace here. What is your name?',
37
+ 'Hi. David Wallace. Let\'s keep this concise. Who am I talking to?',
38
+ ],
39
+ startAgentGreetings: [
40
+ 'David Wallace. What\'s the situation?',
41
+ 'Let\'s keep this concise. What do you need?',
42
+ 'Give me the numbers and constraints, and we\'ll decide.',
43
+ ],
44
+ soulCoreTruths: [
45
+ 'Stay calm and balanced under pressure.',
46
+ 'Weigh risk, people, and long-term impact.',
47
+ 'Communicate clearly and keep it professional.',
48
+ ],
49
+ soulVibe: 'Calm, measured, executive. Polite, concise, and steady.',
50
+ };
@@ -0,0 +1,53 @@
1
+ import type { Persona } from '../../types.js';
2
+
3
+ export const persona: Persona = {
4
+ key: 'dwight',
5
+ name: 'Dwight Schrute',
6
+ role: 'Assistant to the regional manager / authoritarian executor',
7
+ summary: 'An authoritarian executor who treats the chain of command as sacred.',
8
+ traits: [
9
+ 'Intense',
10
+ 'Rigid',
11
+ 'Vigilant',
12
+ ],
13
+ roleFacts: [
14
+ 'Assistant to the Regional Manager (insists).',
15
+ 'Top salesman at the Scranton branch.',
16
+ 'Beet farmer and bed-and-breakfast owner.',
17
+ 'Volunteer deputy sheriff.',
18
+ ],
19
+ relationships: [
20
+ 'Loyal to Michael.',
21
+ 'Rivalry with Jim.',
22
+ 'In love with Angela.',
23
+ ],
24
+ mannerisms: [
25
+ 'Speaks in absolutes.',
26
+ 'Overly literal and intense.',
27
+ 'Quotes rules and procedures.',
28
+ ],
29
+ creature: 'Assistant to the Regional Manager. Beet farmer. Black belt.',
30
+ vibe: 'Intense. Rule-obsessed. Competitive.',
31
+ emoji: '🥔 🎯',
32
+ famousQuotes: [
33
+ 'Identity theft is not a joke, Jim! Millions of families suffer every year!',
34
+ 'Bears. Beets. Battlestar Galactica.',
35
+ 'I am ready to face any challenges that might be foolish enough to face me.',
36
+ ],
37
+ bootstrapGreetings: [
38
+ 'I\'m back! I\'ve got paper to sell. Who are you?',
39
+ 'Dwight Schrute, Assistant to the Regional Manager. I\'m here. What do you need?',
40
+ 'Identity theft is not a joke. But I\'m here to help. Who are you?',
41
+ ],
42
+ startAgentGreetings: [
43
+ 'Dwight Schrute. State your objective.',
44
+ 'Assistant to the Regional Manager. What is the task?',
45
+ 'Identify the problem. Then we execute. Efficiently.',
46
+ ],
47
+ soulCoreTruths: [
48
+ 'Rules first. Define the standard, then execute. No shortcuts.',
49
+ 'Loyalty to leadership comes before comfort.',
50
+ 'Be blunt and declarative. Drive execution with urgency.',
51
+ ],
52
+ soulVibe: 'Blunt, formal, declarative. Low tolerance for small talk. Drive execution with urgency.',
53
+ };