trucontext 0.8.5 → 0.9.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.
package/bin/cli.js CHANGED
@@ -12,15 +12,18 @@ 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';
24
27
 
25
28
  program
26
29
  .name('trucontext')
@@ -83,15 +86,6 @@ program.command('recall <query>')
83
86
  .option('-d, --depth <n>', 'Graph expansion depth', '2')
84
87
  .action(recallCommand);
85
88
 
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
89
  // Entities
96
90
  const entities = program.command('entities').description('Manage entities').action(function() { this.help(); });
97
91
  entities.command('list')
@@ -152,15 +146,31 @@ recipes.command('create')
152
146
  .option('--decay-profile <text>', 'Decay profile description')
153
147
  .option('--confidence-bias <text>', 'Confidence bias description')
154
148
  .action(recipesCreateCommand);
149
+ recipes.command('update <recipeId>')
150
+ .description('Update a custom recipe')
151
+ .option('--name <name>', 'New name')
152
+ .option('--file <path>', 'JSON file with updated interpretation')
153
+ .option('--purpose <text>', 'Update purpose text')
154
+ .action(recipesUpdateCommand);
155
155
  recipes.command('delete <recipeId>')
156
156
  .description('Delete a recipe')
157
157
  .action(recipesDeleteCommand);
158
158
 
159
159
  // Relationship Types
160
- program.command('relationship-types')
160
+ const relTypes = program.command('relationship-types').description('Manage relationship types').action(relationshipTypesListCommand);
161
+ relTypes.command('list')
161
162
  .description('List relationship types')
162
163
  .option('--category <cat>', 'Filter by category')
163
164
  .action(relationshipTypesListCommand);
165
+ relTypes.command('create')
166
+ .description('Create a custom relationship type')
167
+ .requiredOption('--type <type>', 'Relationship type (UPPER_SNAKE_CASE)')
168
+ .requiredOption('--description <text>', 'Description of the relationship')
169
+ .option('--category <cat>', 'Category (default: Custom)')
170
+ .action(relationshipTypesCreateCommand);
171
+ relTypes.command('delete <type>')
172
+ .description('Delete a custom relationship type')
173
+ .action(relationshipTypesDeleteCommand);
164
174
 
165
175
  // Schema
166
176
  const schema = program.command('schema').description('Manage app schema').action(function() { this.help(); });
@@ -196,6 +206,57 @@ billing.command('plans')
196
206
  .description('View available plans')
197
207
  .action(plansCommand);
198
208
 
209
+ // API Keys
210
+ const keys = program.command('keys').description('Manage API keys').action(keysListCommand);
211
+ keys.command('list').description('List API keys').action(keysListCommand);
212
+ keys.command('create')
213
+ .description('Create a new API key')
214
+ .requiredOption('--name <name>', 'Key name')
215
+ .action(keysCreateCommand);
216
+ keys.command('revoke <keyId>')
217
+ .description('Revoke an API key')
218
+ .action(keysRevokeCommand);
219
+
220
+ // Graph Explorer
221
+ const graph = program.command('graph').description('Explore the knowledge graph').action(function() { this.help(); });
222
+ graph.command('overview')
223
+ .description('Graph overview (node/edge counts)')
224
+ .option('-l, --limit <n>', 'Max nodes to return', '100')
225
+ .action(graphOverviewCommand);
226
+ graph.command('search <query>')
227
+ .description('Search graph by keyword')
228
+ .option('--type <type>', 'Filter by node type')
229
+ .option('-l, --limit <n>', 'Max results', '50')
230
+ .action(graphSearchCommand);
231
+ graph.command('node <nodeId>')
232
+ .description('Get detailed node info')
233
+ .action(graphNodeCommand);
234
+ graph.command('neighbors <nodeId>')
235
+ .description('Get node neighbors')
236
+ .option('-l, --limit <n>', 'Max neighbors', '20')
237
+ .action(graphNeighborsCommand);
238
+
239
+ // Mind
240
+ const mind = program.command('mind').description('View mind state and thoughts').action(function() { this.help(); });
241
+ mind.command('presence')
242
+ .description('View consciousness state')
243
+ .action(mindPresenceCommand);
244
+ mind.command('thoughts')
245
+ .description('View dream diary')
246
+ .option('-l, --limit <n>', 'Max entries', '20')
247
+ .option('-m, --mode <mode>', 'Filter by dream type')
248
+ .action(mindThoughtsCommand);
249
+
250
+ // Settings
251
+ const settings = program.command('settings').description('Manage app settings').action(settingsShowCommand);
252
+ settings.command('show').description('Show current settings').action(settingsShowCommand);
253
+ settings.command('update')
254
+ .description('Update settings')
255
+ .option('--json <json>', 'JSON object with settings to update')
256
+ .option('--key <key>', 'Setting key')
257
+ .option('--value <value>', 'Setting value')
258
+ .action(settingsUpdateCommand);
259
+
199
260
  // Root nodes + Curiosity (registered as subcommand groups)
200
261
  registerRootsCommand(program);
201
262
  registerCuriosityCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trucontext",
3
- "version": "0.8.5",
3
+ "version": "0.9.0",
4
4
  "description": "TruContext CLI — contextual memory for AI applications",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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,10 +13,10 @@ 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);
19
- const types = res.data;
18
+ const res = await dataPlane('GET', path);
19
+ const types = res.data.types;
20
20
 
21
21
  if (Array.isArray(types) && types.length === 0) {
22
22
  console.log(chalk.yellow('No relationship types found.'));
@@ -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}/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}/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
+ }