trucontext 0.8.6 → 0.9.1

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.
package/bin/cli.js CHANGED
@@ -12,15 +12,19 @@ import { initCommand } from '../src/commands/init.js';
12
12
  import { ingestCommand } from '../src/commands/ingest.js';
13
13
  import { queryCommand } from '../src/commands/query.js';
14
14
  import { recallCommand } from '../src/commands/recall.js';
15
- import { contextsListCommand, contextsCreateCommand, contextsDeleteCommand } from '../src/commands/contexts.js';
16
15
  import { schemaShowCommand, schemaGenerateCommand, schemaSetAuthorshipCommand } from '../src/commands/schema.js';
17
16
  import { entitiesListCommand, entitiesGetCommand, entitiesCreateCommand, entitiesUpdateCommand, entitiesDeleteCommand, entitiesEdgesCommand } from '../src/commands/entities.js';
18
- import { recipesListCommand, recipesGetCommand, recipesCreateCommand, recipesDeleteCommand } from '../src/commands/recipes.js';
19
- import { relationshipTypesListCommand } from '../src/commands/relationship-types.js';
17
+ import { recipesListCommand, recipesGetCommand, recipesCreateCommand, recipesUpdateCommand, recipesDeleteCommand } from '../src/commands/recipes.js';
18
+ import { relationshipTypesListCommand, relationshipTypesCreateCommand, relationshipTypesDeleteCommand } from '../src/commands/relationship-types.js';
20
19
  import { docsListCommand, docsShowCommand } from '../src/commands/docs.js';
21
20
  import { registerRootsCommand } from '../src/commands/roots.js';
22
21
  import { registerCuriosityCommand } from '../src/commands/curiosity.js';
23
22
  import { usageCommand, usageHistoryCommand, plansCommand } from '../src/commands/billing.js';
23
+ import { keysListCommand, keysCreateCommand, keysRevokeCommand } from '../src/commands/keys.js';
24
+ import { graphOverviewCommand, graphSearchCommand, graphNodeCommand, graphNeighborsCommand } from '../src/commands/graph.js';
25
+ import { mindPresenceCommand, mindThoughtsCommand } from '../src/commands/mind.js';
26
+ import { settingsShowCommand, settingsUpdateCommand } from '../src/commands/settings.js';
27
+ import { provisionCommand } from '../src/commands/agents.js';
24
28
 
25
29
  program
26
30
  .name('trucontext')
@@ -83,15 +87,6 @@ program.command('recall <query>')
83
87
  .option('-d, --depth <n>', 'Graph expansion depth', '2')
84
88
  .action(recallCommand);
85
89
 
86
- // Contexts (deprecated — use entities with contexts array)
87
- const contexts = program.command('contexts').description('Manage contexts (deprecated — use entities)').action(function() { this.help(); });
88
- contexts.command('list').description('List contexts').action(contextsListCommand);
89
- contexts.command('create <name>')
90
- .description('Create a context')
91
- .option('--metadata <json>', 'JSON metadata object')
92
- .action(contextsCreateCommand);
93
- contexts.command('delete <id>').description('Delete a context').action(contextsDeleteCommand);
94
-
95
90
  // Entities
96
91
  const entities = program.command('entities').description('Manage entities').action(function() { this.help(); });
97
92
  entities.command('list')
@@ -152,15 +147,31 @@ recipes.command('create')
152
147
  .option('--decay-profile <text>', 'Decay profile description')
153
148
  .option('--confidence-bias <text>', 'Confidence bias description')
154
149
  .action(recipesCreateCommand);
150
+ recipes.command('update <recipeId>')
151
+ .description('Update a custom recipe')
152
+ .option('--name <name>', 'New name')
153
+ .option('--file <path>', 'JSON file with updated interpretation')
154
+ .option('--purpose <text>', 'Update purpose text')
155
+ .action(recipesUpdateCommand);
155
156
  recipes.command('delete <recipeId>')
156
157
  .description('Delete a recipe')
157
158
  .action(recipesDeleteCommand);
158
159
 
159
160
  // Relationship Types
160
- program.command('relationship-types')
161
+ const relTypes = program.command('relationship-types').description('Manage relationship types').action(relationshipTypesListCommand);
162
+ relTypes.command('list')
161
163
  .description('List relationship types')
162
164
  .option('--category <cat>', 'Filter by category')
163
165
  .action(relationshipTypesListCommand);
166
+ relTypes.command('create')
167
+ .description('Create a custom relationship type')
168
+ .requiredOption('--type <type>', 'Relationship type (UPPER_SNAKE_CASE)')
169
+ .requiredOption('--description <text>', 'Description of the relationship')
170
+ .option('--category <cat>', 'Category (default: Custom)')
171
+ .action(relationshipTypesCreateCommand);
172
+ relTypes.command('delete <type>')
173
+ .description('Delete a custom relationship type')
174
+ .action(relationshipTypesDeleteCommand);
164
175
 
165
176
  // Schema
166
177
  const schema = program.command('schema').description('Manage app schema').action(function() { this.help(); });
@@ -196,8 +207,75 @@ billing.command('plans')
196
207
  .description('View available plans')
197
208
  .action(plansCommand);
198
209
 
210
+ // API Keys
211
+ const keys = program.command('keys').description('Manage API keys').action(keysListCommand);
212
+ keys.command('list').description('List API keys').action(keysListCommand);
213
+ keys.command('create')
214
+ .description('Create a new API key')
215
+ .requiredOption('--name <name>', 'Key name')
216
+ .action(keysCreateCommand);
217
+ keys.command('revoke <keyId>')
218
+ .description('Revoke an API key')
219
+ .action(keysRevokeCommand);
220
+
221
+ // Graph Explorer
222
+ const graph = program.command('graph').description('Explore the knowledge graph').action(function() { this.help(); });
223
+ graph.command('overview')
224
+ .description('Graph overview (node/edge counts)')
225
+ .option('-l, --limit <n>', 'Max nodes to return', '100')
226
+ .action(graphOverviewCommand);
227
+ graph.command('search <query>')
228
+ .description('Search graph by keyword')
229
+ .option('--type <type>', 'Filter by node type')
230
+ .option('-l, --limit <n>', 'Max results', '50')
231
+ .action(graphSearchCommand);
232
+ graph.command('node <nodeId>')
233
+ .description('Get detailed node info')
234
+ .action(graphNodeCommand);
235
+ graph.command('neighbors <nodeId>')
236
+ .description('Get node neighbors')
237
+ .option('-l, --limit <n>', 'Max neighbors', '20')
238
+ .action(graphNeighborsCommand);
239
+
240
+ // Mind
241
+ const mind = program.command('mind').description('View mind state and thoughts').action(function() { this.help(); });
242
+ mind.command('presence')
243
+ .description('View consciousness state')
244
+ .action(mindPresenceCommand);
245
+ mind.command('thoughts')
246
+ .description('View dream diary')
247
+ .option('-l, --limit <n>', 'Max entries', '20')
248
+ .option('-m, --mode <mode>', 'Filter by dream type')
249
+ .action(mindThoughtsCommand);
250
+
251
+ // Settings
252
+ const settings = program.command('settings').description('Manage app settings').action(settingsShowCommand);
253
+ settings.command('show').description('Show current settings').action(settingsShowCommand);
254
+ settings.command('update')
255
+ .description('Update settings')
256
+ .option('--json <json>', 'JSON object with settings to update')
257
+ .option('--key <key>', 'Setting key')
258
+ .option('--value <value>', 'Setting value')
259
+ .action(settingsUpdateCommand);
260
+
199
261
  // Root nodes + Curiosity (registered as subcommand groups)
200
262
  registerRootsCommand(program);
201
263
  registerCuriosityCommand(program);
202
264
 
265
+ // Agent provision
266
+ const agents = program.command('agents').description('Agent provisioning');
267
+ agents.command('provision')
268
+ .description('Provision an agent (create or update root node, ingest content, generate memory briefing)')
269
+ .requiredOption('--agent-id <id>', 'Agent identifier')
270
+ .requiredOption('--name <name>', 'Agent display name')
271
+ .requiredOption('--role <role>', 'Agent role description')
272
+ .option('--user-root <root>', 'User root node ID')
273
+ .requiredOption('--soul <fileOrText>', 'Path to SOUL.md or inline text')
274
+ .option('--agents <fileOrText>', 'Path to AGENTS.md or inline text')
275
+ .option('--identity <fileOrText>', 'Path to IDENTITY.md or inline text')
276
+ .option('--memory <files...>', 'Path(s) to memory files')
277
+ .option('--recipe <recipeId>', 'Recipe hint (skip inference)')
278
+ .option('--dry-run', 'Preview what would happen without doing it')
279
+ .action(provisionCommand);
280
+
203
281
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trucontext",
3
- "version": "0.8.6",
3
+ "version": "0.9.1",
4
4
  "description": "TruContext CLI — contextual memory for AI applications",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,75 @@
1
+ // commands/agents.js — Agent Provision CLI
2
+ //
3
+ // trucontext agents provision --agent-id <id> --name <name> --role <role> \
4
+ // --user-root <root> --soul <file> [--agents <file>] [--identity <file>] \
5
+ // [--memory <file>...] [--recipe <recipeId>] [--dry-run]
6
+
7
+ import { readFileSync, existsSync } from 'fs';
8
+ import { dataPlane } from '../client.js';
9
+ import { getConfig } from '../config.js';
10
+
11
+ export async function provisionCommand(options) {
12
+ const config = getConfig();
13
+ if (!config.activeApp) {
14
+ console.error('No active app. Run: trucontext use <app>');
15
+ process.exit(1);
16
+ }
17
+
18
+ const { agentId, name, role, userRoot, soul, agents, identity, memory, recipe, dryRun } = options;
19
+
20
+ // Read file contents
21
+ const soulText = readFileOrText(soul);
22
+ if (!soulText) {
23
+ console.error('--soul is required (file path or inline text)');
24
+ process.exit(1);
25
+ }
26
+
27
+ const content = {
28
+ soul: soulText,
29
+ agents: agents ? readFileOrText(agents) : undefined,
30
+ identity: identity ? readFileOrText(identity) : undefined,
31
+ memory: (memory || []).map(m => readFileOrText(m)).filter(Boolean),
32
+ };
33
+
34
+ const body = {
35
+ agent: { id: agentId, name, role },
36
+ user: { root_node: userRoot || config.activeRoot || 'dustin' },
37
+ content,
38
+ hints: recipe ? { recipe } : {},
39
+ options: { dry_run: dryRun || false },
40
+ };
41
+
42
+ try {
43
+ const res = await dataPlane('POST', '/v1/agents/provision', body);
44
+ const data = res.data;
45
+
46
+ if (dryRun) {
47
+ console.log(`Dry run — would ${data.status === 'would_create' ? 'create' : 'update'}`);
48
+ console.log(` Agent root: ${data.agent_root}`);
49
+ console.log(` User root: ${data.user_root}`);
50
+ console.log(` Changes: ${Object.entries(data.changes || {}).filter(([,v]) => v).map(([k]) => k).join(', ') || 'none'}`);
51
+ return;
52
+ }
53
+
54
+ console.log(`${data.status === 'created' ? 'Provisioned' : 'Updated'}: ${data.agent_root}`);
55
+ console.log(` Recipe: ${data.recipe_id}`);
56
+ console.log(` Recipe inference: ${data.recipe_inference}`);
57
+ console.log(` Content ingested: soul=${data.content_ingested?.soul}, agents=${data.content_ingested?.agents}, identity=${data.content_ingested?.identity}, memory=${data.content_ingested?.memory_files}`);
58
+
59
+ if (data.prompt_fragment) {
60
+ console.log(`\n--- Prompt Fragment (${data.prompt_fragment.length} chars) ---`);
61
+ console.log(data.prompt_fragment);
62
+ }
63
+ } catch (err) {
64
+ console.error('Failed:', err.message);
65
+ process.exit(1);
66
+ }
67
+ }
68
+
69
+ function readFileOrText(input) {
70
+ if (!input) return null;
71
+ if (existsSync(input)) {
72
+ return readFileSync(input, 'utf8');
73
+ }
74
+ return input;
75
+ }
@@ -4,7 +4,7 @@
4
4
  // Curiosity analysis is always scoped to a root node's ego network.
5
5
 
6
6
  import chalk from 'chalk';
7
- import { controlPlane } from '../client.js';
7
+ import { dataPlane } from '../client.js';
8
8
  import { getConfig, getActiveApp } from '../config.js';
9
9
 
10
10
  export function registerCuriosityCommand(program) {
@@ -30,7 +30,7 @@ export function registerCuriosityCommand(program) {
30
30
  }
31
31
 
32
32
  try {
33
- const res = await controlPlane('GET', `/apps/${appId}/mind/curiosity?root_id=${rootId}&status=${opts.status}&limit=${opts.limit}`);
33
+ const res = await dataPlane('GET', `/v1/curiosity?root_id=${rootId}&status=${opts.status}&limit=${opts.limit}`);
34
34
  const packets = res.data?.packets || [];
35
35
 
36
36
  if (packets.length === 0) {
@@ -69,7 +69,7 @@ export function registerCuriosityCommand(program) {
69
69
 
70
70
  try {
71
71
  console.log(chalk.dim(`Triggering curiosity analysis for root: ${rootId}...`));
72
- await controlPlane('POST', `/apps/${appId}/mind/curiosity/trigger`, {
72
+ await dataPlane('POST', `/v1/curiosity`, {
73
73
  root_id: rootId,
74
74
  });
75
75
  console.log(chalk.green(`Curiosity analysis started for root: ${rootId}`));
@@ -87,7 +87,7 @@ export function registerCuriosityCommand(program) {
87
87
  .action(async (packetId) => {
88
88
  const appId = getActiveApp();
89
89
  try {
90
- await controlPlane('PUT', `/apps/${appId}/mind/curiosity/${packetId}/dismiss`);
90
+ await dataPlane('PUT', `/v1/curiosity/${packetId}/dismiss`);
91
91
  console.log(chalk.green(`Dismissed: ${packetId}`));
92
92
  } catch (err) {
93
93
  console.error(chalk.red('Failed:'), err.response?.data?.error || err.message);
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { controlPlane } from '../client.js';
2
+ import { dataPlane } from '../client.js';
3
3
  import { getActiveApp } from '../config.js';
4
4
 
5
5
  // Fields stored on the node but not user-declared properties
@@ -46,9 +46,9 @@ export async function entitiesListCommand(options) {
46
46
  if (options.scope) params.set('scope', options.scope);
47
47
  if (options.limit) params.set('limit', options.limit);
48
48
  const qs = params.toString();
49
- const path = `/apps/${appId}/entities${qs ? `?${qs}` : ''}`;
49
+ const path = `/v1/entities${qs ? `?${qs}` : ''}`;
50
50
 
51
- const res = await controlPlane('GET', path);
51
+ const res = await dataPlane('GET', path);
52
52
  const entities = Array.isArray(res.data) ? res.data : [];
53
53
 
54
54
  if (entities.length === 0) {
@@ -69,13 +69,13 @@ export async function entitiesListCommand(options) {
69
69
  export async function entitiesGetCommand(entityId) {
70
70
  try {
71
71
  const appId = getActiveApp();
72
- const res = await controlPlane('GET', `/apps/${appId}/entities/${entityId}`);
72
+ const res = await dataPlane('GET', `/v1/entities/${entityId}`);
73
73
  const e = res.data;
74
74
  displayEntity(e);
75
75
 
76
76
  // Show edges if available
77
77
  try {
78
- const edgeRes = await controlPlane('GET', `/apps/${appId}/entities/${entityId}/edges`);
78
+ const edgeRes = await dataPlane('GET', `/v1/entities/${entityId}/edges`);
79
79
  const edges = edgeRes.data;
80
80
  if (edges.length > 0) {
81
81
  console.log(chalk.dim(`\n Edges (${edges.length}):`));
@@ -137,7 +137,7 @@ export async function entitiesCreateCommand(options) {
137
137
  });
138
138
  }
139
139
 
140
- const res = await controlPlane('POST', `/apps/${appId}/entities`, body);
140
+ const res = await dataPlane('POST', `/v1/entities`, body);
141
141
  const e = res.data;
142
142
  console.log(chalk.green(`Created: ${chalk.bold(e.entityId || options.id)}`));
143
143
  } catch (err) {
@@ -178,7 +178,7 @@ export async function entitiesUpdateCommand(entityId, options) {
178
178
  });
179
179
  }
180
180
 
181
- await controlPlane('PUT', `/apps/${appId}/entities/${entityId}`, body);
181
+ await dataPlane('PUT', `/v1/entities/${entityId}`, body);
182
182
  console.log(chalk.green(`Updated: ${entityId}`));
183
183
  } catch (err) {
184
184
  console.error(chalk.red(`Failed: ${err.message}`));
@@ -189,7 +189,7 @@ export async function entitiesUpdateCommand(entityId, options) {
189
189
  export async function entitiesDeleteCommand(entityId) {
190
190
  try {
191
191
  const appId = getActiveApp();
192
- await controlPlane('DELETE', `/apps/${appId}/entities/${entityId}`);
192
+ await dataPlane('DELETE', `/v1/entities/${entityId}`);
193
193
  console.log(chalk.green(`Deleted: ${entityId}`));
194
194
  } catch (err) {
195
195
  console.error(chalk.red(`Failed: ${err.message}`));
@@ -200,7 +200,7 @@ export async function entitiesDeleteCommand(entityId) {
200
200
  export async function entitiesEdgesCommand(entityId) {
201
201
  try {
202
202
  const appId = getActiveApp();
203
- const res = await controlPlane('GET', `/apps/${appId}/entities/${entityId}/edges`);
203
+ const res = await dataPlane('GET', `/v1/entities/${entityId}/edges`);
204
204
  const edges = Array.isArray(res.data) ? res.data : [];
205
205
 
206
206
  if (edges.length === 0) {
@@ -0,0 +1,101 @@
1
+ import chalk from 'chalk';
2
+ import { dataPlane } from '../client.js';
3
+
4
+ export async function graphOverviewCommand(options) {
5
+ try {
6
+ const res = await dataPlane('GET', `/v1/graph/overview?limit=${options.limit || 100}`);
7
+ const data = res.data;
8
+
9
+ console.log(chalk.bold('Graph Overview'));
10
+ if (data.stats) {
11
+ const s = data.stats;
12
+ console.log(` Nodes: ${s.nodeCount || 0}`);
13
+ console.log(` Edges: ${s.edgeCount || 0}`);
14
+ if (s.typeCounts) {
15
+ console.log(' By type:');
16
+ for (const [type, count] of Object.entries(s.typeCounts)) {
17
+ console.log(` ${type}: ${count}`);
18
+ }
19
+ }
20
+ }
21
+ if (data.nodes && data.nodes.length > 0) {
22
+ console.log(`\n ${data.nodes.length} nodes returned`);
23
+ }
24
+ } catch (err) {
25
+ console.error(chalk.red(`Failed: ${err.message}`));
26
+ process.exit(1);
27
+ }
28
+ }
29
+
30
+ export async function graphSearchCommand(query, options) {
31
+ try {
32
+ const params = [`q=${encodeURIComponent(query)}`];
33
+ if (options.type) params.push(`type=${options.type}`);
34
+ if (options.limit) params.push(`limit=${options.limit}`);
35
+ const res = await dataPlane('GET', `/v1/graph/search?${params.join('&')}`);
36
+ const nodes = res.data?.nodes || res.data || [];
37
+
38
+ if (nodes.length === 0) {
39
+ console.log('No matches found.');
40
+ return;
41
+ }
42
+
43
+ for (const node of nodes) {
44
+ const label = node.label || node.displayName || node.name || node.entityId || node.id;
45
+ const type = node.type || (node.labels || []).filter(l => l !== 'Entity')[0] || '?';
46
+ console.log(`${chalk.bold(label)} [${type}]`);
47
+ if (node.snippet) console.log(` ${chalk.dim(node.snippet.slice(0, 120))}`);
48
+ }
49
+ console.log(`\n${nodes.length} result${nodes.length !== 1 ? 's' : ''}`);
50
+ } catch (err) {
51
+ console.error(chalk.red(`Failed: ${err.message}`));
52
+ process.exit(1);
53
+ }
54
+ }
55
+
56
+ export async function graphNodeCommand(nodeId) {
57
+ try {
58
+ const res = await dataPlane('GET', `/v1/graph/node/${encodeURIComponent(nodeId)}`);
59
+ const node = res.data;
60
+ if (!node) {
61
+ console.log('Node not found.');
62
+ return;
63
+ }
64
+ console.log(chalk.bold(node.label || node.displayName || node.name || nodeId));
65
+ console.log(` Type: ${node.type || (node.labels || []).join(', ')}`);
66
+ if (node.properties) {
67
+ for (const [k, v] of Object.entries(node.properties)) {
68
+ console.log(` ${k}: ${v}`);
69
+ }
70
+ }
71
+ if (node.edges) {
72
+ console.log(` Edges: ${node.edges.length}`);
73
+ }
74
+ } catch (err) {
75
+ console.error(chalk.red(`Failed: ${err.message}`));
76
+ process.exit(1);
77
+ }
78
+ }
79
+
80
+ export async function graphNeighborsCommand(nodeId, options) {
81
+ try {
82
+ const res = await dataPlane('GET', `/v1/graph/neighbors/${encodeURIComponent(nodeId)}?limit=${options.limit || 20}`);
83
+ const neighbors = res.data?.neighbors || res.data || [];
84
+
85
+ if (neighbors.length === 0) {
86
+ console.log('No neighbors found.');
87
+ return;
88
+ }
89
+
90
+ for (const n of neighbors) {
91
+ const label = n.label || n.displayName || n.name || n.entityId || n.id;
92
+ const type = n.type || '?';
93
+ const rel = n.relationship || n.edgeType || '';
94
+ console.log(` ${rel ? `[${rel}]` : ''} ${chalk.bold(label)} (${type})`);
95
+ }
96
+ console.log(`\n${neighbors.length} neighbor${neighbors.length !== 1 ? 's' : ''}`);
97
+ } catch (err) {
98
+ console.error(chalk.red(`Failed: ${err.message}`));
99
+ process.exit(1);
100
+ }
101
+ }
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync, existsSync } from 'fs';
3
3
  import { basename } from 'path';
4
- import { controlPlane } from '../client.js';
4
+ import { dataPlane } from '../client.js';
5
5
  import { getActiveApp } from '../config.js';
6
6
 
7
7
  export async function ingestCommand(source, options) {
@@ -52,7 +52,7 @@ export async function ingestCommand(source, options) {
52
52
  };
53
53
  });
54
54
 
55
- const res = await controlPlane('POST', `/apps/${appId}/ingest`, body);
55
+ const res = await dataPlane('POST', `/v1/ingest`, body);
56
56
  const data = res.data;
57
57
  console.log(chalk.green('Accepted'));
58
58
  console.log(chalk.dim(`Content ID: ${data.contentId}`));
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import input from '@inquirer/input';
3
3
  import select from '@inquirer/select';
4
- import { controlPlane } from '../client.js';
4
+ import { controlPlane, dataPlane } from '../client.js';
5
5
  import { setActiveApp } from '../config.js';
6
6
 
7
7
  export async function initCommand(name, options) {
@@ -105,7 +105,7 @@ export async function initCommand(name, options) {
105
105
  const rootName = await input({ message: 'Display name (e.g., Dustin Henderson):' });
106
106
 
107
107
  try {
108
- await controlPlane('POST', `/apps/${app.appId}/roots`, {
108
+ await dataPlane('POST', `/v1/roots`, {
109
109
  entityId: rootId,
110
110
  type: rootType,
111
111
  recipe_id: rootRecipe,
@@ -0,0 +1,63 @@
1
+ import chalk from 'chalk';
2
+ import { controlPlane } from '../client.js';
3
+ import { getActiveApp } from '../config.js';
4
+
5
+ export async function keysListCommand() {
6
+ try {
7
+ const appId = getActiveApp();
8
+ const res = await controlPlane('GET', `/apps/${appId}/keys`);
9
+ const keys = Array.isArray(res.data) ? res.data : [];
10
+
11
+ if (keys.length === 0) {
12
+ console.log('No API keys. Create one with: trucontext keys create --name "My Key"');
13
+ return;
14
+ }
15
+
16
+ for (const key of keys) {
17
+ const status = key.status === 'active'
18
+ ? chalk.green('Active')
19
+ : chalk.red(key.status || 'Unknown');
20
+ console.log(`${chalk.bold(key.name || key.keyId)} ${status}`);
21
+ console.log(` ID: ${key.keyId}`);
22
+ if (key.createdAt) console.log(` Created: ${key.createdAt}`);
23
+ if (key.lastUsed) console.log(` Last used: ${key.lastUsed}`);
24
+ console.log('');
25
+ }
26
+ console.log(`${keys.length} key${keys.length !== 1 ? 's' : ''}`);
27
+ } catch (err) {
28
+ console.error(chalk.red(`Failed: ${err.message}`));
29
+ process.exit(1);
30
+ }
31
+ }
32
+
33
+ export async function keysCreateCommand(options) {
34
+ try {
35
+ const appId = getActiveApp();
36
+ if (!options.name) {
37
+ console.error(chalk.red('--name is required'));
38
+ process.exit(1);
39
+ }
40
+ const res = await controlPlane('POST', `/apps/${appId}/keys`, { name: options.name });
41
+ const key = res.data;
42
+
43
+ console.log(chalk.green('API Key Created'));
44
+ console.log(` Name: ${key.name || options.name}`);
45
+ console.log(` Key: ${chalk.bold(key.plainKey || key.key)}`);
46
+ console.log('');
47
+ console.log(chalk.yellow('Save this key now — you will not see it again.'));
48
+ } catch (err) {
49
+ console.error(chalk.red(`Failed: ${err.message}`));
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ export async function keysRevokeCommand(keyId) {
55
+ try {
56
+ const appId = getActiveApp();
57
+ await controlPlane('DELETE', `/apps/${appId}/keys/${keyId}`);
58
+ console.log(chalk.green(`Revoked: ${keyId}`));
59
+ } catch (err) {
60
+ console.error(chalk.red(`Failed: ${err.message}`));
61
+ process.exit(1);
62
+ }
63
+ }
@@ -0,0 +1,66 @@
1
+ import chalk from 'chalk';
2
+ import { dataPlane } from '../client.js';
3
+
4
+ export async function mindPresenceCommand() {
5
+ try {
6
+ const res = await dataPlane('GET', '/v1/mind/presence');
7
+ const p = res.data;
8
+
9
+ console.log(chalk.bold('Mind Presence'));
10
+ if (p.status) console.log(` Status: ${p.status}`);
11
+ if (p.lastCycle) console.log(` Last cycle: ${p.lastCycle}`);
12
+ if (p.cycleNumber != null) console.log(` Cycle: ${p.cycleNumber}`);
13
+ if (p.events != null) console.log(` Events: ${p.events}`);
14
+ if (p.insights != null) console.log(` Insights: ${p.insights}`);
15
+ if (p.patterns != null) console.log(` Patterns: ${p.patterns}`);
16
+ if (p.confirmed != null) console.log(` Confirmed: ${p.confirmed}`);
17
+ if (p.rejected != null) console.log(` Rejected: ${p.rejected}`);
18
+ if (p.thought) {
19
+ console.log(`\n ${chalk.italic(p.thought.slice(0, 200))}`);
20
+ }
21
+ } catch (err) {
22
+ console.error(chalk.red(`Failed: ${err.message}`));
23
+ process.exit(1);
24
+ }
25
+ }
26
+
27
+ export async function mindThoughtsCommand(options) {
28
+ try {
29
+ const params = [];
30
+ if (options.limit) params.push(`limit=${options.limit}`);
31
+ if (options.mode) params.push(`mode=${options.mode}`);
32
+ const qs = params.length > 0 ? `?${params.join('&')}` : '';
33
+ const res = await dataPlane('GET', `/v1/mind/thoughts${qs}`);
34
+ const thoughts = res.data?.thoughts || res.data || [];
35
+
36
+ if (thoughts.length === 0) {
37
+ console.log('No thoughts recorded yet.');
38
+ return;
39
+ }
40
+
41
+ for (const t of thoughts) {
42
+ const mode = t.mode || t.dream_type || '?';
43
+ const cycle = t.cycle_number != null ? `cycle ${t.cycle_number}` : '';
44
+ const ts = t.created_at || t.createdAt || '';
45
+ const ago = ts ? timeSince(ts) : '';
46
+ console.log(` ${chalk.cyan(`[${mode}]`)} ${cycle} ${chalk.dim(ago)}`);
47
+ if (t.diary || t.thought) {
48
+ console.log(` ${(t.diary || t.thought).slice(0, 200)}`);
49
+ }
50
+ console.log('');
51
+ }
52
+ console.log(`${thoughts.length} thought${thoughts.length !== 1 ? 's' : ''}`);
53
+ } catch (err) {
54
+ console.error(chalk.red(`Failed: ${err.message}`));
55
+ process.exit(1);
56
+ }
57
+ }
58
+
59
+ function timeSince(dateStr) {
60
+ const diff = Date.now() - new Date(dateStr).getTime();
61
+ const mins = Math.floor(diff / 60000);
62
+ if (mins < 60) return `${mins}m ago`;
63
+ const hours = Math.floor(mins / 60);
64
+ if (hours < 24) return `${hours}h ago`;
65
+ return `${Math.floor(hours / 24)}d ago`;
66
+ }
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { controlPlane } from '../client.js';
2
+ import { dataPlane } from '../client.js';
3
3
  import { getConfig, getActiveApp } from '../config.js';
4
4
 
5
5
  export async function queryCommand(question, options) {
@@ -27,7 +27,7 @@ export async function queryCommand(question, options) {
27
27
 
28
28
  if (options.context) body.context_id = options.context;
29
29
 
30
- const res = await controlPlane('POST', `/apps/${appId}/mind/query`, body);
30
+ const res = await dataPlane('POST', `/v1/query`, body);
31
31
  const data = res.data;
32
32
 
33
33
  // Print answer
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { controlPlane } from '../client.js';
2
+ import { dataPlane } from '../client.js';
3
3
  import { getConfig, getActiveApp } from '../config.js';
4
4
 
5
5
  export async function recallCommand(query, options) {
@@ -22,7 +22,7 @@ export async function recallCommand(query, options) {
22
22
 
23
23
  if (options.context) body.context_id = options.context;
24
24
 
25
- const res = await controlPlane('POST', `/apps/${appId}/recall`, body);
25
+ const res = await dataPlane('POST', `/v1/recall`, body);
26
26
  const data = res.data;
27
27
 
28
28
  // Print synthesis
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'fs';
2
2
  import chalk from 'chalk';
3
- import { controlPlane } from '../client.js';
3
+ import { dataPlane } from '../client.js';
4
4
  import { getActiveApp } from '../config.js';
5
5
 
6
6
  function requireApp() {
@@ -15,7 +15,7 @@ function requireApp() {
15
15
  export async function recipesListCommand() {
16
16
  try {
17
17
  const appId = requireApp();
18
- const res = await controlPlane('GET', `/apps/${appId}/recipes`);
18
+ const res = await dataPlane('GET', `/v1/recipes`);
19
19
  const recipes = res.data;
20
20
 
21
21
  if (recipes.length === 0) {
@@ -36,7 +36,7 @@ export async function recipesListCommand() {
36
36
  export async function recipesGetCommand(recipeId) {
37
37
  try {
38
38
  const appId = requireApp();
39
- const res = await controlPlane('GET', `/apps/${appId}/recipes/${recipeId}`);
39
+ const res = await dataPlane('GET', `/v1/recipes/${recipeId}`);
40
40
  const r = res.data;
41
41
 
42
42
  console.log(chalk.bold(r.recipeId || recipeId));
@@ -138,7 +138,7 @@ export async function recipesCreateCommand(options) {
138
138
  if (options.confidenceBias) body.interpretation.confidence_bias = options.confidenceBias;
139
139
  }
140
140
 
141
- const res = await controlPlane('POST', `/apps/${appId}/recipes`, body);
141
+ const res = await dataPlane('POST', `/v1/recipes`, body);
142
142
  const r = res.data;
143
143
  console.log(chalk.green(`Created: ${chalk.bold(r.recipeId || options.id)}`));
144
144
  } catch (err) {
@@ -147,10 +147,41 @@ export async function recipesCreateCommand(options) {
147
147
  }
148
148
  }
149
149
 
150
+ export async function recipesUpdateCommand(recipeId, options) {
151
+ try {
152
+ const appId = requireApp();
153
+ const body = {};
154
+
155
+ if (options.name) body.name = options.name;
156
+
157
+ if (options.file) {
158
+ const fs = await import('fs');
159
+ const content = fs.readFileSync(options.file, 'utf8');
160
+ body.interpretation = JSON.parse(content);
161
+ }
162
+
163
+ if (options.purpose) {
164
+ body.interpretation = body.interpretation || {};
165
+ body.interpretation.purpose = options.purpose;
166
+ }
167
+
168
+ if (Object.keys(body).length === 0) {
169
+ console.error(chalk.red('Nothing to update. Provide --name, --file, or --purpose'));
170
+ process.exit(1);
171
+ }
172
+
173
+ const res = await dataPlane('PUT', `/v1/recipes/${recipeId}`, body);
174
+ console.log(chalk.green(`Updated: ${recipeId}`));
175
+ } catch (err) {
176
+ console.error(chalk.red(`Failed: ${err.message}`));
177
+ process.exit(1);
178
+ }
179
+ }
180
+
150
181
  export async function recipesDeleteCommand(recipeId) {
151
182
  try {
152
183
  const appId = requireApp();
153
- await controlPlane('DELETE', `/apps/${appId}/recipes/${recipeId}`);
184
+ await dataPlane('DELETE', `/v1/recipes/${recipeId}`);
154
185
  console.log(chalk.green(`Deleted: ${recipeId}`));
155
186
  } catch (err) {
156
187
  console.error(chalk.red(`Failed: ${err.message}`));
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { controlPlane } from '../client.js';
2
+ import { dataPlane } from '../client.js';
3
3
  import { getActiveApp } from '../config.js';
4
4
 
5
5
  export async function relationshipTypesListCommand(options) {
@@ -13,9 +13,9 @@ export async function relationshipTypesListCommand(options) {
13
13
  const params = new URLSearchParams();
14
14
  if (options.category) params.set('category', options.category);
15
15
  const qs = params.toString();
16
- const path = `/apps/${appId}/relationship-types${qs ? `?${qs}` : ''}`;
16
+ const path = `/v1/relationship-types${qs ? `?${qs}` : ''}`;
17
17
 
18
- const res = await controlPlane('GET', path);
18
+ const res = await dataPlane('GET', path);
19
19
  const types = res.data.types;
20
20
 
21
21
  if (Array.isArray(types) && types.length === 0) {
@@ -54,3 +54,40 @@ export async function relationshipTypesListCommand(options) {
54
54
  process.exit(1);
55
55
  }
56
56
  }
57
+
58
+ export async function relationshipTypesCreateCommand(options) {
59
+ try {
60
+ const appId = getActiveApp();
61
+ if (!options.type) {
62
+ console.error(chalk.red('--type is required'));
63
+ process.exit(1);
64
+ }
65
+ if (!options.description) {
66
+ console.error(chalk.red('--description is required'));
67
+ process.exit(1);
68
+ }
69
+
70
+ const body = {
71
+ type: options.type,
72
+ description: options.description,
73
+ category: options.category || 'Custom',
74
+ };
75
+
76
+ const res = await dataPlane('POST', '/v1/relationship-types', body);
77
+ console.log(chalk.green(`Created: ${chalk.bold(res.data.type)}`));
78
+ } catch (err) {
79
+ console.error(chalk.red(`Failed: ${err.message}`));
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ export async function relationshipTypesDeleteCommand(type) {
85
+ try {
86
+ const appId = getActiveApp();
87
+ await dataPlane('DELETE', `/v1/relationship-types/${encodeURIComponent(type)}`);
88
+ console.log(chalk.green(`Deleted: ${type}`));
89
+ } catch (err) {
90
+ console.error(chalk.red(`Failed: ${err.message}`));
91
+ process.exit(1);
92
+ }
93
+ }
@@ -4,7 +4,7 @@
4
4
  // carry recipes, and control which dreams run. Queries require a root_id.
5
5
 
6
6
  import chalk from 'chalk';
7
- import { controlPlane } from '../client.js';
7
+ import { dataPlane } from '../client.js';
8
8
  import { getConfig, saveConfig, getActiveApp } from '../config.js';
9
9
 
10
10
  export function registerRootsCommand(program) {
@@ -55,7 +55,7 @@ export function registerRootsCommand(program) {
55
55
  }
56
56
 
57
57
  const appId = getActiveApp();
58
- const res = await controlPlane('POST', `/apps/${appId}/roots`, body);
58
+ const res = await dataPlane('POST', `/v1/roots`, body);
59
59
  const r = res.data;
60
60
  console.log(chalk.green(`Root node created: ${r.entityId}`));
61
61
  console.log(` Type: ${r.type}`);
@@ -75,7 +75,7 @@ export function registerRootsCommand(program) {
75
75
  .action(async () => {
76
76
  try {
77
77
  const appId = getActiveApp();
78
- const res = await controlPlane('GET', `/apps/${appId}/roots`);
78
+ const res = await dataPlane('GET', `/v1/roots`);
79
79
  const rootNodes = Array.isArray(res.data) ? res.data : [];
80
80
  if (rootNodes.length === 0) {
81
81
  console.log('No root nodes. Create one with: trucontext roots create');
@@ -102,7 +102,7 @@ export function registerRootsCommand(program) {
102
102
  .action(async (rootId) => {
103
103
  try {
104
104
  const appId = getActiveApp();
105
- const res = await controlPlane('GET', `/apps/${appId}/roots/${rootId}`);
105
+ const res = await dataPlane('GET', `/v1/roots/${rootId}`);
106
106
  const r = res.data;
107
107
  console.log(chalk.bold(`Root: ${r.entityId}`));
108
108
  console.log(` Type: ${r.type}`);
@@ -148,7 +148,7 @@ export function registerRootsCommand(program) {
148
148
  }
149
149
 
150
150
  const appId = getActiveApp();
151
- await controlPlane('PUT', `/apps/${appId}/roots/${rootId}`, body);
151
+ await dataPlane('PUT', `/v1/roots/${rootId}`, body);
152
152
  console.log(chalk.green(`Updated: ${rootId}`));
153
153
  } catch (err) {
154
154
  console.error(chalk.red('Failed:'), err.response?.data?.error || err.message);
@@ -163,7 +163,7 @@ export function registerRootsCommand(program) {
163
163
  .action(async (rootId) => {
164
164
  try {
165
165
  const appId = getActiveApp();
166
- await controlPlane('DELETE', `/apps/${appId}/roots/${rootId}`);
166
+ await dataPlane('DELETE', `/v1/roots/${rootId}`);
167
167
  console.log(chalk.green(`Demoted: ${rootId} (no longer a root node)`));
168
168
 
169
169
  const config = getConfig();
@@ -184,7 +184,7 @@ export function registerRootsCommand(program) {
184
184
  .action(async (rootId) => {
185
185
  try {
186
186
  const appId = getActiveApp();
187
- await controlPlane('GET', `/apps/${appId}/roots/${rootId}`);
187
+ await dataPlane('GET', `/v1/roots/${rootId}`);
188
188
  const config = getConfig();
189
189
  config.activeRoot = rootId;
190
190
  saveConfig(config);
@@ -0,0 +1,50 @@
1
+ import chalk from 'chalk';
2
+ import { controlPlane } from '../client.js';
3
+ import { getActiveApp } from '../config.js';
4
+
5
+ export async function settingsShowCommand() {
6
+ try {
7
+ const appId = getActiveApp();
8
+ const res = await controlPlane('GET', `/apps/${appId}/intelligence/debug-settings`);
9
+ const settings = res.data;
10
+
11
+ console.log(chalk.bold('App Settings'));
12
+ if (typeof settings === 'object') {
13
+ for (const [k, v] of Object.entries(settings)) {
14
+ const val = typeof v === 'object' ? JSON.stringify(v) : v;
15
+ console.log(` ${k}: ${val}`);
16
+ }
17
+ } else {
18
+ console.log(' No settings configured.');
19
+ }
20
+ } catch (err) {
21
+ console.error(chalk.red(`Failed: ${err.message}`));
22
+ process.exit(1);
23
+ }
24
+ }
25
+
26
+ export async function settingsUpdateCommand(options) {
27
+ try {
28
+ const appId = getActiveApp();
29
+ let updates;
30
+ if (options.json) {
31
+ try {
32
+ updates = JSON.parse(options.json);
33
+ } catch {
34
+ console.error(chalk.red('Invalid JSON'));
35
+ process.exit(1);
36
+ }
37
+ } else if (options.key && options.value !== undefined) {
38
+ updates = { [options.key]: options.value };
39
+ } else {
40
+ console.error(chalk.red('Provide --json or --key + --value'));
41
+ process.exit(1);
42
+ }
43
+
44
+ const res = await controlPlane('PUT', `/apps/${appId}/intelligence/debug-settings`, updates);
45
+ console.log(chalk.green('Settings updated'));
46
+ } catch (err) {
47
+ console.error(chalk.red(`Failed: ${err.message}`));
48
+ process.exit(1);
49
+ }
50
+ }