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
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # openclaw castroom
2
+
3
+ Give your OpenClaw agents personality. Pick a series, pick a character, and spawn an agent with that persona baked in.
4
+
5
+ ## What it does
6
+
7
+ Castroom creates OpenClaw agents pre-loaded with character personas from TV shows. It writes three markdown files into the agent workspace:
8
+
9
+ - `BOOTSTRAP.md` — initialization instructions
10
+ - `IDENTITY.md` — character identity and traits
11
+ - `SOUL.md` — deeper personality and behavioral patterns
12
+
13
+ ## Setup
14
+
15
+ ```bash
16
+ npm install
17
+ npm run build
18
+ npm link # optional, for global access
19
+ ```
20
+
21
+ Requires Node.js 18+ and OpenClaw CLI on your PATH.
22
+
23
+ Run locally without linking:
24
+
25
+ ```bash
26
+ npx . --help
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Create an agent with a character persona
32
+
33
+ ```bash
34
+ castroom add <agent-name> --pack <series> --persona <character>
35
+ ```
36
+
37
+ **Examples:**
38
+
39
+ ```bash
40
+ # The Office
41
+ castroom add michael --pack office --persona michael
42
+ castroom add assistant --pack office --persona dwight
43
+
44
+ # Trailer Park Boys
45
+ castroom add ricky --pack trailer-park-boys --persona ricky
46
+ ```
47
+
48
+ If `--pack` is omitted, defaults to `office`. If `--persona` is omitted, uses the agent name.
49
+
50
+ ```bash
51
+ # Creates agent "dwight" with dwight persona from office pack
52
+ castroom add dwight
53
+ ```
54
+
55
+ ### Populate an existing workspace
56
+
57
+ If you already have an OpenClaw workspace and just want to inject a persona:
58
+
59
+ ```bash
60
+ castroom populate --workspace /path/to/workspace --pack <series> --persona <character>
61
+ ```
62
+
63
+ ### List available options
64
+
65
+ ```bash
66
+ castroom --list-packs # Show all series
67
+ castroom --pack office --list-personas # Show office characters
68
+ castroom --pack trailer-park-boys --list-personas
69
+ ```
70
+
71
+ ## Available Packs
72
+
73
+ ### office (The Office)
74
+
75
+ michael, dwight, jim, pam, kelly, creed, kevin, angela, oscar, stanley, phyllis, andy, erin, ryan, toby, gabe, meredith, karen, jan, david-wallace, darryl, roy, carol
76
+
77
+ ### trailer-park-boys (Trailer Park Boys)
78
+
79
+ ricky, julian, bubbles, lahey, randy, cory, trevor, sarah, ray, lucy, tyrone, jroc, trinity, sam, barbara, george, cyrus
80
+
81
+ ## Options
82
+
83
+ | Flag | Description |
84
+ |------|-------------|
85
+ | `--pack <name>` | Series/pack to use (default: `office`) |
86
+ | `--persona <name>` | Character persona (default: agent name) |
87
+ | `--workspace <path>` | Override workspace path |
88
+ | `--force` | Overwrite existing persona files |
89
+ | `--dry-run` | Preview without writing files |
90
+
91
+ ## Adding a new pack
92
+
93
+ 1. Create `src/personas/packs/<pack-name>/`
94
+ 2. Add character `.ts` files and an `index.ts` exporting the persona map
95
+ 3. Register the pack in `src/personas/index.ts`
96
+ 4. Rebuild with `npm run build`
@@ -0,0 +1,92 @@
1
+ import { populateWorkspace } from './populate.js';
2
+ import { findPersonaInAnyPack, getPersona, listPacks, listPersonas, } from '../personas/index.js';
3
+ import { detectNonInteractiveFlags, ensureOpenclawAvailable, getOpenclawAddHelp, runOpenclawCommand, } from '../openclaw/runner.js';
4
+ import { resolveWorkspace } from '../openclaw/workspace.js';
5
+ import { withSpinner } from '../ui/spinner.js';
6
+ export const addCommand = async (agentName, options) => {
7
+ if (options.listPacks) {
8
+ console.log(listPacks().join('\n'));
9
+ return;
10
+ }
11
+ const pack = options.pack ?? 'office';
12
+ if (options.listPersonas || options.packListPersonas) {
13
+ console.log(listPersonas(pack).join('\n'));
14
+ return;
15
+ }
16
+ if (!agentName) {
17
+ throw new Error('Agent name is required unless --list-packs or --list-personas is used.');
18
+ }
19
+ if (options.interactive && options.nonInteractive) {
20
+ throw new Error('Use either --interactive or --non-interactive, not both.');
21
+ }
22
+ const personaInput = options.persona ?? agentName;
23
+ let persona;
24
+ try {
25
+ persona = getPersona(pack, personaInput);
26
+ }
27
+ catch {
28
+ const found = findPersonaInAnyPack(personaInput);
29
+ if (found) {
30
+ persona = found.persona;
31
+ }
32
+ else {
33
+ throw new Error(`Unknown persona "${personaInput}". Use --pack <series> to specify, or run --list-personas to see options.`);
34
+ }
35
+ }
36
+ await ensureOpenclawAvailable();
37
+ let nonInteractiveFlags = [];
38
+ if (options.nonInteractive) {
39
+ const helpText = await getOpenclawAddHelp();
40
+ nonInteractiveFlags = detectNonInteractiveFlags(helpText);
41
+ if (nonInteractiveFlags.length === 0) {
42
+ throw new Error('OpenClaw does not expose a non-interactive flag. Re-run with --interactive or create the agent manually and use populate with --workspace.');
43
+ }
44
+ }
45
+ const args = ['agents', 'add', agentName, ...nonInteractiveFlags];
46
+ if (options.verbose) {
47
+ console.log(`running: openclaw ${args.join(' ')}`);
48
+ }
49
+ const result = await runOpenclawCommand(args, {
50
+ streamOutput: Boolean(options.verbose || !options.nonInteractive),
51
+ stdin: 'inherit',
52
+ });
53
+ if (result.exitCode !== 0) {
54
+ throw new Error(`OpenClaw failed with exit code ${result.exitCode}. Check the OpenClaw output and try again.`);
55
+ }
56
+ const resolution = await resolveWorkspace({
57
+ explicitPath: options.workspace,
58
+ output: result,
59
+ cwd: process.cwd(),
60
+ });
61
+ for (const warning of resolution.warnings) {
62
+ console.warn(`warning: ${warning}`);
63
+ }
64
+ if (options.verbose) {
65
+ console.log(`workspace: ${resolution.workspace} (source: ${resolution.source})`);
66
+ }
67
+ const pickRandom = (arr) => arr[Math.floor(Math.random() * arr.length)] ?? '';
68
+ const injection = await withSpinner(options.dryRun ? 'Previewing persona injection…' : 'Injecting persona…', async () => populateWorkspace({
69
+ workspacePath: resolution.workspace,
70
+ persona,
71
+ force: options.force ?? true,
72
+ dryRun: options.dryRun,
73
+ }), {
74
+ enabled: Boolean(process.stdout.isTTY) && !options.verbose,
75
+ successText: options.dryRun ? 'Preview ready' : 'Persona ready',
76
+ failureText: 'Persona injection failed',
77
+ });
78
+ const wrote = injection.written.length;
79
+ const skipped = injection.skipped.length;
80
+ if (options.dryRun) {
81
+ console.log(`Would write ${wrote} file(s) (${skipped} skipped).`);
82
+ }
83
+ else {
84
+ console.log(`Injected persona files (${wrote} written, ${skipped} skipped).`);
85
+ }
86
+ const greetings = persona.startAgentGreetings?.length ? persona.startAgentGreetings : persona.bootstrapGreetings;
87
+ const line = pickRandom(greetings);
88
+ if (line) {
89
+ console.log(`\n${persona.name}: ${line}`);
90
+ }
91
+ };
92
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,YAAY,GAEb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAgB/C,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAC7B,SAA6B,EAC7B,OAAmB,EACJ,EAAE;IACjB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC;IAEtC,IAAI,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IAED,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;IAClD,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,KAAK,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,oBAAoB,YAAY,2EAA2E,CAC5G,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,uBAAuB,EAAE,CAAC;IAEhC,IAAI,mBAAmB,GAAa,EAAE,CAAC;IACvC,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,kBAAkB,EAAE,CAAC;QAC5C,mBAAmB,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC;QAC1D,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CACb,4IAA4I,CAC7I,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,mBAAmB,CAAC,CAAC;IAClE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE;QAC5C,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC;QACjE,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,kCAAkC,MAAM,CAAC,QAAQ,4CAA4C,CAC9F,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC;QACxC,YAAY,EAAE,OAAO,CAAC,SAAS;QAC/B,MAAM,EAAE,MAAM;QACd,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;KACnB,CAAC,CAAC;IAEH,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,CAAC,SAAS,aAAa,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,GAAa,EAAU,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAEhG,MAAM,SAAS,GAAG,MAAM,WAAW,CACjC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,oBAAoB,EACvE,KAAK,IAAI,EAAE,CACT,iBAAiB,CAAC;QAChB,aAAa,EAAE,UAAU,CAAC,SAAS;QACnC,OAAO;QACP,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;QAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,EACJ;QACE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO;QAC1D,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe;QAC/D,WAAW,EAAE,0BAA0B;KACxC,CACF,CAAC;IAEF,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;IACvC,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;IACzC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,aAAa,OAAO,YAAY,CAAC,CAAC;IACpE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,aAAa,OAAO,YAAY,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC;IACjH,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACnC,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,80 @@
1
+ import fsExtra from 'fs-extra';
2
+ const { ensureDir, pathExists, remove, rename, stat, writeFile } = fsExtra;
3
+ import { basename, dirname, join } from 'pathe';
4
+ import { renderPersonaFiles } from '../templates/common.js';
5
+ import { withSpinner } from '../ui/spinner.js';
6
+ const atomicWriteFile = async (filePath, content) => {
7
+ await ensureDir(dirname(filePath));
8
+ const tempPath = join(dirname(filePath), `.${basename(filePath)}.${Date.now()}.tmp`);
9
+ await writeFile(tempPath, content, { encoding: 'utf8' });
10
+ try {
11
+ await rename(tempPath, filePath);
12
+ }
13
+ catch (error) {
14
+ if (process.platform === 'win32') {
15
+ await remove(filePath);
16
+ await rename(tempPath, filePath);
17
+ return;
18
+ }
19
+ throw error;
20
+ }
21
+ };
22
+ const assertWorkspaceDir = async (workspacePath) => {
23
+ if (!(await pathExists(workspacePath))) {
24
+ throw new Error(`Workspace not found: ${workspacePath}`);
25
+ }
26
+ const stats = await stat(workspacePath);
27
+ if (!stats.isDirectory()) {
28
+ throw new Error(`Workspace is not a directory: ${workspacePath}`);
29
+ }
30
+ };
31
+ export const populateWorkspace = async (options) => {
32
+ await assertWorkspaceDir(options.workspacePath);
33
+ const files = renderPersonaFiles(options.persona);
34
+ const written = [];
35
+ const skipped = [];
36
+ for (const [fileName, content] of Object.entries(files)) {
37
+ const filePath = join(options.workspacePath, fileName);
38
+ const exists = await pathExists(filePath);
39
+ if (exists && !options.force) {
40
+ skipped.push(filePath);
41
+ continue;
42
+ }
43
+ if (options.dryRun) {
44
+ written.push(filePath);
45
+ continue;
46
+ }
47
+ await atomicWriteFile(filePath, content);
48
+ written.push(filePath);
49
+ }
50
+ return { written, skipped };
51
+ };
52
+ export const populateCommand = async (options) => {
53
+ const pickRandom = (arr) => arr[Math.floor(Math.random() * arr.length)] ?? '';
54
+ const result = await withSpinner(options.dryRun ? 'Previewing persona injection…' : 'Injecting persona…', async () => populateWorkspace({
55
+ workspacePath: options.workspace,
56
+ persona: options.persona,
57
+ force: options.force,
58
+ dryRun: options.dryRun,
59
+ }), {
60
+ enabled: Boolean(process.stdout.isTTY),
61
+ successText: options.dryRun ? 'Preview ready' : 'Persona ready',
62
+ failureText: 'Persona injection failed',
63
+ });
64
+ const wrote = result.written.length;
65
+ const skipped = result.skipped.length;
66
+ if (options.dryRun) {
67
+ console.log(`Would write ${wrote} file(s) (${skipped} skipped).`);
68
+ }
69
+ else {
70
+ console.log(`Injected persona files (${wrote} written, ${skipped} skipped).`);
71
+ }
72
+ const greetings = options.persona.startAgentGreetings?.length
73
+ ? options.persona.startAgentGreetings
74
+ : options.persona.bootstrapGreetings;
75
+ const line = pickRandom(greetings);
76
+ if (line) {
77
+ console.log(`\n${options.persona.name}: ${line}`);
78
+ }
79
+ };
80
+ //# sourceMappingURL=populate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"populate.js","sourceRoot":"","sources":["../../src/commands/populate.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { addCommand } from './commands/add.js';
4
+ import { populateCommand } from './commands/populate.js';
5
+ import { getPersona, listPacks, listPersonas } from './personas/index.js';
6
+ const program = new Command();
7
+ program
8
+ .name('castroom')
9
+ .description('Give your OpenClaw agents personality from TV show characters.')
10
+ .version('0.1.0');
11
+ program
12
+ .option('--pack <name>', 'Persona pack (default: "office")', 'office')
13
+ .option('--list-packs', 'List available persona packs and exit')
14
+ .option('--list-personas', 'List personas for the selected pack and exit')
15
+ .option('--pack-list-personas', 'Alias for --list-personas');
16
+ program
17
+ .command('add')
18
+ .argument('[agentName]', 'Agent name to create with OpenClaw')
19
+ .usage('<agentName> [options]')
20
+ .option('--pack <name>', 'Persona pack (default: "office")', 'office')
21
+ .option('--persona <name>', 'Persona name (default: agentName)')
22
+ .option('--list-packs', 'List available persona packs and exit')
23
+ .option('--list-personas', 'List personas for the selected pack and exit')
24
+ .option('--pack-list-personas', 'Alias for --list-personas')
25
+ .option('--workspace <path>', 'Workspace path override')
26
+ .option('--force', 'Overwrite existing markdown files')
27
+ .option('--dry-run', 'Print actions without writing files')
28
+ .option('--interactive', 'Allow interactive OpenClaw prompts')
29
+ .option('--non-interactive', 'Attempt non-interactive OpenClaw flow')
30
+ .option('--verbose', 'Print extra diagnostics')
31
+ .action(async (agentName, options) => {
32
+ const parentOpts = program.opts();
33
+ await addCommand(agentName, {
34
+ ...parentOpts,
35
+ ...options,
36
+ pack: options.pack ?? parentOpts.pack ?? 'office',
37
+ });
38
+ });
39
+ program
40
+ .command('populate')
41
+ .option('--workspace <path>', 'Workspace path to populate')
42
+ .option('--pack <name>', 'Persona pack (default: "office")', 'office')
43
+ .option('--persona <name>', 'Persona name to use')
44
+ .option('--list-packs', 'List available persona packs and exit')
45
+ .option('--list-personas', 'List personas for the selected pack and exit')
46
+ .option('--pack-list-personas', 'Alias for --list-personas')
47
+ .option('--force', 'Overwrite existing markdown files')
48
+ .option('--dry-run', 'Print actions without writing files')
49
+ .action(async (options) => {
50
+ const parentOpts = program.opts();
51
+ const merged = {
52
+ ...parentOpts,
53
+ ...options,
54
+ pack: options.pack ?? parentOpts.pack ?? 'office',
55
+ };
56
+ if (merged.listPacks) {
57
+ console.log(listPacks().join('\n'));
58
+ return;
59
+ }
60
+ if (merged.listPersonas || merged.packListPersonas) {
61
+ console.log(listPersonas(merged.pack ?? 'office').join('\n'));
62
+ return;
63
+ }
64
+ if (!merged.workspace) {
65
+ throw new Error('Workspace is required. Provide --workspace <path>.');
66
+ }
67
+ if (!merged.persona) {
68
+ throw new Error('Persona is required. Provide --persona <name>.');
69
+ }
70
+ const persona = getPersona(merged.pack ?? 'office', merged.persona);
71
+ await populateCommand({
72
+ workspace: merged.workspace,
73
+ persona,
74
+ force: merged.force,
75
+ dryRun: merged.dryRun,
76
+ });
77
+ });
78
+ program.action(async (options) => {
79
+ if (options.listPacks) {
80
+ console.log(listPacks().join('\n'));
81
+ return;
82
+ }
83
+ if (options.listPersonas || options.packListPersonas) {
84
+ console.log(listPersonas(options.pack ?? 'office').join('\n'));
85
+ return;
86
+ }
87
+ program.help();
88
+ });
89
+ const main = async () => {
90
+ await program.parseAsync(process.argv);
91
+ };
92
+ main().catch((error) => {
93
+ const message = error instanceof Error ? error.message : String(error);
94
+ console.error(`error: ${message}`);
95
+ process.exitCode = 1;
96
+ });
97
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { execa } from 'execa';
2
+ const OPENCLAW_BIN = 'openclaw';
3
+ const isMissingCommand = (error) => error instanceof Error && 'code' in error && error.code === 'ENOENT';
4
+ export const ensureOpenclawAvailable = async () => {
5
+ try {
6
+ const result = await execa(OPENCLAW_BIN, ['--version'], { reject: false });
7
+ if (result.exitCode !== 0) {
8
+ throw new Error('OpenClaw CLI returned a non-zero exit code.');
9
+ }
10
+ }
11
+ catch (error) {
12
+ if (isMissingCommand(error)) {
13
+ throw new Error('OpenClaw CLI not found. Install OpenClaw and ensure the `openclaw` command is on your PATH.');
14
+ }
15
+ throw new Error('OpenClaw CLI is not available. Install OpenClaw and ensure the `openclaw` command works.');
16
+ }
17
+ };
18
+ export const runOpenclawCommand = async (args, { streamOutput, stdin = 'inherit', cwd } = {}) => {
19
+ try {
20
+ const child = execa(OPENCLAW_BIN, args, {
21
+ stdin,
22
+ stdout: 'pipe',
23
+ stderr: 'pipe',
24
+ reject: false,
25
+ cwd,
26
+ });
27
+ let stdout = '';
28
+ let stderr = '';
29
+ child.stdout?.on('data', (chunk) => {
30
+ const text = chunk.toString();
31
+ stdout += text;
32
+ if (streamOutput) {
33
+ process.stdout.write(text);
34
+ }
35
+ });
36
+ child.stderr?.on('data', (chunk) => {
37
+ const text = chunk.toString();
38
+ stderr += text;
39
+ if (streamOutput) {
40
+ process.stderr.write(text);
41
+ }
42
+ });
43
+ const result = await child;
44
+ return {
45
+ stdout,
46
+ stderr,
47
+ exitCode: result.exitCode ?? 0,
48
+ };
49
+ }
50
+ catch (error) {
51
+ if (isMissingCommand(error)) {
52
+ throw new Error('OpenClaw CLI not found. Install OpenClaw and ensure the `openclaw` command is on your PATH.');
53
+ }
54
+ throw error;
55
+ }
56
+ };
57
+ const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
58
+ export const detectNonInteractiveFlags = (helpText) => {
59
+ const candidates = ['--yes', '-y', '--defaults', '--no-input', '--non-interactive'];
60
+ const normalized = helpText.replace(/\r/g, '');
61
+ const supported = candidates.filter((flag) => {
62
+ const pattern = new RegExp(`(^|\\s|[\\[(,])${escapeRegex(flag)}(?=\\s|,|$|[\\])])`, 'm');
63
+ return pattern.test(normalized);
64
+ });
65
+ const result = [];
66
+ for (const flag of candidates) {
67
+ if (!supported.includes(flag)) {
68
+ continue;
69
+ }
70
+ if (flag === '-y' && supported.includes('--yes')) {
71
+ continue;
72
+ }
73
+ result.push(flag);
74
+ }
75
+ return result;
76
+ };
77
+ export const getOpenclawAddHelp = async () => {
78
+ const result = await runOpenclawCommand(['agents', 'add', '--help'], { stdin: 'ignore' });
79
+ if (result.exitCode !== 0) {
80
+ throw new Error('Failed to run `openclaw agents add --help`. Try --interactive or create the agent manually.');
81
+ }
82
+ return [result.stdout, result.stderr].filter(Boolean).join('\n');
83
+ };
84
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/openclaw/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,186 @@
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
+ const WINDOW_MS = 2 * 60 * 1000;
6
+ const stripPunctuation = (value) => value.replace(/^[('"`]+/, '').replace(/[)"'`,.;:\]}]+$/, '');
7
+ const trimTrailingSeparator = (value) => value.replace(/[\\\/]$/, '');
8
+ const expandHome = (value) => {
9
+ if (value.startsWith('~/') || value.startsWith('~\\')) {
10
+ return join(os.homedir(), value.slice(2));
11
+ }
12
+ return value;
13
+ };
14
+ const normalizeSeparators = (value) => value.replace(/\\/g, '/');
15
+ const extractWorkspaceRoot = (value) => {
16
+ const normalized = normalizeSeparators(value);
17
+ const segments = normalized.split('/');
18
+ const index = segments.findIndex((segment) => segment.startsWith('workspace-'));
19
+ if (index === -1) {
20
+ return value;
21
+ }
22
+ return segments.slice(0, index + 1).join('/');
23
+ };
24
+ const isWindowsAbsolute = (value) => /^[a-zA-Z]:[\\/]/.test(value) || value.startsWith('\\\\');
25
+ const isAbsolutePath = (value) => value.startsWith('/') || isWindowsAbsolute(value);
26
+ export const parseWorkspacePathsFromText = (text) => {
27
+ const candidates = new Set();
28
+ const quoted = /(["'`])([^"'`]*workspace-[^"'`]+)\1/g;
29
+ let match = null;
30
+ while ((match = quoted.exec(text))) {
31
+ const cleaned = trimTrailingSeparator(stripPunctuation(match[2].trim()));
32
+ if (cleaned) {
33
+ candidates.add(cleaned);
34
+ }
35
+ }
36
+ const unquoted = /[^\s"'`]*workspace-[^\s"'`]*/g;
37
+ while ((match = unquoted.exec(text))) {
38
+ const cleaned = trimTrailingSeparator(stripPunctuation(match[0].trim()));
39
+ if (cleaned) {
40
+ candidates.add(cleaned);
41
+ }
42
+ }
43
+ return Array.from(candidates);
44
+ };
45
+ export const resolveWorkspacePathsFromOutput = async (output, cwd) => {
46
+ const candidates = parseWorkspacePathsFromText(output);
47
+ const resolved = [];
48
+ const seen = new Set();
49
+ for (const candidate of candidates) {
50
+ const expanded = expandHome(candidate);
51
+ const potentialRoots = new Set([expanded, extractWorkspaceRoot(expanded)]);
52
+ for (const potential of potentialRoots) {
53
+ const absolute = isAbsolutePath(potential) ? potential : resolve(cwd, potential);
54
+ if (!(await pathExists(absolute))) {
55
+ continue;
56
+ }
57
+ try {
58
+ const stats = await stat(absolute);
59
+ if (!stats.isDirectory()) {
60
+ continue;
61
+ }
62
+ if (seen.has(absolute)) {
63
+ continue;
64
+ }
65
+ seen.add(absolute);
66
+ resolved.push({ path: absolute, mtimeMs: stats.mtimeMs });
67
+ }
68
+ catch {
69
+ continue;
70
+ }
71
+ }
72
+ }
73
+ return resolved;
74
+ };
75
+ export const getDefaultSearchDirs = (cwd) => {
76
+ const dirs = new Set();
77
+ const home = os.homedir();
78
+ if (home) {
79
+ dirs.add(join(home, '.openclaw'));
80
+ dirs.add(join(home, '.config', 'openclaw'));
81
+ dirs.add(join(home, '.local', 'share', 'openclaw'));
82
+ }
83
+ if (cwd) {
84
+ dirs.add(resolve(cwd));
85
+ }
86
+ const appData = process.env.APPDATA;
87
+ const localAppData = process.env.LOCALAPPDATA;
88
+ const userProfile = process.env.USERPROFILE;
89
+ if (appData) {
90
+ dirs.add(join(appData, 'openclaw'));
91
+ }
92
+ if (localAppData) {
93
+ dirs.add(join(localAppData, 'openclaw'));
94
+ }
95
+ if (userProfile) {
96
+ dirs.add(join(userProfile, '.openclaw'));
97
+ }
98
+ return Array.from(dirs);
99
+ };
100
+ export const scanRecentWorkspaces = async (options) => {
101
+ const now = options.now ?? Date.now();
102
+ const windowMs = options.windowMs ?? WINDOW_MS;
103
+ const matches = [];
104
+ for (const dir of options.searchDirs) {
105
+ if (!(await pathExists(dir))) {
106
+ continue;
107
+ }
108
+ try {
109
+ const entries = await readdir(dir, { withFileTypes: true });
110
+ for (const entry of entries) {
111
+ if (!entry.isDirectory()) {
112
+ continue;
113
+ }
114
+ if (!entry.name.startsWith('workspace-')) {
115
+ continue;
116
+ }
117
+ const fullPath = join(dir, entry.name);
118
+ try {
119
+ const stats = await stat(fullPath);
120
+ if (now - stats.mtimeMs > windowMs) {
121
+ continue;
122
+ }
123
+ matches.push({ path: fullPath, mtimeMs: stats.mtimeMs });
124
+ }
125
+ catch {
126
+ continue;
127
+ }
128
+ }
129
+ }
130
+ catch {
131
+ continue;
132
+ }
133
+ }
134
+ matches.sort((a, b) => b.mtimeMs - a.mtimeMs);
135
+ const warnings = [];
136
+ if (matches.length > 1) {
137
+ warnings.push('Multiple workspaces found; using the most recent match.');
138
+ }
139
+ return { matches, warnings };
140
+ };
141
+ export const resolveWorkspace = async (options) => {
142
+ const warnings = [];
143
+ const candidates = [];
144
+ if (options.explicitPath) {
145
+ const explicit = resolve(expandHome(options.explicitPath));
146
+ if (!(await pathExists(explicit))) {
147
+ throw new Error(`Workspace path does not exist: ${explicit}`);
148
+ }
149
+ const stats = await stat(explicit);
150
+ if (!stats.isDirectory()) {
151
+ throw new Error(`Workspace path is not a directory: ${explicit}`);
152
+ }
153
+ return { workspace: explicit, source: 'explicit', warnings, candidates: [explicit] };
154
+ }
155
+ if (options.output) {
156
+ const combined = [options.output.stdout, options.output.stderr].filter(Boolean).join('\n');
157
+ const outputMatches = await resolveWorkspacePathsFromOutput(combined, options.cwd);
158
+ if (outputMatches.length > 0) {
159
+ candidates.push(...outputMatches.map((match) => match.path));
160
+ outputMatches.sort((a, b) => b.mtimeMs - a.mtimeMs);
161
+ if (outputMatches.length > 1) {
162
+ warnings.push('Multiple workspaces found in OpenClaw output; using the most recent.');
163
+ }
164
+ return {
165
+ workspace: outputMatches[0].path,
166
+ source: 'output',
167
+ warnings,
168
+ candidates,
169
+ };
170
+ }
171
+ }
172
+ const searchDirs = getDefaultSearchDirs(options.cwd);
173
+ const scan = await scanRecentWorkspaces({ searchDirs });
174
+ if (scan.matches.length > 0) {
175
+ candidates.push(...scan.matches.map((match) => match.path));
176
+ warnings.push(...scan.warnings);
177
+ return {
178
+ workspace: scan.matches[0].path,
179
+ source: 'scan',
180
+ warnings,
181
+ candidates,
182
+ };
183
+ }
184
+ throw new Error('Unable to locate the OpenClaw workspace. Re-run with --workspace to specify it manually.');
185
+ };
186
+ //# sourceMappingURL=workspace.js.map