trucontext 0.2.1 → 0.3.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
@@ -11,14 +11,14 @@ import { queryCommand } from '../src/commands/query.js';
11
11
  import { recallCommand } from '../src/commands/recall.js';
12
12
  import { contextsListCommand, contextsCreateCommand, contextsDeleteCommand } from '../src/commands/contexts.js';
13
13
  import { schemaShowCommand, schemaGenerateCommand, schemaSetAuthorshipCommand } from '../src/commands/schema.js';
14
- import { entitiesListCommand, entitiesGetCommand, entitiesCreateCommand, entitiesUpdateCommand, entitiesDeleteCommand } from '../src/commands/entities.js';
14
+ import { entitiesListCommand, entitiesGetCommand, entitiesCreateCommand, entitiesUpdateCommand, entitiesDeleteCommand, entitiesEdgesCommand } from '../src/commands/entities.js';
15
15
  import { recipesListCommand, recipesGetCommand, recipesCreateCommand, recipesDeleteCommand } from '../src/commands/recipes.js';
16
16
  import { relationshipTypesListCommand } from '../src/commands/relationship-types.js';
17
17
 
18
18
  program
19
19
  .name('trucontext')
20
20
  .description('TruContext CLI — contextual memory for AI applications')
21
- .version('0.2.1');
21
+ .version('0.3.1');
22
22
 
23
23
  // Auth
24
24
  program.command('login')
@@ -88,7 +88,9 @@ const entities = program.command('entities').description('Manage entities').acti
88
88
  entities.command('list')
89
89
  .description('List entities')
90
90
  .option('--type <type>', 'Filter by entity type')
91
- .option('-l, --limit <n>', 'Max results')
91
+ .option('--context <id>', 'Filter by context')
92
+ .option('--scope <scope>', 'Filter by scope')
93
+ .option('-l, --limit <n>', 'Max results (default: 50)')
92
94
  .action(entitiesListCommand);
93
95
  entities.command('get <entityId>')
94
96
  .description('Get an entity')
@@ -114,10 +116,14 @@ entities.command('update <entityId>')
114
116
  .option('--confidence <n>', 'Confidence level (0.0-1.0)')
115
117
  .option('--temporal', 'Content can decay over time')
116
118
  .option('--no-temporal', 'Content is a permanent fact')
119
+ .option('-c, --context <entry>', 'Link to context (entity-id:RELATIONSHIP, repeatable)', (val, prev) => [...prev, val], [])
117
120
  .action(entitiesUpdateCommand);
118
121
  entities.command('delete <entityId>')
119
122
  .description('Delete an entity')
120
123
  .action(entitiesDeleteCommand);
124
+ entities.command('edges <entityId>')
125
+ .description('List edges for an entity')
126
+ .action(entitiesEdgesCommand);
121
127
 
122
128
  // Recipes
123
129
  const recipes = program.command('recipes').description('Manage recipes').action(function() { this.help(); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trucontext",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "TruContext CLI — contextual memory for AI applications",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,10 +1,47 @@
1
1
  import chalk from 'chalk';
2
2
  import { dataPlane } from '../client.js';
3
3
 
4
+ // Fields stored on the node but not user-declared properties
5
+ const SYSTEM_FIELDS = new Set([
6
+ 'tenantId', 'entityId', 'provenance', 'scope', 'createdAt', 'updatedAt',
7
+ 'lastReinforcedAt', 'confidence', 'temporal', 'heartbeat_enabled', 'recipe_id',
8
+ ]);
9
+
10
+ function displayEntity(e) {
11
+ const props = e.properties || {};
12
+ const type = e.type || e.labels?.[0] || 'Unknown';
13
+ const entityId = e.entityId || props.entityId;
14
+ const confidence = e.confidence ?? props.confidence;
15
+ const temporal = e.temporal ?? props.temporal;
16
+ const recipeId = e.recipe_id ?? props.recipe_id;
17
+ const heartbeat = e.heartbeat_enabled ?? props.heartbeat_enabled;
18
+ const provenance = e.provenance ?? props.provenance;
19
+
20
+ console.log(`${chalk.bold(entityId)} ${chalk.dim(`[${type}]`)}`);
21
+ if (provenance) console.log(` provenance: ${chalk.dim(provenance)}`);
22
+ if (recipeId) console.log(` recipe: ${chalk.cyan(recipeId)}`);
23
+ if (confidence !== undefined) console.log(` confidence: ${chalk.dim(confidence)}`);
24
+ if (temporal !== undefined) console.log(` temporal: ${chalk.dim(temporal)}`);
25
+ if (heartbeat !== undefined) console.log(` heartbeat: ${chalk.dim(heartbeat)}`);
26
+
27
+ // Show user-declared properties (exclude system fields)
28
+ const userProps = {};
29
+ for (const [k, v] of Object.entries(props)) {
30
+ if (!SYSTEM_FIELDS.has(k) && v !== null && v !== undefined) {
31
+ userProps[k] = v;
32
+ }
33
+ }
34
+ if (Object.keys(userProps).length > 0) {
35
+ console.log(` properties: ${chalk.dim(JSON.stringify(userProps))}`);
36
+ }
37
+ }
38
+
4
39
  export async function entitiesListCommand(options) {
5
40
  try {
6
41
  const params = new URLSearchParams();
7
42
  if (options.type) params.set('type', options.type);
43
+ if (options.context) params.set('context_id', options.context);
44
+ if (options.scope) params.set('scope', options.scope);
8
45
  if (options.limit) params.set('limit', options.limit);
9
46
  const qs = params.toString();
10
47
  const path = `/v1/entities${qs ? `?${qs}` : ''}`;
@@ -18,15 +55,9 @@ export async function entitiesListCommand(options) {
18
55
  }
19
56
 
20
57
  for (const e of entities) {
21
- console.log(`${chalk.bold(e.entityId)} ${chalk.dim(`[${e.type}]`)}`);
22
- if (e.recipe_id) console.log(` recipe: ${chalk.dim(e.recipe_id)}`);
23
- if (e.properties && Object.keys(e.properties).length > 0) {
24
- console.log(` properties: ${chalk.dim(JSON.stringify(e.properties))}`);
25
- }
26
- if (e.heartbeat_enabled !== undefined) console.log(` heartbeat: ${chalk.dim(e.heartbeat_enabled)}`);
27
- if (e.confidence !== undefined) console.log(` confidence: ${chalk.dim(e.confidence)}`);
28
- if (e.temporal !== undefined) console.log(` temporal: ${chalk.dim(e.temporal)}`);
58
+ displayEntity(e);
29
59
  }
60
+ console.log(chalk.dim(`\n${entities.length} entities`));
30
61
  } catch (err) {
31
62
  console.error(chalk.red(`Failed: ${err.message}`));
32
63
  process.exit(1);
@@ -37,15 +68,22 @@ export async function entitiesGetCommand(entityId) {
37
68
  try {
38
69
  const res = await dataPlane('GET', `/v1/entities/${entityId}`);
39
70
  const e = res.data || res;
40
-
41
- console.log(`${chalk.bold(e.entityId)} ${chalk.dim(`[${e.type}]`)}`);
42
- if (e.recipe_id) console.log(` recipe: ${chalk.dim(e.recipe_id)}`);
43
- if (e.properties && Object.keys(e.properties).length > 0) {
44
- console.log(` properties: ${chalk.dim(JSON.stringify(e.properties))}`);
71
+ displayEntity(e);
72
+
73
+ // Show edges if available
74
+ try {
75
+ const edgeRes = await dataPlane('GET', `/v1/entities/${entityId}/edges`);
76
+ const edges = edgeRes.data?.edges || edgeRes.data || [];
77
+ if (edges.length > 0) {
78
+ console.log(chalk.dim(`\n Edges (${edges.length}):`));
79
+ for (const edge of edges) {
80
+ const dir = edge.direction === 'outgoing' ? '→' : '←';
81
+ console.log(` ${dir} ${chalk.cyan(edge.type)} ${chalk.dim(edge.targetEntityId || edge.targetLabel || edge.target || '')}`);
82
+ }
83
+ }
84
+ } catch {
85
+ // Edge listing may not be available — skip silently
45
86
  }
46
- if (e.heartbeat_enabled !== undefined) console.log(` heartbeat: ${chalk.dim(e.heartbeat_enabled)}`);
47
- if (e.confidence !== undefined) console.log(` confidence: ${chalk.dim(e.confidence)}`);
48
- if (e.temporal !== undefined) console.log(` temporal: ${chalk.dim(e.temporal)}`);
49
87
  } catch (err) {
50
88
  console.error(chalk.red(`Failed: ${err.message}`));
51
89
  process.exit(1);
@@ -122,6 +160,19 @@ export async function entitiesUpdateCommand(entityId, options) {
122
160
  if (options.confidence !== undefined) body.confidence = parseFloat(options.confidence);
123
161
  if (options.temporal !== undefined) body.temporal = options.temporal;
124
162
 
163
+ if (options.context?.length > 0) {
164
+ body.contexts = options.context.map(entry => {
165
+ const colonIdx = entry.indexOf(':');
166
+ if (colonIdx === -1) {
167
+ return { context_id: entry, relationship: 'ABOUT' };
168
+ }
169
+ return {
170
+ context_id: entry.slice(0, colonIdx),
171
+ relationship: entry.slice(colonIdx + 1),
172
+ };
173
+ });
174
+ }
175
+
125
176
  await dataPlane('PUT', `/v1/entities/${entityId}`, body);
126
177
  console.log(chalk.green(`Updated: ${entityId}`));
127
178
  } catch (err) {
@@ -139,3 +190,27 @@ export async function entitiesDeleteCommand(entityId) {
139
190
  process.exit(1);
140
191
  }
141
192
  }
193
+
194
+ export async function entitiesEdgesCommand(entityId) {
195
+ try {
196
+ const res = await dataPlane('GET', `/v1/entities/${entityId}/edges`);
197
+ const edges = res.data?.edges || res.data || [];
198
+
199
+ if (edges.length === 0) {
200
+ console.log(chalk.yellow('No edges found.'));
201
+ return;
202
+ }
203
+
204
+ for (const edge of edges) {
205
+ const dir = edge.direction === 'outgoing' ? '→' : '←';
206
+ console.log(` ${dir} ${chalk.cyan(edge.type)} ${chalk.dim(edge.targetEntityId || edge.targetLabel || '')}`);
207
+ if (edge.properties && Object.keys(edge.properties).length > 0) {
208
+ console.log(` ${chalk.dim(JSON.stringify(edge.properties))}`);
209
+ }
210
+ }
211
+ console.log(chalk.dim(`\n${edges.length} edges`));
212
+ } catch (err) {
213
+ console.error(chalk.red(`Failed: ${err.message}`));
214
+ process.exit(1);
215
+ }
216
+ }