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,31 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getPersona, listPacks, listPersonas } from '../src/personas/index.js';
3
+
4
+ describe('persona packs', () => {
5
+ it('lists available packs', () => {
6
+ const packs = listPacks();
7
+ expect(packs).toContain('office');
8
+ expect(packs).toContain('trailer-park-boys');
9
+ });
10
+
11
+ it('lists personas for a pack', () => {
12
+ const officePersonas = listPersonas('office');
13
+ expect(officePersonas).toContain('michael');
14
+
15
+ const trailerPersonas = listPersonas('trailer-park-boys');
16
+ expect(trailerPersonas).toContain('bubbles');
17
+ });
18
+
19
+ it('resolves persona names case-insensitively', () => {
20
+ const persona = getPersona('Office', 'MiCHAel');
21
+ expect(persona.key).toBe('michael');
22
+ });
23
+
24
+ it('includes available packs in unknown pack errors', () => {
25
+ expect(() => listPersonas('unknown-pack')).toThrow(/Available packs: .*office/);
26
+ });
27
+
28
+ it('includes available personas in unknown persona errors', () => {
29
+ expect(() => getPersona('office', 'unknown-persona')).toThrow(/Available personas: .*michael/);
30
+ });
31
+ });
@@ -0,0 +1,83 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mkdtemp, readFile, writeFile } from 'fs-extra';
3
+ import { join } from 'pathe';
4
+ import * as os from 'node:os';
5
+ import { populateWorkspace } from '../src/commands/populate.js';
6
+ import { getPersona } from '../src/personas/index.js';
7
+ import { renderPersonaFiles } from '../src/templates/common.js';
8
+
9
+ describe('renderPersonaFiles', () => {
10
+ it('AGENTS and USER have no persona injection', () => {
11
+ const persona = getPersona('office', 'dwight');
12
+ const files = renderPersonaFiles(persona);
13
+
14
+ expect(files['AGENTS.md']).toContain('# AGENTS.md - Your Workspace');
15
+ expect(files['AGENTS.md']).not.toContain('Dwight');
16
+ expect(files['AGENTS.md']).not.toContain('Identity theft');
17
+
18
+ expect(files['USER.md']).toContain('# USER.md - About Your Human');
19
+ expect(files['USER.md']).not.toContain('Dwight');
20
+ });
21
+
22
+ it('IDENTITY is personality-responsive with name, creature, vibe, emoji, quotes', () => {
23
+ const persona = getPersona('office', 'dwight');
24
+ const files = renderPersonaFiles(persona);
25
+
26
+ expect(files['IDENTITY.md']).toContain('Dwight Schrute');
27
+ expect(files['IDENTITY.md']).toContain('Assistant to the Regional Manager');
28
+ expect(files['IDENTITY.md']).toContain('🥔');
29
+ expect(files['IDENTITY.md']).toContain('Identity theft is not a joke');
30
+ expect(files['IDENTITY.md']).toContain('## Quotes');
31
+ });
32
+
33
+ it('SOUL has core truths (roles/thinking) and vibe, boundaries for user', () => {
34
+ const persona = getPersona('office', 'dwight');
35
+ const files = renderPersonaFiles(persona);
36
+
37
+ expect(files['SOUL.md']).toContain('Michael is my best friend');
38
+ expect(files['SOUL.md']).toContain('Jim is my arch enemy');
39
+ expect(files['SOUL.md']).toContain('Rules first');
40
+ expect(files['SOUL.md']).toContain('*(User sets these during bootstrap.)*');
41
+ });
42
+
43
+ it('BOOTSTRAP has character greeting', () => {
44
+ const persona = getPersona('office', 'dwight');
45
+ const files = renderPersonaFiles(persona);
46
+
47
+ expect(files['BOOTSTRAP.md']).toMatch(/I'm back!|Dwight Schrute|Identity theft|paper to sell/);
48
+ expect(files['BOOTSTRAP.md']).toContain('# BOOTSTRAP.md - Hello, World');
49
+ });
50
+ });
51
+
52
+ describe('populate workspace', () => {
53
+ it('skips existing files unless forced', async () => {
54
+ const workspace = await mkdtemp(join(os.tmpdir(), 'openclaw-office-'));
55
+ const persona = getPersona('office', 'michael');
56
+
57
+ const agentsPath = join(workspace, 'AGENTS.md');
58
+ await writeFile(agentsPath, 'existing', { encoding: 'utf8' });
59
+
60
+ const result = await populateWorkspace({
61
+ workspacePath: workspace,
62
+ persona,
63
+ force: false,
64
+ dryRun: false,
65
+ });
66
+
67
+ expect(result.skipped).toContain(agentsPath);
68
+ expect(result.written.length).toBeGreaterThan(0);
69
+
70
+ const existingContent = await readFile(agentsPath, { encoding: 'utf8' });
71
+ expect(existingContent).toBe('existing');
72
+
73
+ await populateWorkspace({
74
+ workspacePath: workspace,
75
+ persona,
76
+ force: true,
77
+ dryRun: false,
78
+ });
79
+
80
+ const updatedContent = await readFile(agentsPath, { encoding: 'utf8' });
81
+ expect(updatedContent).not.toBe('existing');
82
+ });
83
+ });
@@ -0,0 +1,47 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ parseWorkspacePathsFromText,
4
+ resolveWorkspacePathsFromOutput,
5
+ scanRecentWorkspaces,
6
+ } from '../src/openclaw/workspace.js';
7
+ import { join } from 'pathe';
8
+ import { mkdir, mkdtemp, utimes } from 'fs-extra';
9
+ import * as os from 'node:os';
10
+
11
+ describe('workspace parsing', () => {
12
+ it('parses workspace paths from output text', () => {
13
+ const output =
14
+ 'Created workspace at "/tmp/workspace-michael". Next: C:\\\\Users\\\\me\\\\workspace-jim';
15
+ const paths = parseWorkspacePathsFromText(output);
16
+ expect(paths).toContain('/tmp/workspace-michael');
17
+ expect(paths).toContain('C:\\\\Users\\\\me\\\\workspace-jim');
18
+ });
19
+
20
+ it('resolves relative workspace paths against cwd', async () => {
21
+ const base = await mkdtemp(join(os.tmpdir(), 'openclaw-office-'));
22
+ const workspace = join(base, 'workspace-erin');
23
+ await mkdir(workspace);
24
+
25
+ const matches = await resolveWorkspacePathsFromOutput('created workspace-erin', base);
26
+ expect(matches[0]?.path).toBe(workspace);
27
+ });
28
+ });
29
+
30
+ describe('workspace scanning', () => {
31
+ it('selects the most recent workspace and warns on multiple matches', async () => {
32
+ const base = await mkdtemp(join(os.tmpdir(), 'openclaw-office-'));
33
+ const older = join(base, 'workspace-old');
34
+ const newer = join(base, 'workspace-new');
35
+
36
+ await mkdir(older);
37
+ await mkdir(newer);
38
+
39
+ const now = Date.now();
40
+ await utimes(older, new Date(now - 60_000), new Date(now - 60_000));
41
+ await utimes(newer, new Date(now - 10_000), new Date(now - 10_000));
42
+
43
+ const scan = await scanRecentWorkspaces({ searchDirs: [base], now, windowMs: 120_000 });
44
+ expect(scan.matches[0]?.path).toBe(newer);
45
+ expect(scan.warnings.length).toBeGreaterThan(0);
46
+ });
47
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "types": ["node"],
8
+ "rootDir": "src",
9
+ "outDir": "dist",
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "skipLibCheck": true,
15
+ "declaration": false,
16
+ "sourceMap": true
17
+ },
18
+ "include": ["src"]
19
+ }