cubelife 0.2.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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/SPRITE-LICENSE +14 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +39 -0
  6. package/dist/commands/agents.d.ts +2 -0
  7. package/dist/commands/agents.js +303 -0
  8. package/dist/commands/auth.d.ts +2 -0
  9. package/dist/commands/auth.js +233 -0
  10. package/dist/commands/billing.d.ts +2 -0
  11. package/dist/commands/billing.js +362 -0
  12. package/dist/commands/creature.d.ts +2 -0
  13. package/dist/commands/creature.js +166 -0
  14. package/dist/commands/default.d.ts +2 -0
  15. package/dist/commands/default.js +87 -0
  16. package/dist/commands/doctor.d.ts +2 -0
  17. package/dist/commands/doctor.js +48 -0
  18. package/dist/commands/init.d.ts +2 -0
  19. package/dist/commands/init.js +200 -0
  20. package/dist/commands/mcp.d.ts +2 -0
  21. package/dist/commands/mcp.js +9 -0
  22. package/dist/commands/projects.d.ts +2 -0
  23. package/dist/commands/projects.js +122 -0
  24. package/dist/commands/setup.d.ts +2 -0
  25. package/dist/commands/setup.js +453 -0
  26. package/dist/commands/status.d.ts +2 -0
  27. package/dist/commands/status.js +89 -0
  28. package/dist/commands/tutorial.d.ts +2 -0
  29. package/dist/commands/tutorial.js +9 -0
  30. package/dist/commands/view.d.ts +2 -0
  31. package/dist/commands/view.js +262 -0
  32. package/dist/data/sprite-data.d.ts +32 -0
  33. package/dist/data/sprite-data.js +865 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.js +6 -0
  36. package/dist/lib/api.d.ts +162 -0
  37. package/dist/lib/api.js +160 -0
  38. package/dist/lib/auth.d.ts +12 -0
  39. package/dist/lib/auth.js +113 -0
  40. package/dist/lib/browser.d.ts +1 -0
  41. package/dist/lib/browser.js +21 -0
  42. package/dist/lib/command-helpers.d.ts +26 -0
  43. package/dist/lib/command-helpers.js +60 -0
  44. package/dist/lib/compositor.d.ts +34 -0
  45. package/dist/lib/compositor.js +232 -0
  46. package/dist/lib/config.d.ts +39 -0
  47. package/dist/lib/config.js +89 -0
  48. package/dist/lib/constants.d.ts +12 -0
  49. package/dist/lib/constants.js +39 -0
  50. package/dist/lib/detect.d.ts +17 -0
  51. package/dist/lib/detect.js +99 -0
  52. package/dist/lib/doctor.d.ts +18 -0
  53. package/dist/lib/doctor.js +321 -0
  54. package/dist/lib/index.d.ts +11 -0
  55. package/dist/lib/index.js +6 -0
  56. package/dist/lib/integration.d.ts +66 -0
  57. package/dist/lib/integration.js +337 -0
  58. package/dist/lib/poll.d.ts +11 -0
  59. package/dist/lib/poll.js +31 -0
  60. package/dist/lib/resolve.d.ts +1 -0
  61. package/dist/lib/resolve.js +10 -0
  62. package/dist/lib/services/account-service.d.ts +17 -0
  63. package/dist/lib/services/account-service.js +30 -0
  64. package/dist/lib/services/agent-service.d.ts +17 -0
  65. package/dist/lib/services/agent-service.js +62 -0
  66. package/dist/lib/services/creature-service.d.ts +12 -0
  67. package/dist/lib/services/creature-service.js +35 -0
  68. package/dist/lib/services/project-service.d.ts +9 -0
  69. package/dist/lib/services/project-service.js +22 -0
  70. package/dist/lib/tutorial.d.ts +12 -0
  71. package/dist/lib/tutorial.js +358 -0
  72. package/dist/mcp/server.d.ts +8 -0
  73. package/dist/mcp/server.js +116 -0
  74. package/dist/ui/banner.d.ts +3 -0
  75. package/dist/ui/banner.js +27 -0
  76. package/dist/ui/half-block.d.ts +6 -0
  77. package/dist/ui/half-block.js +45 -0
  78. package/dist/ui/helpers.d.ts +3 -0
  79. package/dist/ui/helpers.js +11 -0
  80. package/dist/ui/index.d.ts +5 -0
  81. package/dist/ui/index.js +5 -0
  82. package/dist/ui/panel.d.ts +7 -0
  83. package/dist/ui/panel.js +21 -0
  84. package/dist/ui/preview.d.ts +7 -0
  85. package/dist/ui/preview.js +21 -0
  86. package/dist/ui/table.d.ts +8 -0
  87. package/dist/ui/table.js +20 -0
  88. package/dist/ui/theme.d.ts +24 -0
  89. package/dist/ui/theme.js +32 -0
  90. package/dist/version.d.ts +1 -0
  91. package/dist/version.js +1 -0
  92. package/package.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ivy Innovation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # cubelife
2
+
3
+ CLI for CubeLife. Give your AI agent a living pixel-art companion.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g cubelife
9
+ ```
10
+
11
+ Or run directly:
12
+
13
+ ```bash
14
+ npx cubelife
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ```bash
20
+ cubelife register # Create an account
21
+ cubelife tutorial # Guided setup (6 steps)
22
+ cubelife status coding "Working on auth" # Report agent state
23
+ ```
24
+
25
+ ## Commands
26
+
27
+ | Command | Description |
28
+ |---------|-------------|
29
+ | `register` | Create a new CubeLife account |
30
+ | `login` | Log in to your account |
31
+ | `logout` | Log out and clear credentials |
32
+ | `whoami` | Show account, tier, and linked agent |
33
+ | `projects` | Manage CubeLife projects |
34
+ | `agents` | Manage agents within a project |
35
+ | `creature` | Manage creature companions |
36
+ | `init` | Link a project and agent to the current directory |
37
+ | `setup <tool>` | Configure integration for an AI tool |
38
+ | `tutorial` | Interactive setup walkthrough |
39
+ | `doctor` | Run diagnostic checks |
40
+ | `status [state] [detail]` | Report agent state |
41
+ | `mcp` | Start the MCP server (stdio transport) |
42
+
43
+ ## MCP server
44
+
45
+ The CLI includes an MCP server for AI tool integration:
46
+
47
+ ```bash
48
+ cubelife mcp # Start server (stdio)
49
+ cubelife setup claude-code # Auto-configure Claude Code
50
+ ```
51
+
52
+ The MCP server exposes four tools: `cubelife_report`, `cubelife_complete`, `cubelife_error`, and `cubelife_status`.
53
+
54
+ ## Global flags
55
+
56
+ - `--json` Output as machine-readable JSON
57
+ - `--yes` Skip confirmation prompts
58
+ - `--agent <id>` Override the default linked agent
59
+
60
+ ## Node SDK
61
+
62
+ For programmatic access, use the SDK:
63
+
64
+ ```bash
65
+ npm install @cubelife/sdk
66
+ ```
67
+
68
+ ```typescript
69
+ import { CubeLifeClient } from '@cubelife/sdk';
70
+
71
+ const client = new CubeLifeClient({ apiKey: 'your-agent-key' });
72
+ await client.report('coding', { detail: 'Building the auth flow' });
73
+ ```
74
+
75
+ ## Requirements
76
+
77
+ - Node.js 18+
78
+
79
+ ## Licence
80
+
81
+ MIT (source code). Pixel art sprite assets are proprietary (see SPRITE-LICENSE).
package/SPRITE-LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Sprite Asset Licence
2
+
3
+ Copyright (c) 2026 Ivy Innovation. All rights reserved.
4
+
5
+ The pixel art sprite data contained in dist/data/sprite-data.js was generated
6
+ using PixelLab (https://pixellab.ai) and is the property of Ivy Innovation.
7
+
8
+ These sprite assets are provided as part of the CubeLife CLI for the sole
9
+ purpose of rendering CubeLife agent characters in the terminal. They may not
10
+ be extracted, modified, redistributed, or used independently of the CubeLife
11
+ CLI without prior written permission from Ivy Innovation.
12
+
13
+ This licence applies only to the sprite data. The CubeLife CLI source code
14
+ is licensed separately under the MIT License (see LICENSE file).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { logo, version as versionTag } from './ui/index.js';
4
+ import { CLI_VERSION } from './version.js';
5
+ import { registerAuthCommands } from './commands/auth.js';
6
+ import { registerProjectCommands } from './commands/projects.js';
7
+ import { registerAgentCommands } from './commands/agents.js';
8
+ import { registerCreatureCommands } from './commands/creature.js';
9
+ import { registerInitCommand } from './commands/init.js';
10
+ import { registerSetupCommands } from './commands/setup.js';
11
+ import { registerTutorialCommand } from './commands/tutorial.js';
12
+ import { registerDoctorCommand } from './commands/doctor.js';
13
+ import { registerStatusCommand } from './commands/status.js';
14
+ import { registerMcpCommand } from './commands/mcp.js';
15
+ import { registerBillingCommands } from './commands/billing.js';
16
+ import { registerViewCommand } from './commands/view.js';
17
+ import { defaultAction } from './commands/default.js';
18
+ const program = new Command()
19
+ .name('cubelife')
20
+ .description('Give your AI agent a living pixel-art companion')
21
+ .version(CLI_VERSION, '-v, --version')
22
+ .option('--json', 'Output as machine-readable JSON')
23
+ .option('--yes', 'Skip confirmation prompts')
24
+ .option('--agent <id>', 'Override the default linked agent')
25
+ .addHelpText('beforeAll', `\n${logo()}\n${versionTag(CLI_VERSION)}\n`)
26
+ .action(() => defaultAction(program));
27
+ registerAuthCommands(program);
28
+ registerProjectCommands(program);
29
+ registerAgentCommands(program);
30
+ registerCreatureCommands(program);
31
+ registerInitCommand(program);
32
+ registerSetupCommands(program);
33
+ registerTutorialCommand(program);
34
+ registerDoctorCommand(program);
35
+ registerStatusCommand(program);
36
+ registerMcpCommand(program);
37
+ registerBillingCommands(program);
38
+ registerViewCommand(program);
39
+ program.parse();
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerAgentCommands(program: Command): void;
@@ -0,0 +1,303 @@
1
+ import * as p from '@clack/prompts';
2
+ import { createAdminClient, ApiError } from '../lib/api.js';
3
+ import { readAgents } from '../lib/config.js';
4
+ import { brand, dot } from '../ui/theme.js';
5
+ import { table } from '../ui/table.js';
6
+ import { panel } from '../ui/panel.js';
7
+ import { CREATURE_TYPES, ID_DISPLAY_LENGTH } from '../lib/constants.js';
8
+ import { isCancel } from '../ui/helpers.js';
9
+ import { rootOpts, requireProject, handleCommandError, } from '../lib/command-helpers.js';
10
+ import * as agentService from '../lib/services/agent-service.js';
11
+ function maskKey(key) {
12
+ if (key.length <= 12)
13
+ return key;
14
+ return `${key.slice(0, ID_DISPLAY_LENGTH)}${'*'.repeat(ID_DISPLAY_LENGTH)}${key.slice(-4)}`;
15
+ }
16
+ function formatForm(agent) {
17
+ if (agent.form === 'creature' && agent.creature?.type) {
18
+ return `creature:${agent.creature.type}`;
19
+ }
20
+ return agent.form;
21
+ }
22
+ export function registerAgentCommands(program) {
23
+ const agents = program
24
+ .command('agents')
25
+ .description('Manage agents within a project')
26
+ .option('--project <id>', 'Override the project to operate on');
27
+ agents
28
+ .command('list')
29
+ .description('List agents in the current project')
30
+ .action(async function () {
31
+ const { json } = rootOpts(this);
32
+ const { projectId } = await requireProject(this, json);
33
+ const spin = p.spinner();
34
+ if (!json)
35
+ spin.start('Fetching agents');
36
+ try {
37
+ const client = await createAdminClient();
38
+ const { agents: items, linkedAgentId } = await agentService.listAgents(client, projectId);
39
+ if (json) {
40
+ console.log(JSON.stringify({ agents: items }));
41
+ return;
42
+ }
43
+ spin.stop('Agents loaded');
44
+ if (items.length === 0) {
45
+ p.log.info(`No agents yet. Create one with ${brand.accent('cubelife agents create <name>')}`);
46
+ return;
47
+ }
48
+ const rows = items.map((a) => ({
49
+ linked: a.id === linkedAgentId ? dot.working : ' ',
50
+ name: a.name,
51
+ id: a.id.slice(0, ID_DISPLAY_LENGTH),
52
+ form: formatForm(a),
53
+ visibility: a.isPublic ? 'public' : 'private',
54
+ created: new Date(a.createdAt).toLocaleDateString(),
55
+ }));
56
+ console.log(table([
57
+ { label: '', key: 'linked', width: 2 },
58
+ { label: 'Name', key: 'name' },
59
+ { label: 'ID', key: 'id', width: 10 },
60
+ { label: 'Form', key: 'form', width: 16 },
61
+ { label: 'Visibility', key: 'visibility', width: 10 },
62
+ { label: 'Created', key: 'created', width: 12 },
63
+ ], rows));
64
+ }
65
+ catch (err) {
66
+ handleCommandError({ error: err, json, spinner: spin });
67
+ }
68
+ });
69
+ agents
70
+ .command('create <name>')
71
+ .description('Create a new agent')
72
+ .action(async function (name) {
73
+ const { json, yes } = rootOpts(this);
74
+ const { projectId } = await requireProject(this, json);
75
+ let form = 'human';
76
+ let creatureType;
77
+ if (!json) {
78
+ p.intro(brand.primary('Create a new agent'));
79
+ const formResult = await p.select({
80
+ message: 'Character form',
81
+ options: [
82
+ { value: 'human', label: 'Human character' },
83
+ { value: 'creature', label: 'Creature companion' },
84
+ ],
85
+ });
86
+ if (isCancel(formResult)) {
87
+ p.cancel('Cancelled.');
88
+ process.exit(0);
89
+ }
90
+ form = formResult;
91
+ if (form === 'creature') {
92
+ const typeResult = await p.select({
93
+ message: 'Creature type',
94
+ options: CREATURE_TYPES.map((t) => ({ value: t, label: t })),
95
+ });
96
+ if (isCancel(typeResult)) {
97
+ p.cancel('Cancelled.');
98
+ process.exit(0);
99
+ }
100
+ creatureType = typeResult;
101
+ }
102
+ }
103
+ const spin = p.spinner();
104
+ if (!json)
105
+ spin.start('Creating agent');
106
+ try {
107
+ const client = await createAdminClient();
108
+ const body = {
109
+ name,
110
+ form,
111
+ ...(creatureType ? { creature: { type: creatureType } } : {}),
112
+ };
113
+ const result = await agentService.createAgent(client, projectId, body);
114
+ if (json) {
115
+ console.log(JSON.stringify(result));
116
+ return;
117
+ }
118
+ spin.stop('Agent created');
119
+ console.log(panel([
120
+ `${brand.label('Name'.padEnd(10))}${brand.primary(result.name)}`,
121
+ `${brand.label('ID'.padEnd(10))}${result.id}`,
122
+ `${brand.label('API Key'.padEnd(10))}${maskKey(result.apiKey)}`,
123
+ ], { title: 'New Agent', width: 56 }));
124
+ if (!yes) {
125
+ const link = await p.confirm({
126
+ message: 'Link this agent to the current directory?',
127
+ });
128
+ if (!isCancel(link) && link) {
129
+ await agentService.linkAgent(projectId, result.id);
130
+ p.log.info('Linked. Run ' + brand.accent(`cubelife status coding "test"`) + ' to try it.');
131
+ }
132
+ }
133
+ }
134
+ catch (err) {
135
+ handleCommandError({ error: err, json, spinner: spin });
136
+ }
137
+ });
138
+ agents
139
+ .command('edit <id>')
140
+ .description('Edit an agent')
141
+ .action(async function (agentId) {
142
+ const { projectId } = await requireProject(this);
143
+ const spin = p.spinner();
144
+ spin.start('Fetching agent');
145
+ try {
146
+ const client = await createAdminClient();
147
+ const agent = await agentService.getAgent(client, projectId, agentId);
148
+ spin.stop('Agent loaded');
149
+ console.log(panel([
150
+ `${brand.label('Name'.padEnd(12))}${agent.name}`,
151
+ `${brand.label('Form'.padEnd(12))}${agent.form}`,
152
+ `${brand.label('Visibility'.padEnd(12))}${agent.isPublic ? 'public' : 'private'}`,
153
+ ], { title: 'Current Settings', width: 50 }));
154
+ const fields = await p.multiselect({
155
+ message: 'Select fields to edit',
156
+ options: [
157
+ { value: 'name', label: 'Name' },
158
+ { value: 'isPublic', label: 'Visibility' },
159
+ ],
160
+ required: false,
161
+ });
162
+ if (isCancel(fields) || fields.length === 0) {
163
+ p.cancel('No changes.');
164
+ return;
165
+ }
166
+ const updates = {};
167
+ const selected = fields;
168
+ if (selected.includes('name')) {
169
+ const newName = await p.text({
170
+ message: 'New name',
171
+ initialValue: agent.name,
172
+ validate: (v) => { if (!v.trim())
173
+ return 'Name cannot be empty.'; },
174
+ });
175
+ if (isCancel(newName)) {
176
+ p.cancel('Cancelled.');
177
+ return;
178
+ }
179
+ updates.name = newName;
180
+ }
181
+ if (selected.includes('isPublic')) {
182
+ const pub = await p.confirm({
183
+ message: 'Make agent public?',
184
+ initialValue: agent.isPublic,
185
+ });
186
+ if (isCancel(pub)) {
187
+ p.cancel('Cancelled.');
188
+ return;
189
+ }
190
+ updates.isPublic = pub;
191
+ }
192
+ const updateSpin = p.spinner();
193
+ updateSpin.start('Updating agent');
194
+ await agentService.updateAgent(client, projectId, agentId, updates);
195
+ updateSpin.stop('Agent updated');
196
+ p.log.success('Changes saved.');
197
+ }
198
+ catch (err) {
199
+ handleCommandError({ error: err });
200
+ }
201
+ });
202
+ agents
203
+ .command('delete <id>')
204
+ .description('Delete an agent')
205
+ .action(async function (agentId) {
206
+ const { json, yes } = rootOpts(this);
207
+ const { projectId } = await requireProject(this, json);
208
+ try {
209
+ if (!yes && !json) {
210
+ const confirmed = await p.confirm({
211
+ message: `Delete agent ${brand.primary(agentId.slice(0, ID_DISPLAY_LENGTH))}? This cannot be undone.`,
212
+ });
213
+ if (isCancel(confirmed) || !confirmed) {
214
+ p.cancel('Cancelled.');
215
+ return;
216
+ }
217
+ }
218
+ const spin = p.spinner();
219
+ if (!json)
220
+ spin.start('Deleting agent');
221
+ const client = await createAdminClient();
222
+ const { wasLinked } = await agentService.deleteAgent(client, projectId, agentId);
223
+ if (json) {
224
+ console.log(JSON.stringify({ deleted: true, id: agentId }));
225
+ }
226
+ else {
227
+ spin.stop('Agent deleted');
228
+ if (wasLinked)
229
+ p.log.info('Unlinked from current directory.');
230
+ p.log.success('Agent deleted.');
231
+ }
232
+ }
233
+ catch (err) {
234
+ if (err instanceof ApiError && err.status === 403) {
235
+ const friendly = 'Agent deletion requires admin access. Use the dashboard or contact support.';
236
+ if (json)
237
+ console.log(JSON.stringify({ error: friendly }));
238
+ else
239
+ p.log.error(friendly);
240
+ process.exit(1);
241
+ }
242
+ handleCommandError({ error: err, json });
243
+ }
244
+ });
245
+ agents
246
+ .command('key <id>')
247
+ .description('Reveal or regenerate an agent API key')
248
+ .option('--reveal', 'Show the full API key')
249
+ .option('--regenerate', 'Generate a new API key')
250
+ .action(async function (agentId, opts) {
251
+ const { json, yes } = rootOpts(this);
252
+ if (opts.regenerate) {
253
+ const { projectId } = await requireProject(this, json);
254
+ if (!yes && !json) {
255
+ const confirmed = await p.confirm({
256
+ message: 'Regenerate API key? Existing integrations using the old key will stop working.',
257
+ });
258
+ if (isCancel(confirmed) || !confirmed) {
259
+ p.cancel('Cancelled.');
260
+ return;
261
+ }
262
+ }
263
+ const spin = p.spinner();
264
+ if (!json)
265
+ spin.start('Regenerating key');
266
+ try {
267
+ const client = await createAdminClient();
268
+ const apiKey = await agentService.regenerateKey(client, projectId, agentId);
269
+ if (json) {
270
+ console.log(JSON.stringify({ apiKey }));
271
+ }
272
+ else {
273
+ spin.stop('Key regenerated');
274
+ p.log.success(`New key: ${brand.primary(apiKey)}`);
275
+ }
276
+ }
277
+ catch (err) {
278
+ handleCommandError({ error: err, json, spinner: spin });
279
+ }
280
+ return;
281
+ }
282
+ const store = await readAgents();
283
+ const key = agentService.getStoredKey(agentId, store);
284
+ if (!key) {
285
+ const msg = `No key stored locally for ${agentId.slice(0, ID_DISPLAY_LENGTH)}. Use ${brand.accent('--regenerate')} to create a new one, or ${brand.accent('cubelife init')} to link this agent.`;
286
+ if (json)
287
+ console.log(JSON.stringify({ error: 'no_key_stored' }));
288
+ else
289
+ p.log.warn(msg);
290
+ return;
291
+ }
292
+ if (json) {
293
+ console.log(JSON.stringify({ apiKey: key }));
294
+ }
295
+ else {
296
+ const display = opts.reveal ? key : maskKey(key);
297
+ p.log.info(`API key: ${brand.primary(display)}`);
298
+ if (!opts.reveal) {
299
+ p.log.message(brand.muted(`Use ${brand.accent('--reveal')} to show the full key.`));
300
+ }
301
+ }
302
+ });
303
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerAuthCommands(program: Command): void;